dolibarr  x.y.z
pgsql.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2001 Fabien Seisen <seisen@linuxfr.org>
3  * Copyright (C) 2002-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
4  * Copyright (C) 2004-2014 Laurent Destailleur <eldy@users.sourceforge.net>
5  * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7  * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
8  * Copyright (C) 2012 Yann Droneaud <yann@droneaud.fr>
9  * Copyright (C) 2012 Florian Henry <florian.henry@open-concept.pro>
10  * Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program. If not, see <https://www.gnu.org/licenses/>.
24  */
25 
31 require_once DOL_DOCUMENT_ROOT.'/core/db/DoliDB.class.php';
32 
36 class DoliDBPgsql extends DoliDB
37 {
39  public $type = 'pgsql'; // Name of manager
40 
42  const LABEL = 'PostgreSQL'; // Label of manager
43 
45  public $forcecharset = 'UTF8'; // Can't be static as it may be forced with a dynamic value
46 
48  public $forcecollate = ''; // Can't be static as it may be forced with a dynamic value
49 
51  const VERSIONMIN = '9.0.0'; // Version min database
52 
54  private $_results;
55 
56  public $unescapeslashquot;
57  public $standard_conforming_strings;
58 
59 
71  public function __construct($type, $host, $user, $pass, $name = '', $port = 0)
72  {
73  global $conf, $langs;
74 
75  // Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
76  if (!empty($conf->db->character_set)) {
77  $this->forcecharset = $conf->db->character_set;
78  }
79  if (!empty($conf->db->dolibarr_main_db_collation)) {
80  $this->forcecollate = $conf->db->dolibarr_main_db_collation;
81  }
82 
83  $this->database_user = $user;
84  $this->database_host = $host;
85  $this->database_port = $port;
86 
87  $this->transaction_opened = 0;
88 
89  //print "Name DB: $host,$user,$pass,$name<br>";
90 
91  if (!function_exists("pg_connect")) {
92  $this->connected = false;
93  $this->ok = false;
94  $this->error = "Pgsql PHP functions are not available in this version of PHP";
95  dol_syslog(get_class($this)."::DoliDBPgsql : Pgsql PHP functions are not available in this version of PHP", LOG_ERR);
96  return $this->ok;
97  }
98 
99  if (!$host) {
100  $this->connected = false;
101  $this->ok = false;
102  $this->error = $langs->trans("ErrorWrongHostParameter");
103  dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Connect, wrong host parameters", LOG_ERR);
104  return $this->ok;
105  }
106 
107  // Essai connexion serveur
108  //print "$host, $user, $pass, $name, $port";
109  $this->db = $this->connect($host, $user, $pass, $name, $port);
110 
111  if ($this->db) {
112  $this->connected = true;
113  $this->ok = true;
114  } else {
115  // host, login ou password incorrect
116  $this->connected = false;
117  $this->ok = false;
118  $this->error = 'Host, login or password incorrect';
119  dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Connect ".$this->error.'. Failed to connect to host='.$host.' port='.$port.' user='.$user, LOG_ERR);
120  }
121 
122  // Si connexion serveur ok et si connexion base demandee, on essaie connexion base
123  if ($this->connected && $name) {
124  if ($this->select_db($name)) {
125  $this->database_selected = true;
126  $this->database_name = $name;
127  $this->ok = true;
128  } else {
129  $this->database_selected = false;
130  $this->database_name = '';
131  $this->ok = false;
132  $this->error = $this->error();
133  dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Select_db ".$this->error, LOG_ERR);
134  }
135  } else {
136  // Pas de selection de base demandee, ok ou ko
137  $this->database_selected = false;
138  }
139 
140  return $this->ok;
141  }
142 
143 
152  public static function convertSQLFromMysql($line, $type = 'auto', $unescapeslashquot = false)
153  {
154  global $conf;
155 
156  // Removed empty line if this is a comment line for SVN tagging
157  if (preg_match('/^--\s\$Id/i', $line)) {
158  return '';
159  }
160  // Return line if this is a comment
161  if (preg_match('/^#/i', $line) || preg_match('/^$/i', $line) || preg_match('/^--/i', $line)) {
162  return $line;
163  }
164  if ($line != "") {
165  // group_concat support (PgSQL >= 9.0)
166  // Replace group_concat(x) or group_concat(x SEPARATOR ',') with string_agg(x, ',')
167  $line = preg_replace('/GROUP_CONCAT/i', 'STRING_AGG', $line);
168  $line = preg_replace('/ SEPARATOR/i', ',', $line);
169  $line = preg_replace('/STRING_AGG\‍(([^,\‍)]+)\‍)/i', 'STRING_AGG(\\1, \',\')', $line);
170  //print $line."\n";
171 
172  if ($type == 'auto') {
173  if (preg_match('/ALTER TABLE/i', $line)) {
174  $type = 'dml';
175  } elseif (preg_match('/CREATE TABLE/i', $line)) {
176  $type = 'dml';
177  } elseif (preg_match('/DROP TABLE/i', $line)) {
178  $type = 'dml';
179  }
180  }
181 
182  $line = preg_replace('/ as signed\‍)/i', ' as integer)', $line);
183 
184  if ($type == 'dml') {
185  $reg = array();
186 
187  $line = preg_replace('/\s/', ' ', $line); // Replace tabulation with space
188 
189  // we are inside create table statement so lets process datatypes
190  if (preg_match('/(ISAM|innodb)/i', $line)) { // end of create table sequence
191  $line = preg_replace('/\‍)[\s\t]*type[\s\t]*=[\s\t]*(MyISAM|innodb).*;/i', ');', $line);
192  $line = preg_replace('/\‍)[\s\t]*engine[\s\t]*=[\s\t]*(MyISAM|innodb).*;/i', ');', $line);
193  $line = preg_replace('/,$/', '', $line);
194  }
195 
196  // Process case: "CREATE TABLE llx_mytable(rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,code..."
197  if (preg_match('/[\s\t\‍(]*(\w*)[\s\t]+int.*auto_increment/i', $line, $reg)) {
198  $newline = preg_replace('/([\s\t\‍(]*)([a-zA-Z_0-9]*)[\s\t]+int.*auto_increment[^,]*/i', '\\1 \\2 SERIAL PRIMARY KEY', $line);
199  //$line = "-- ".$line." replaced by --\n".$newline;
200  $line = $newline;
201  }
202 
203  if (preg_match('/[\s\t\‍(]*(\w*)[\s\t]+bigint.*auto_increment/i', $line, $reg)) {
204  $newline = preg_replace('/([\s\t\‍(]*)([a-zA-Z_0-9]*)[\s\t]+bigint.*auto_increment[^,]*/i', '\\1 \\2 BIGSERIAL PRIMARY KEY', $line);
205  //$line = "-- ".$line." replaced by --\n".$newline;
206  $line = $newline;
207  }
208 
209  // tinyint type conversion
210  $line = preg_replace('/tinyint\‍(?[0-9]*\‍)?/', 'smallint', $line);
211  $line = preg_replace('/tinyint/i', 'smallint', $line);
212 
213  // nuke unsigned
214  $line = preg_replace('/(int\w+|smallint|bigint)\s+unsigned/i', '\\1', $line);
215 
216  // blob -> text
217  $line = preg_replace('/\w*blob/i', 'text', $line);
218 
219  // tinytext/mediumtext -> text
220  $line = preg_replace('/tinytext/i', 'text', $line);
221  $line = preg_replace('/mediumtext/i', 'text', $line);
222  $line = preg_replace('/longtext/i', 'text', $line);
223 
224  $line = preg_replace('/text\‍([0-9]+\‍)/i', 'text', $line);
225 
226  // change not null datetime field to null valid ones
227  // (to support remapping of "zero time" to null
228  $line = preg_replace('/datetime not null/i', 'datetime', $line);
229  $line = preg_replace('/datetime/i', 'timestamp', $line);
230 
231  // double -> numeric
232  $line = preg_replace('/^double/i', 'numeric', $line);
233  $line = preg_replace('/(\s*)double/i', '\\1numeric', $line);
234  // float -> numeric
235  $line = preg_replace('/^float/i', 'numeric', $line);
236  $line = preg_replace('/(\s*)float/i', '\\1numeric', $line);
237 
238  //Check tms timestamp field case (in Mysql this field is defautled to now and
239  // on update defaulted by now
240  $line = preg_replace('/(\s*)tms(\s*)timestamp/i', '\\1tms timestamp without time zone DEFAULT now() NOT NULL', $line);
241 
242  // nuke DEFAULT CURRENT_TIMESTAMP
243  $line = preg_replace('/(\s*)DEFAULT(\s*)CURRENT_TIMESTAMP/i', '\\1', $line);
244 
245  // nuke ON UPDATE CURRENT_TIMESTAMP
246  $line = preg_replace('/(\s*)ON(\s*)UPDATE(\s*)CURRENT_TIMESTAMP/i', '\\1', $line);
247 
248  // unique index(field1,field2)
249  if (preg_match('/unique index\s*\‍((\w+\s*,\s*\w+)\‍)/i', $line)) {
250  $line = preg_replace('/unique index\s*\‍((\w+\s*,\s*\w+)\‍)/i', 'UNIQUE\‍(\\1\‍)', $line);
251  }
252 
253  // We remove end of requests "AFTER fieldxxx"
254  $line = preg_replace('/\sAFTER [a-z0-9_]+/i', '', $line);
255 
256  // We remove start of requests "ALTER TABLE tablexxx" if this is a DROP INDEX
257  $line = preg_replace('/ALTER TABLE [a-z0-9_]+\s+DROP INDEX/i', 'DROP INDEX', $line);
258 
259  // Translate order to rename fields
260  if (preg_match('/ALTER TABLE ([a-z0-9_]+)\s+CHANGE(?: COLUMN)? ([a-z0-9_]+) ([a-z0-9_]+)(.*)$/i', $line, $reg)) {
261  $line = "-- ".$line." replaced by --\n";
262  $line .= "ALTER TABLE ".$reg[1]." RENAME COLUMN ".$reg[2]." TO ".$reg[3];
263  }
264 
265  // Translate order to modify field format
266  if (preg_match('/ALTER TABLE ([a-z0-9_]+)\s+MODIFY(?: COLUMN)? ([a-z0-9_]+) (.*)$/i', $line, $reg)) {
267  $line = "-- ".$line." replaced by --\n";
268  $newreg3 = $reg[3];
269  $newreg3 = preg_replace('/ DEFAULT NULL/i', '', $newreg3);
270  $newreg3 = preg_replace('/ NOT NULL/i', '', $newreg3);
271  $newreg3 = preg_replace('/ NULL/i', '', $newreg3);
272  $newreg3 = preg_replace('/ DEFAULT 0/i', '', $newreg3);
273  $newreg3 = preg_replace('/ DEFAULT \'?[0-9a-zA-Z_@]*\'?/i', '', $newreg3);
274  $line .= "ALTER TABLE ".$reg[1]." ALTER COLUMN ".$reg[2]." TYPE ".$newreg3;
275  // TODO Add alter to set default value or null/not null if there is this in $reg[3]
276  }
277 
278  // alter table add primary key (field1, field2 ...) -> We remove the primary key name not accepted by PostGreSQL
279  // ALTER TABLE llx_dolibarr_modules ADD PRIMARY KEY pk_dolibarr_modules (numero, entity)
280  if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+PRIMARY\s+KEY\s*(.*)\s*\‍((.*)$/i', $line, $reg)) {
281  $line = "-- ".$line." replaced by --\n";
282  $line .= "ALTER TABLE ".$reg[1]." ADD PRIMARY KEY (".$reg[3];
283  }
284 
285  // Translate order to drop primary keys
286  // ALTER TABLE llx_dolibarr_modules DROP PRIMARY KEY pk_xxx
287  if (preg_match('/ALTER\s+TABLE\s*(.*)\s*DROP\s+PRIMARY\s+KEY\s*([^;]+)$/i', $line, $reg)) {
288  $line = "-- ".$line." replaced by --\n";
289  $line .= "ALTER TABLE ".$reg[1]." DROP CONSTRAINT ".$reg[2];
290  }
291 
292  // Translate order to drop foreign keys
293  // ALTER TABLE llx_dolibarr_modules DROP FOREIGN KEY fk_xxx
294  if (preg_match('/ALTER\s+TABLE\s*(.*)\s*DROP\s+FOREIGN\s+KEY\s*(.*)$/i', $line, $reg)) {
295  $line = "-- ".$line." replaced by --\n";
296  $line .= "ALTER TABLE ".$reg[1]." DROP CONSTRAINT ".$reg[2];
297  }
298 
299  // Translate order to add foreign keys
300  // ALTER TABLE llx_tablechild ADD CONSTRAINT fk_tablechild_fk_fieldparent FOREIGN KEY (fk_fieldparent) REFERENCES llx_tableparent (rowid)
301  if (preg_match('/ALTER\s+TABLE\s+(.*)\s*ADD CONSTRAINT\s+(.*)\s*FOREIGN\s+KEY\s*(.*)$/i', $line, $reg)) {
302  $line = preg_replace('/;$/', '', $line);
303  $line .= " DEFERRABLE INITIALLY IMMEDIATE;";
304  }
305 
306  // alter table add [unique] [index] (field1, field2 ...)
307  // ALTER TABLE llx_accountingaccount ADD INDEX idx_accountingaccount_fk_pcg_version (fk_pcg_version)
308  if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+(UNIQUE INDEX|INDEX|UNIQUE)\s+(.*)\s*\‍(([\w,\s]+)\‍)/i', $line, $reg)) {
309  $fieldlist = $reg[4];
310  $idxname = $reg[3];
311  $tablename = $reg[1];
312  $line = "-- ".$line." replaced by --\n";
313  $line .= "CREATE ".(preg_match('/UNIQUE/', $reg[2]) ? 'UNIQUE ' : '')."INDEX ".$idxname." ON ".$tablename." (".$fieldlist.")";
314  }
315  }
316 
317  // To have postgresql case sensitive
318  $count_like = 0;
319  $line = str_replace(' LIKE \'', ' ILIKE \'', $line, $count_like);
320  if (!empty($conf->global->PSQL_USE_UNACCENT) && $count_like > 0) {
321  // @see https://docs.postgresql.fr/11/unaccent.html : 'unaccent()' function must be installed before
322  $line = preg_replace('/\s+(\‍(+\s*)([a-zA-Z0-9\-\_\.]+) ILIKE /', ' \1unaccent(\2) ILIKE ', $line);
323  }
324 
325  $line = str_replace(' LIKE BINARY \'', ' LIKE \'', $line);
326 
327  // Replace INSERT IGNORE into INSERT
328  $line = preg_replace('/^INSERT IGNORE/', 'INSERT', $line);
329 
330  // Delete using criteria on other table must not declare twice the deleted table
331  // DELETE FROM tabletodelete USING tabletodelete, othertable -> DELETE FROM tabletodelete USING othertable
332  if (preg_match('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', $line, $reg)) {
333  if ($reg[1] == $reg[2]) { // If same table, we remove second one
334  $line = preg_replace('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', 'DELETE FROM \\1 USING \\3', $line);
335  }
336  }
337 
338  // Remove () in the tables in FROM if 1 table
339  $line = preg_replace('/FROM\s*\‍((([a-z_]+)\s+as\s+([a-z_]+)\s*)\‍)/i', 'FROM \\1', $line);
340  //print $line."\n";
341 
342  // Remove () in the tables in FROM if 2 table
343  $line = preg_replace('/FROM\s*\‍(([a-z_]+\s+as\s+[a-z_]+)\s*,\s*([a-z_]+\s+as\s+[a-z_]+\s*)\‍)/i', 'FROM \\1, \\2', $line);
344  //print $line."\n";
345 
346  // Remove () in the tables in FROM if 3 table
347  $line = preg_replace('/FROM\s*\‍(([a-z_]+\s+as\s+[a-z_]+)\s*,\s*([a-z_]+\s+as\s+[a-z_]+\s*),\s*([a-z_]+\s+as\s+[a-z_]+\s*)\‍)/i', 'FROM \\1, \\2, \\3', $line);
348  //print $line."\n";
349 
350  // Remove () in the tables in FROM if 4 table
351  $line = preg_replace('/FROM\s*\‍(([a-z_]+\s+as\s+[a-z_]+)\s*,\s*([a-z_]+\s+as\s+[a-z_]+\s*),\s*([a-z_]+\s+as\s+[a-z_]+\s*),\s*([a-z_]+\s+as\s+[a-z_]+\s*)\‍)/i', 'FROM \\1, \\2, \\3, \\4', $line);
352  //print $line."\n";
353 
354  // Remove () in the tables in FROM if 5 table
355  $line = preg_replace('/FROM\s*\‍(([a-z_]+\s+as\s+[a-z_]+)\s*,\s*([a-z_]+\s+as\s+[a-z_]+\s*),\s*([a-z_]+\s+as\s+[a-z_]+\s*),\s*([a-z_]+\s+as\s+[a-z_]+\s*),\s*([a-z_]+\s+as\s+[a-z_]+\s*)\‍)/i', 'FROM \\1, \\2, \\3, \\4, \\5', $line);
356  //print $line."\n";
357 
358  // Replace espacing \' by ''.
359  // By default we do not (should be already done by db->escape function if required
360  // except for sql insert in data file that are mysql escaped so we removed them to
361  // be compatible with standard_conforming_strings=on that considers \ as ordinary character).
362  if ($unescapeslashquot) {
363  $line = preg_replace("/\\\'/", "''", $line);
364  }
365 
366  //print "type=".$type." newline=".$line."<br>\n";
367  }
368 
369  return $line;
370  }
371 
372  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
381  public function select_db($database)
382  {
383  // phpcs:enable
384  if ($database == $this->database_name) {
385  return true;
386  } else {
387  return false;
388  }
389  }
390 
402  public function connect($host, $login, $passwd, $name, $port = 0)
403  {
404  // use pg_pconnect() instead of pg_connect() if you want to use persistent connection costing 1ms, instead of 30ms for non persistent
405 
406  $this->db = false;
407 
408  // connections parameters must be protected (only \ and ' according to pg_connect() manual)
409  $host = str_replace(array("\\", "'"), array("\\\\", "\\'"), $host);
410  $login = str_replace(array("\\", "'"), array("\\\\", "\\'"), $login);
411  $passwd = str_replace(array("\\", "'"), array("\\\\", "\\'"), $passwd);
412  $name = str_replace(array("\\", "'"), array("\\\\", "\\'"), $name);
413  $port = str_replace(array("\\", "'"), array("\\\\", "\\'"), $port);
414 
415  if (!$name) {
416  $name = "postgres"; // When try to connect using admin user
417  }
418 
419  // try first Unix domain socket (local)
420  if ((!empty($host) && $host == "socket") && !defined('NOLOCALSOCKETPGCONNECT')) {
421  $con_string = "dbname='".$name."' user='".$login."' password='".$passwd."'"; // $name may be empty
422  try {
423  $this->db = @pg_connect($con_string);
424  } catch (Exception $e) {
425  // No message
426  }
427  }
428 
429  // if local connection failed or not requested, use TCP/IP
430  if (empty($this->db)) {
431  if (!$host) {
432  $host = "localhost";
433  }
434  if (!$port) {
435  $port = 5432;
436  }
437 
438  $con_string = "host='".$host."' port='".$port."' dbname='".$name."' user='".$login."' password='".$passwd."'";
439  try {
440  $this->db = @pg_connect($con_string);
441  } catch (Exception $e) {
442  print $e->getMessage();
443  }
444  }
445 
446  // now we test if at least one connect method was a success
447  if ($this->db) {
448  $this->database_name = $name;
449  pg_set_error_verbosity($this->db, PGSQL_ERRORS_VERBOSE); // Set verbosity to max
450  pg_query($this->db, "set datestyle = 'ISO, YMD';");
451  }
452 
453  return $this->db;
454  }
455 
461  public function getVersion()
462  {
463  $resql = $this->query('SHOW server_version');
464  if ($resql) {
465  $liste = $this->fetch_array($resql);
466  return $liste['server_version'];
467  }
468  return '';
469  }
470 
476  public function getDriverInfo()
477  {
478  return 'pgsql php driver';
479  }
480 
487  public function close()
488  {
489  if ($this->db) {
490  if ($this->transaction_opened > 0) {
491  dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened, LOG_ERR);
492  }
493  $this->connected = false;
494  return pg_close($this->db);
495  }
496  return false;
497  }
498 
508  public function query($query, $usesavepoint = 0, $type = 'auto', $result_mode = 0)
509  {
510  global $conf, $dolibarr_main_db_readonly;
511 
512  $query = trim($query);
513 
514  // Convert MySQL syntax to PostgresSQL syntax
515  $query = $this->convertSQLFromMysql($query, $type, ($this->unescapeslashquot && $this->standard_conforming_strings));
516  //print "After convertSQLFromMysql:\n".$query."<br>\n";
517 
518  if (!empty($conf->global->MAIN_DB_AUTOFIX_BAD_SQL_REQUEST)) {
519  // Fix bad formed requests. If request contains a date without quotes, we fix this but this should not occurs.
520  $loop = true;
521  while ($loop) {
522  if (preg_match('/([^\'])([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9])/', $query)) {
523  $query = preg_replace('/([^\'])([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9])/', '\\1\'\\2\'', $query);
524  dol_syslog("Warning: Bad formed request converted into ".$query, LOG_WARNING);
525  } else {
526  $loop = false;
527  }
528  }
529  }
530 
531  if ($usesavepoint && $this->transaction_opened) {
532  @pg_query($this->db, 'SAVEPOINT mysavepoint');
533  }
534 
535  if (!in_array($query, array('BEGIN', 'COMMIT', 'ROLLBACK'))) {
536  $SYSLOG_SQL_LIMIT = 10000; // limit log to 10kb per line to limit DOS attacks
537  dol_syslog('sql='.substr($query, 0, $SYSLOG_SQL_LIMIT), LOG_DEBUG);
538  }
539  if (empty($query)) {
540  return false; // Return false = error if empty request
541  }
542 
543  if (!empty($dolibarr_main_db_readonly)) {
544  if (preg_match('/^(INSERT|UPDATE|REPLACE|DELETE|CREATE|ALTER|TRUNCATE|DROP)/i', $query)) {
545  $this->lasterror = 'Application in read-only mode';
546  $this->lasterrno = 'APPREADONLY';
547  $this->lastquery = $query;
548  return false;
549  }
550  }
551 
552  $ret = @pg_query($this->db, $query);
553 
554  //print $query;
555  if (!preg_match("/^COMMIT/i", $query) && !preg_match("/^ROLLBACK/i", $query)) { // Si requete utilisateur, on la sauvegarde ainsi que son resultset
556  if (!$ret) {
557  if ($this->errno() != 'DB_ERROR_25P02') { // Do not overwrite errors if this is a consecutive error
558  $this->lastqueryerror = $query;
559  $this->lasterror = $this->error();
560  $this->lasterrno = $this->errno();
561 
562  if ($conf->global->SYSLOG_LEVEL < LOG_DEBUG) {
563  dol_syslog(get_class($this)."::query SQL Error query: ".$query, LOG_ERR); // Log of request was not yet done previously
564  }
565  dol_syslog(get_class($this)."::query SQL Error message: ".$this->lasterror." (".$this->lasterrno.")", LOG_ERR);
566  dol_syslog(get_class($this)."::query SQL Error usesavepoint = ".$usesavepoint, LOG_ERR);
567  }
568 
569  if ($usesavepoint && $this->transaction_opened) { // Warning, after that errno will be erased
570  @pg_query($this->db, 'ROLLBACK TO SAVEPOINT mysavepoint');
571  }
572  }
573  $this->lastquery = $query;
574  $this->_results = $ret;
575  }
576 
577  return $ret;
578  }
579 
580  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
587  public function fetch_object($resultset)
588  {
589  // phpcs:enable
590  // If resultset not provided, we take the last used by connexion
591  if (!is_resource($resultset) && !is_object($resultset)) {
592  $resultset = $this->_results;
593  }
594  return pg_fetch_object($resultset);
595  }
596 
597  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
604  public function fetch_array($resultset)
605  {
606  // phpcs:enable
607  // If resultset not provided, we take the last used by connexion
608  if (!is_resource($resultset) && !is_object($resultset)) {
609  $resultset = $this->_results;
610  }
611  return pg_fetch_array($resultset);
612  }
613 
614  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
621  public function fetch_row($resultset)
622  {
623  // phpcs:enable
624  // Si le resultset n'est pas fourni, on prend le dernier utilise sur cette connexion
625  if (!is_resource($resultset) && !is_object($resultset)) {
626  $resultset = $this->_results;
627  }
628  return pg_fetch_row($resultset);
629  }
630 
631  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
639  public function num_rows($resultset)
640  {
641  // phpcs:enable
642  // If resultset not provided, we take the last used by connexion
643  if (!is_resource($resultset) && !is_object($resultset)) {
644  $resultset = $this->_results;
645  }
646  return pg_num_rows($resultset);
647  }
648 
649  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
657  public function affected_rows($resultset)
658  {
659  // phpcs:enable
660  // If resultset not provided, we take the last used by connexion
661  if (!is_resource($resultset) && !is_object($resultset)) {
662  $resultset = $this->_results;
663  }
664  // pgsql necessite un resultset pour cette fonction contrairement
665  // a mysql qui prend un link de base
666  return pg_affected_rows($resultset);
667  }
668 
669 
676  public function free($resultset = null)
677  {
678  // If resultset not provided, we take the last used by connexion
679  if (!is_resource($resultset) && !is_object($resultset)) {
680  $resultset = $this->_results;
681  }
682  // Si resultset en est un, on libere la memoire
683  if (is_resource($resultset) || is_object($resultset)) {
684  pg_free_result($resultset);
685  }
686  }
687 
688 
696  public function plimit($limit = 0, $offset = 0)
697  {
698  global $conf;
699  if (empty($limit)) {
700  return "";
701  }
702  if ($limit < 0) {
703  $limit = $conf->liste_limit;
704  }
705  if ($offset > 0) {
706  return " LIMIT ".$limit." OFFSET ".$offset." ";
707  } else {
708  return " LIMIT $limit ";
709  }
710  }
711 
712 
719  public function escape($stringtoencode)
720  {
721  return pg_escape_string($stringtoencode);
722  }
723 
730  public function escapeforlike($stringtoencode)
731  {
732  return str_replace(array('_', '\\', '%'), array('\_', '\\\\', '\%'), (string) $stringtoencode);
733  }
734 
743  public function ifsql($test, $resok, $resko)
744  {
745  return '(CASE WHEN '.$test.' THEN '.$resok.' ELSE '.$resko.' END)';
746  }
747 
756  public function regexpsql($subject, $pattern, $sqlstring = false)
757  {
758  if ($sqlstring) {
759  return "(". $subject ." ~ '" . $pattern . "')";
760  }
761 
762  return "('". $subject ."' ~ '" . $pattern . "')";
763  }
764 
765 
771  public function errno()
772  {
773  if (!$this->connected) {
774  // Si il y a eu echec de connexion, $this->db n'est pas valide.
775  return 'DB_ERROR_FAILED_TO_CONNECT';
776  } else {
777  // Constants to convert error code to a generic Dolibarr error code
778  $errorcode_map = array(
779  1004 => 'DB_ERROR_CANNOT_CREATE',
780  1005 => 'DB_ERROR_CANNOT_CREATE',
781  1006 => 'DB_ERROR_CANNOT_CREATE',
782  1007 => 'DB_ERROR_ALREADY_EXISTS',
783  1008 => 'DB_ERROR_CANNOT_DROP',
784  1025 => 'DB_ERROR_NO_FOREIGN_KEY_TO_DROP',
785  1044 => 'DB_ERROR_ACCESSDENIED',
786  1046 => 'DB_ERROR_NODBSELECTED',
787  1048 => 'DB_ERROR_CONSTRAINT',
788  '42P07' => 'DB_ERROR_TABLE_OR_KEY_ALREADY_EXISTS',
789  '42703' => 'DB_ERROR_NOSUCHFIELD',
790  1060 => 'DB_ERROR_COLUMN_ALREADY_EXISTS',
791  42701=> 'DB_ERROR_COLUMN_ALREADY_EXISTS',
792  '42710' => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
793  '23505' => 'DB_ERROR_RECORD_ALREADY_EXISTS',
794  '42704' => 'DB_ERROR_NO_INDEX_TO_DROP', // May also be Type xxx does not exists
795  '42601' => 'DB_ERROR_SYNTAX',
796  '42P16' => 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS',
797  1075 => 'DB_ERROR_CANT_DROP_PRIMARY_KEY',
798  1091 => 'DB_ERROR_NOSUCHFIELD',
799  1100 => 'DB_ERROR_NOT_LOCKED',
800  1136 => 'DB_ERROR_VALUE_COUNT_ON_ROW',
801  '42P01' => 'DB_ERROR_NOSUCHTABLE',
802  '23503' => 'DB_ERROR_NO_PARENT',
803  1217 => 'DB_ERROR_CHILD_EXISTS',
804  1451 => 'DB_ERROR_CHILD_EXISTS',
805  '42P04' => 'DB_DATABASE_ALREADY_EXISTS'
806  );
807 
808  $errorlabel = pg_last_error($this->db);
809  $errorcode = '';
810  $reg = array();
811  if (preg_match('/: *([0-9P]+):/', $errorlabel, $reg)) {
812  $errorcode = $reg[1];
813  if (isset($errorcode_map[$errorcode])) {
814  return $errorcode_map[$errorcode];
815  }
816  }
817  $errno = $errorcode ? $errorcode : $errorlabel;
818  return ($errno ? 'DB_ERROR_'.$errno : '0');
819  }
820  // '/(Table does not exist\.|Relation [\"\'].*[\"\'] does not exist|sequence does not exist|class ".+" not found)$/' => 'DB_ERROR_NOSUCHTABLE',
821  // '/table [\"\'].*[\"\'] does not exist/' => 'DB_ERROR_NOSUCHTABLE',
822  // '/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*/' => 'DB_ERROR_RECORD_ALREADY_EXISTS',
823  // '/divide by zero$/' => 'DB_ERROR_DIVZERO',
824  // '/pg_atoi: error in .*: can\'t parse /' => 'DB_ERROR_INVALID_NUMBER',
825  // '/ttribute [\"\'].*[\"\'] not found$|Relation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => 'DB_ERROR_NOSUCHFIELD',
826  // '/parser: parse error at or near \"/' => 'DB_ERROR_SYNTAX',
827  // '/referential integrity violation/' => 'DB_ERROR_CONSTRAINT'
828  }
829 
835  public function error()
836  {
837  return pg_last_error($this->db);
838  }
839 
840  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
848  public function last_insert_id($tab, $fieldid = 'rowid')
849  {
850  // phpcs:enable
851  //$result = pg_query($this->db,"SELECT MAX(".$fieldid.") FROM ".$tab);
852  $result = pg_query($this->db, "SELECT currval('".$tab."_".$fieldid."_seq')");
853  if (!$result) {
854  print pg_last_error($this->db);
855  exit;
856  }
857  //$nbre = pg_num_rows($result);
858  $row = pg_fetch_result($result, 0, 0);
859  return $row;
860  }
861 
870  public function encrypt($fieldorvalue, $withQuotes = 1)
871  {
872  global $conf;
873 
874  // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
875  //$cryptType = ($conf->db->dolibarr_main_db_encryption ? $conf->db->dolibarr_main_db_encryption : 0);
876 
877  //Encryption key
878  //$cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : '');
879 
880  $return = $fieldorvalue;
881  return ($withQuotes ? "'" : "").$this->escape($return).($withQuotes ? "'" : "");
882  }
883 
884 
891  public function decrypt($value)
892  {
893  global $conf;
894 
895  // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
896  //$cryptType = ($conf->db->dolibarr_main_db_encryption ? $conf->db->dolibarr_main_db_encryption : 0);
897 
898  //Encryption key
899  //$cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : '');
900 
901  $return = $value;
902  return $return;
903  }
904 
905 
906  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
912  public function DDLGetConnectId()
913  {
914  // phpcs:enable
915  return '?';
916  }
917 
918 
919 
920  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
932  public function DDLCreateDb($database, $charset = '', $collation = '', $owner = '')
933  {
934  // phpcs:enable
935  if (empty($charset)) {
936  $charset = $this->forcecharset;
937  }
938  if (empty($collation)) {
939  $collation = $this->forcecollate;
940  }
941 
942  // Test charset match LC_TYPE (pgsql error otherwise)
943  //print $charset.' '.setlocale(LC_CTYPE,'0'); exit;
944 
945  // NOTE: Do not use ' around the database name
946  $sql = "CREATE DATABASE ".$this->escape($database)." OWNER '".$this->escape($owner)."' ENCODING '".$this->escape($charset)."'";
947  dol_syslog($sql, LOG_DEBUG);
948  $ret = $this->query($sql);
949  return $ret;
950  }
951 
952  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
960  public function DDLListTables($database, $table = '')
961  {
962  // phpcs:enable
963  $listtables = array();
964 
965  $escapedlike = '';
966  if ($table) {
967  $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table);
968 
969  $escapedlike = " AND table_name LIKE '".$this->escape($tmptable)."'";
970  }
971  $result = pg_query($this->db, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'".$escapedlike." ORDER BY table_name");
972  if ($result) {
973  while ($row = $this->fetch_row($result)) {
974  $listtables[] = $row[0];
975  }
976  }
977  return $listtables;
978  }
979 
980  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
988  public function DDLInfoTable($table)
989  {
990  // phpcs:enable
991  $infotables = array();
992 
993  $sql = "SELECT ";
994  $sql .= " infcol.column_name as \"Column\",";
995  $sql .= " CASE WHEN infcol.character_maximum_length IS NOT NULL THEN infcol.udt_name || '('||infcol.character_maximum_length||')'";
996  $sql .= " ELSE infcol.udt_name";
997  $sql .= " END as \"Type\",";
998  $sql .= " infcol.collation_name as \"Collation\",";
999  $sql .= " infcol.is_nullable as \"Null\",";
1000  $sql .= " '' as \"Key\",";
1001  $sql .= " infcol.column_default as \"Default\",";
1002  $sql .= " '' as \"Extra\",";
1003  $sql .= " '' as \"Privileges\"";
1004  $sql .= " FROM information_schema.columns infcol";
1005  $sql .= " WHERE table_schema = 'public' ";
1006  $sql .= " AND table_name = '".$this->escape($table)."'";
1007  $sql .= " ORDER BY ordinal_position;";
1008 
1009  dol_syslog($sql, LOG_DEBUG);
1010  $result = $this->query($sql);
1011  if ($result) {
1012  while ($row = $this->fetch_row($result)) {
1013  $infotables[] = $row;
1014  }
1015  }
1016  return $infotables;
1017  }
1018 
1019 
1020  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1033  public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null)
1034  {
1035  // phpcs:enable
1036  // FIXME: $fulltext_keys parameter is unused
1037 
1038  // cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra
1039  // ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
1040  $sql = "create table ".$table."(";
1041  $i = 0;
1042  foreach ($fields as $field_name => $field_desc) {
1043  $sqlfields[$i] = $field_name." ";
1044  $sqlfields[$i] .= $field_desc['type'];
1045  if (preg_match("/^[^\s]/i", $field_desc['value'])) {
1046  $sqlfields[$i] .= "(".$field_desc['value'].")";
1047  } elseif (preg_match("/^[^\s]/i", $field_desc['attribute'])) {
1048  $sqlfields[$i] .= " ".$field_desc['attribute'];
1049  } elseif (preg_match("/^[^\s]/i", $field_desc['default'])) {
1050  if (preg_match("/null/i", $field_desc['default'])) {
1051  $sqlfields[$i] .= " default ".$field_desc['default'];
1052  } else {
1053  $sqlfields[$i] .= " default '".$this->escape($field_desc['default'])."'";
1054  }
1055  } elseif (preg_match("/^[^\s]/i", $field_desc['null'])) {
1056  $sqlfields[$i] .= " ".$field_desc['null'];
1057  } elseif (preg_match("/^[^\s]/i", $field_desc['extra'])) {
1058  $sqlfields[$i] .= " ".$field_desc['extra'];
1059  }
1060  $i++;
1061  }
1062  if ($primary_key != "") {
1063  $pk = "primary key(".$primary_key.")";
1064  }
1065 
1066  if (is_array($unique_keys)) {
1067  $i = 0;
1068  foreach ($unique_keys as $key => $value) {
1069  $sqluq[$i] = "UNIQUE KEY '".$key."' ('".$this->escape($value)."')";
1070  $i++;
1071  }
1072  }
1073  if (is_array($keys)) {
1074  $i = 0;
1075  foreach ($keys as $key => $value) {
1076  $sqlk[$i] = "KEY ".$key." (".$value.")";
1077  $i++;
1078  }
1079  }
1080  $sql .= implode(',', $sqlfields);
1081  if ($primary_key != "") {
1082  $sql .= ",".$pk;
1083  }
1084  if (is_array($unique_keys)) {
1085  $sql .= ",".implode(',', $sqluq);
1086  }
1087  if (is_array($keys)) {
1088  $sql .= ",".implode(',', $sqlk);
1089  }
1090  $sql .= ") type=".$type;
1091 
1092  dol_syslog($sql, LOG_DEBUG);
1093  if (!$this->query($sql)) {
1094  return -1;
1095  } else {
1096  return 1;
1097  }
1098  }
1099 
1100  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1107  public function DDLDropTable($table)
1108  {
1109  // phpcs:enable
1110  $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table);
1111 
1112  $sql = "DROP TABLE ".$tmptable;
1113 
1114  if (!$this->query($sql)) {
1115  return -1;
1116  } else {
1117  return 1;
1118  }
1119  }
1120 
1121  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1131  public function DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name)
1132  {
1133  // phpcs:enable
1134  // Note: using ' on user does not works with pgsql
1135  $sql = "CREATE USER ".$this->escape($dolibarr_main_db_user)." with password '".$this->escape($dolibarr_main_db_pass)."'";
1136 
1137  dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log
1138  $resql = $this->query($sql);
1139  if (!$resql) {
1140  return -1;
1141  }
1142 
1143  return 1;
1144  }
1145 
1146  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1154  public function DDLDescTable($table, $field = "")
1155  {
1156  // phpcs:enable
1157  $sql = "SELECT attname FROM pg_attribute, pg_type WHERE typname = '".$this->escape($table)."' AND attrelid = typrelid";
1158  $sql .= " AND attname NOT IN ('cmin', 'cmax', 'ctid', 'oid', 'tableoid', 'xmin', 'xmax')";
1159  if ($field) {
1160  $sql .= " AND attname = '".$this->escape($field)."'";
1161  }
1162 
1163  dol_syslog($sql, LOG_DEBUG);
1164  $this->_results = $this->query($sql);
1165  return $this->_results;
1166  }
1167 
1168  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1178  public function DDLAddField($table, $field_name, $field_desc, $field_position = "")
1179  {
1180  // phpcs:enable
1181  // cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra
1182  // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
1183  $sql = "ALTER TABLE ".$table." ADD ".$field_name." ";
1184  $sql .= $field_desc['type'];
1185  if (preg_match("/^[^\s]/i", $field_desc['value'])) {
1186  if (!in_array($field_desc['type'], array('int', 'date', 'datetime')) && $field_desc['value']) {
1187  $sql .= "(".$field_desc['value'].")";
1188  }
1189  }
1190  if (preg_match("/^[^\s]/i", $field_desc['attribute'])) {
1191  $sql .= " ".$field_desc['attribute'];
1192  }
1193  if (preg_match("/^[^\s]/i", $field_desc['null'])) {
1194  $sql .= " ".$field_desc['null'];
1195  }
1196  if (preg_match("/^[^\s]/i", $field_desc['default'])) {
1197  if (preg_match("/null/i", $field_desc['default'])) {
1198  $sql .= " default ".$field_desc['default'];
1199  } else {
1200  $sql .= " default '".$this->escape($field_desc['default'])."'";
1201  }
1202  }
1203  if (preg_match("/^[^\s]/i", $field_desc['extra'])) {
1204  $sql .= " ".$field_desc['extra'];
1205  }
1206  $sql .= " ".$field_position;
1207 
1208  dol_syslog($sql, LOG_DEBUG);
1209  if (!$this -> query($sql)) {
1210  return -1;
1211  }
1212  return 1;
1213  }
1214 
1215  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1224  public function DDLUpdateField($table, $field_name, $field_desc)
1225  {
1226  // phpcs:enable
1227  $sql = "ALTER TABLE ".$table;
1228  $sql .= " MODIFY COLUMN ".$field_name." ".$field_desc['type'];
1229  if (in_array($field_desc['type'], array('double', 'varchar')) && $field_desc['value']) {
1230  $sql .= "(".$field_desc['value'].")";
1231  }
1232 
1233  if ($field_desc['null'] == 'not null' || $field_desc['null'] == 'NOT NULL') {
1234  // We will try to change format of column to NOT NULL. To be sure the ALTER works, we try to update fields that are NULL
1235  if ($field_desc['type'] == 'varchar' || $field_desc['type'] == 'text') {
1236  $sqlbis = "UPDATE ".$table." SET ".$field_name." = '".$this->escape($field_desc['default'] ? $field_desc['default'] : '')."' WHERE ".$field_name." IS NULL";
1237  $this->query($sqlbis);
1238  } elseif ($field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') {
1239  $sqlbis = "UPDATE ".$table." SET ".$field_name." = ".((int) $this->escape($field_desc['default'] ? $field_desc['default'] : 0))." WHERE ".$field_name." IS NULL";
1240  $this->query($sqlbis);
1241  }
1242  }
1243 
1244  if ($field_desc['default'] != '') {
1245  if ($field_desc['type'] == 'double' || $field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') {
1246  $sql .= " DEFAULT ".$this->escape($field_desc['default']);
1247  } elseif ($field_desc['type'] != 'text') {
1248  $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'"; // Default not supported on text fields
1249  }
1250  }
1251 
1252  dol_syslog($sql, LOG_DEBUG);
1253  if (!$this->query($sql)) {
1254  return -1;
1255  }
1256  return 1;
1257  }
1258 
1259  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1267  public function DDLDropField($table, $field_name)
1268  {
1269  // phpcs:enable
1270  $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name);
1271 
1272  $sql = "ALTER TABLE ".$table." DROP COLUMN ".$tmp_field_name;
1273  if (!$this->query($sql)) {
1274  $this->error = $this->lasterror();
1275  return -1;
1276  }
1277  return 1;
1278  }
1279 
1286  {
1287  $resql = $this->query('SHOW SERVER_ENCODING');
1288  if ($resql) {
1289  $liste = $this->fetch_array($resql);
1290  return $liste['server_encoding'];
1291  } else {
1292  return '';
1293  }
1294  }
1295 
1301  public function getListOfCharacterSet()
1302  {
1303  $resql = $this->query('SHOW SERVER_ENCODING');
1304  $liste = array();
1305  if ($resql) {
1306  $i = 0;
1307  while ($obj = $this->fetch_object($resql)) {
1308  $liste[$i]['charset'] = $obj->server_encoding;
1309  $liste[$i]['description'] = 'Default database charset';
1310  $i++;
1311  }
1312  $this->free($resql);
1313  } else {
1314  return null;
1315  }
1316  return $liste;
1317  }
1318 
1325  {
1326  $resql = $this->query('SHOW LC_COLLATE');
1327  if ($resql) {
1328  $liste = $this->fetch_array($resql);
1329  return $liste['lc_collate'];
1330  } else {
1331  return '';
1332  }
1333  }
1334 
1340  public function getListOfCollation()
1341  {
1342  $resql = $this->query('SHOW LC_COLLATE');
1343  $liste = array();
1344  if ($resql) {
1345  $i = 0;
1346  while ($obj = $this->fetch_object($resql)) {
1347  $liste[$i]['collation'] = $obj->lc_collate;
1348  $i++;
1349  }
1350  $this->free($resql);
1351  } else {
1352  return null;
1353  }
1354  return $liste;
1355  }
1356 
1362  public function getPathOfDump()
1363  {
1364  $fullpathofdump = '/pathtopgdump/pg_dump';
1365 
1366  if (file_exists('/usr/bin/pg_dump')) {
1367  $fullpathofdump = '/usr/bin/pg_dump';
1368  } else {
1369  // TODO L'utilisateur de la base doit etre un superadmin pour lancer cette commande
1370  $resql = $this->query('SHOW data_directory');
1371  if ($resql) {
1372  $liste = $this->fetch_array($resql);
1373  $basedir = $liste['data_directory'];
1374  $fullpathofdump = preg_replace('/data$/', 'bin', $basedir).'/pg_dump';
1375  }
1376  }
1377 
1378  return $fullpathofdump;
1379  }
1380 
1386  public function getPathOfRestore()
1387  {
1388  //$tool='pg_restore';
1389  $tool = 'psql';
1390 
1391  $fullpathofdump = '/pathtopgrestore/'.$tool;
1392 
1393  if (file_exists('/usr/bin/'.$tool)) {
1394  $fullpathofdump = '/usr/bin/'.$tool;
1395  } else {
1396  // TODO L'utilisateur de la base doit etre un superadmin pour lancer cette commande
1397  $resql = $this->query('SHOW data_directory');
1398  if ($resql) {
1399  $liste = $this->fetch_array($resql);
1400  $basedir = $liste['data_directory'];
1401  $fullpathofdump = preg_replace('/data$/', 'bin', $basedir).'/'.$tool;
1402  }
1403  }
1404 
1405  return $fullpathofdump;
1406  }
1407 
1414  public function getServerParametersValues($filter = '')
1415  {
1416  $result = array();
1417 
1418  $resql = 'select name,setting from pg_settings';
1419  if ($filter) {
1420  $resql .= " WHERE name = '".$this->escape($filter)."'";
1421  }
1422  $resql = $this->query($resql);
1423  if ($resql) {
1424  while ($obj = $this->fetch_object($resql)) {
1425  $result[$obj->name] = $obj->setting;
1426  }
1427  }
1428 
1429  return $result;
1430  }
1431 
1438  public function getServerStatusValues($filter = '')
1439  {
1440  /* This is to return current running requests.
1441  $sql='SELECT datname,procpid,current_query FROM pg_stat_activity ORDER BY procpid';
1442  if ($filter) $sql.=" LIKE '".$this->escape($filter)."'";
1443  $resql=$this->query($sql);
1444  if ($resql)
1445  {
1446  $obj=$this->fetch_object($resql);
1447  $result[$obj->Variable_name]=$obj->Value;
1448  }
1449  */
1450 
1451  return array();
1452  }
1453 }
Class to manage Dolibarr database access.
lastqueryerror()
Return last query in error.
lasterror()
Return last error label.
lasterrno()
Return last error code.
lastquery()
Return last request executed with query()
Class to drive a Postgresql database for Dolibarr.
Definition: pgsql.class.php:37
errno()
Renvoie le code erreur generique de l'operation precedente.
DDLGetConnectId()
Return connexion ID.
num_rows($resultset)
Return number of lines for result of a SELECT.
const VERSIONMIN
Version min database.
Definition: pgsql.class.php:51
__construct($type, $host, $user, $pass, $name='', $port=0)
Constructor.
Definition: pgsql.class.php:71
static convertSQLFromMysql($line, $type='auto', $unescapeslashquot=false)
Convert a SQL request in Mysql syntax to native syntax.
DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name)
Create a user to connect to database.
DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys=null, $fulltext_keys=null, $keys=null)
Create a table into database.
DDLDropTable($table)
Drop a table into database.
DDLUpdateField($table, $field_name, $field_desc)
Update format of a field into a table.
getPathOfDump()
Return full path of dump program.
select_db($database)
Select a database Ici postgresql n'a aucune fonction equivalente de mysql_select_db On compare juste ...
getServerStatusValues($filter='')
Return value of server status.
plimit($limit=0, $offset=0)
Define limits and offset of request.
decrypt($value)
Decrypt sensitive data in database.
error()
Renvoie le texte de l'erreur pgsql de l'operation precedente.
escape($stringtoencode)
Escape a string to insert data.
fetch_object($resultset)
Returns the current line (as an object) for the resultset cursor.
close()
Close database connexion.
encrypt($fieldorvalue, $withQuotes=1)
Encrypt sensitive data in database Warning: This function includes the escape and add the SQL simple ...
getListOfCharacterSet()
Return list of available charset that can be used to store data in database.
fetch_array($resultset)
Return datas as an array.
regexpsql($subject, $pattern, $sqlstring=false)
Format a SQL REGEXP.
last_insert_id($tab, $fieldid='rowid')
Get last ID after an insert INSERT.
getPathOfRestore()
Return full path of restore program.
DDLAddField($table, $field_name, $field_desc, $field_position="")
Create a new field into table.
$type
Database type.
Definition: pgsql.class.php:39
DDLInfoTable($table)
List information of columns into a table.
connect($host, $login, $passwd, $name, $port=0)
Connexion to server.
getVersion()
Return version of database server.
$forcecharset
Charset.
Definition: pgsql.class.php:45
escapeforlike($stringtoencode)
Escape a string to insert data into a like.
affected_rows($resultset)
Return the number of lines in the result of a request INSERT, DELETE or UPDATE.
DDLDropField($table, $field_name)
Drop a field from table.
DDLCreateDb($database, $charset='', $collation='', $owner='')
Create a new database Do not use function xxx_create_db (xxx=mysql, ...) as they are deprecated We fo...
const LABEL
Database label.
Definition: pgsql.class.php:42
getListOfCollation()
Return list of available collation that can be used for database.
getDriverInfo()
Return version of database client driver.
free($resultset=null)
Libere le dernier resultset utilise sur cette connexion.
DDLDescTable($table, $field="")
Return a pointer of line with description of a table or field.
ifsql($test, $resok, $resko)
Format a SQL IF.
getDefaultCollationDatabase()
Return collation used in database.
getDefaultCharacterSetDatabase()
Return charset used to store data in database.
query($query, $usesavepoint=0, $type='auto', $result_mode=0)
Convert request to PostgreSQL syntax, execute it and return the resultset.
$forcecollate
Collate used to force collate when creating database.
Definition: pgsql.class.php:48
fetch_row($resultset)
Return datas as an array.
getServerParametersValues($filter='')
Return value of server parameters.
DDLListTables($database, $table='')
List tables into a database.
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
$conf db
API class for accounts.
Definition: inc.php:41