Subversion Repositories oidplus

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. if (!defined('IN_OIDPLUS')) die();
  21.  
  22. class OIDplusDatabasePluginPgSql extends OIDplusDatabasePlugin {
  23.         private $conn;
  24.         private $already_prepared;
  25.  
  26.         public static function getPluginInformation(): array {
  27.                 $out = array();
  28.                 $out['name'] = 'PostgreSQL';
  29.                 $out['author'] = 'ViaThinkSoft';
  30.                 $out['version'] = null;
  31.                 $out['descriptionHTML'] = null;
  32.                 return $out;
  33.         }
  34.  
  35.         public static function name(): string {
  36.                 return "PgSQL";
  37.         }
  38.  
  39.         public function query(string $sql, /*?array*/ $prepared_args=null): OIDplusQueryResult {
  40.                 if (is_null($prepared_args)) {
  41.                         $res = pg_query($this->conn, $sql);
  42.  
  43.                         if ($res === false) {
  44.                                 throw new OIDplusSQLException($sql, $this->error());
  45.                         } else {
  46.                                 return new OIDplusQueryResultPgSql($res);
  47.                         }
  48.                 } else {
  49.                         if (!is_array($prepared_args)) {
  50.                                 throw new OIDplusException("'prepared_args' must be either NULL or an ARRAY.");
  51.                         }
  52.  
  53.                         // convert ? ? ? to $1 $2 $3
  54.                         $sql = preg_replace_callback('@\\?@', function($found) {
  55.                                 static $i = 0;
  56.                                 $i++;
  57.                                 return '$'.$i;
  58.                         }, $sql);
  59.  
  60.                         $prepare_name = 'OIDPLUS_'.sha1($sql);
  61.                         if (!in_array($prepare_name, $this->already_prepared)) {
  62.                                 $res = pg_prepare($this->conn, $prepare_name, $sql);
  63.                                 if ($res === false) {
  64.                                         throw new OIDplusSQLException($sql, 'Cannot prepare statement');
  65.                                 }
  66.                                 $this->already_prepared[] = $prepare_name;
  67.                         }
  68.  
  69.                         foreach ($prepared_args as &$value) {
  70.                                 if (is_bool($value)) $value = $value ? '1' : '0';
  71.                         }
  72.  
  73.                         $ps = pg_execute($this->conn, $prepare_name, $prepared_args);
  74.                         if ($ps === false) {
  75.                                 throw new OIDplusSQLException($sql, $this->error());
  76.                         }
  77.                         return new OIDplusQueryResultPgSql($ps);
  78.                 }
  79.         }
  80.  
  81.         public function insert_id(): int {
  82.                 return (int)$this->query('select lastval() as id')->fetch_object()->id;
  83.         }
  84.  
  85.         public function error(): string {
  86.                 $err = pg_last_error($this->conn);
  87.                 if (!$err) $err = '';
  88.                 return $err;
  89.         }
  90.  
  91.         protected function doConnect(): void {
  92.                 // Try connecting to the database
  93.                 ob_start();
  94.                 try {
  95.                         $err = '';
  96.                         list($hostname, $port) = explode(':', OIDPLUS_PGSQL_HOST.':5432');
  97.                         $username = OIDPLUS_PGSQL_USERNAME;
  98.                         $password = OIDPLUS_PGSQL_PASSWORD;
  99.                         $dbname   = OIDPLUS_PGSQL_DATABASE;
  100.                         $this->conn = pg_connect("host=$hostname user=$username password=$password port=$port dbname=$dbname");
  101.                 } finally {
  102.                         $err = ob_get_contents();
  103.                         ob_end_clean();
  104.                 }
  105.  
  106.                 if (!$this->conn) {
  107.                         throw new OIDplusConfigInitializationException('Connection to the database failed! ' . strip_tags($err));
  108.                 }
  109.  
  110.                 $this->already_prepared = array();
  111.  
  112.                 try {
  113.                         $this->query("SET NAMES 'utf8'");
  114.                 } catch (Exception $e) {
  115.                 }
  116.         }
  117.  
  118.         protected function doDisconnect(): void {
  119.                 $this->already_prepared = array();
  120.                 pg_close($this->conn);
  121.         }
  122.  
  123.         private $intransaction = false;
  124.  
  125.         public function transaction_begin(): void {
  126.                 $this->query('begin transaction');
  127.                 $this->intransaction = true;
  128.         }
  129.  
  130.         public function transaction_commit(): void {
  131.                 $this->query('commit');
  132.                 $this->intransaction = false;
  133.         }
  134.  
  135.         public function transaction_rollback(): void {
  136.                 $this->query('rollback');
  137.                 $this->intransaction = false;
  138.         }
  139. }
  140.  
  141. class OIDplusQueryResultPgSql extends OIDplusQueryResult {
  142.         protected $no_resultset;
  143.         protected $res;
  144.  
  145.         public function __construct($res) {
  146.                 $this->no_resultset = is_bool($res);
  147.  
  148.                 if (!$this->no_resultset) {
  149.                         $this->res = $res;
  150.                 }
  151.         }
  152.  
  153.         public function __destruct() {
  154.                 pg_free_result($this->res);
  155.         }
  156.  
  157.         public function containsResultSet(): bool {
  158.                 return !$this->no_resultset;
  159.         }
  160.  
  161.         public function num_rows(): int {
  162.                 if ($this->no_resultset) throw new OIDplusException("The query has returned no result set (i.e. it was not a SELECT query)");
  163.                 return pg_num_rows($this->res);
  164.         }
  165.  
  166.         public function fetch_array()/*: ?array*/ {
  167.                 if ($this->no_resultset) throw new OIDplusException("The query has returned no result set (i.e. it was not a SELECT query)");
  168.                 $ret = pg_fetch_array($this->res, null, PGSQL_ASSOC);
  169.                 if ($ret === false) $ret = null;
  170.                 if (!is_null($ret)) {
  171.                         foreach ($ret as $key => &$value){
  172.                                 $type = pg_field_type($this->res,pg_field_num($this->res, $key));
  173.                                 if ($type == 'bool'){
  174.                                         $value = ($value == 't');
  175.                                 }
  176.                         }
  177.                 }
  178.                 return $ret;
  179.         }
  180.  
  181.         public function fetch_object()/*: ?object*/ {
  182.                 if ($this->no_resultset) throw new OIDplusException("The query has returned no result set (i.e. it was not a SELECT query)");
  183.                 $ret = pg_fetch_object($this->res);
  184.                 if ($ret === false) $ret = null;
  185.                 if (!is_null($ret)) {
  186.                         foreach ($ret as $key => &$value){
  187.                                 $type = pg_field_type($this->res,pg_field_num($this->res, $key));
  188.                                 if ($type == 'bool'){
  189.                                         $value = ($value == 't');
  190.                                 }
  191.                         }
  192.                 }
  193.                 return $ret;
  194.         }
  195. }
  196.