dolibarr  20.0.0-alpha
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  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program. If not, see <https://www.gnu.org/licenses/>.
25  */
26 
32 require_once DOL_DOCUMENT_ROOT.'/core/db/DoliDB.class.php';
33 
37 class DoliDBPgsql extends DoliDB
38 {
40  public $type = 'pgsql'; // Name of manager
41 
43  const LABEL = 'PostgreSQL'; // Label of manager
44 
46  public $forcecharset = 'UTF8'; // Can't be static as it may be forced with a dynamic value
47 
49  public $forcecollate = ''; // Can't be static as it may be forced with a dynamic value
50 
52  const VERSIONMIN = '9.0.0'; // Version min database
53 
57  public $unescapeslashquot = false;
61  public $standard_conforming_strings = false;
62 
63 
65  private $_results;
66 
67 
68 
80  public function __construct($type, $host, $user, $pass, $name = '', $port = 0)
81  {
82  global $conf, $langs;
83 
84  // Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
85  if (!empty($conf->db->character_set)) {
86  $this->forcecharset = $conf->db->character_set;
87  }
88  if (!empty($conf->db->dolibarr_main_db_collation)) {
89  $this->forcecollate = $conf->db->dolibarr_main_db_collation;
90  }
91 
92  $this->database_user = $user;
93  $this->database_host = $host;
94  $this->database_port = $port;
95 
96  $this->transaction_opened = 0;
97 
98  //print "Name DB: $host,$user,$pass,$name<br>";
99 
100  if (!function_exists("pg_connect")) {
101  $this->connected = false;
102  $this->ok = false;
103  $this->error = "Pgsql PHP functions are not available in this version of PHP";
104  dol_syslog(get_class($this)."::DoliDBPgsql : Pgsql PHP functions are not available in this version of PHP", LOG_ERR);
105  return;
106  }
107 
108  if (!$host) {
109  $this->connected = false;
110  $this->ok = false;
111  $this->error = $langs->trans("ErrorWrongHostParameter");
112  dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Connect, wrong host parameters", LOG_ERR);
113  return;
114  }
115 
116  // Essai connection serveur
117  //print "$host, $user, $pass, $name, $port";
118  $this->db = $this->connect($host, $user, $pass, $name, $port);
119 
120  if ($this->db) {
121  $this->connected = true;
122  $this->ok = true;
123  } else {
124  // host, login ou password incorrect
125  $this->connected = false;
126  $this->ok = false;
127  $this->error = 'Host, login or password incorrect';
128  dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Connect ".$this->error.'. Failed to connect to host='.$host.' port='.$port.' user='.$user, LOG_ERR);
129  }
130 
131  // If server connection serveur ok and DB connection is requested, try to connect to DB
132  if ($this->connected && $name) {
133  if ($this->select_db($name)) {
134  $this->database_selected = true;
135  $this->database_name = $name;
136  $this->ok = true;
137  } else {
138  $this->database_selected = false;
139  $this->database_name = '';
140  $this->ok = false;
141  $this->error = $this->error();
142  dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Select_db ".$this->error, LOG_ERR);
143  }
144  } else {
145  // Pas de selection de base demandee, ok ou ko
146  $this->database_selected = false;
147  }
148  }
149 
150 
159  public function convertSQLFromMysql($line, $type = 'auto', $unescapeslashquot = false)
160  {
161  global $conf;
162 
163  // Removed empty line if this is a comment line for SVN tagging
164  if (preg_match('/^--\s\$Id/i', $line)) {
165  return '';
166  }
167  // Return line if this is a comment
168  if (preg_match('/^#/i', $line) || preg_match('/^$/i', $line) || preg_match('/^--/i', $line)) {
169  return $line;
170  }
171  if ($line != "") {
172  // group_concat support (PgSQL >= 9.0)
173  // Replace group_concat(x) or group_concat(x SEPARATOR ',') with string_agg(x, ',')
174  $line = preg_replace('/GROUP_CONCAT/i', 'STRING_AGG', $line);
175  $line = preg_replace('/ SEPARATOR/i', ',', $line);
176  $line = preg_replace('/STRING_AGG\‍(([^,\‍)]+)\‍)/i', 'STRING_AGG(\\1, \',\')', $line);
177  //print $line."\n";
178 
179  if ($type == 'auto') {
180  if (preg_match('/ALTER TABLE/i', $line)) {
181  $type = 'dml';
182  } elseif (preg_match('/CREATE TABLE/i', $line)) {
183  $type = 'dml';
184  } elseif (preg_match('/DROP TABLE/i', $line)) {
185  $type = 'dml';
186  }
187  }
188 
189  $line = preg_replace('/ as signed\‍)/i', ' as integer)', $line);
190 
191  if ($type == 'dml') {
192  $reg = array();
193 
194  $line = preg_replace('/\s/', ' ', $line); // Replace tabulation with space
195 
196  // we are inside create table statement so lets process datatypes
197  if (preg_match('/(ISAM|innodb)/i', $line)) { // end of create table sequence
198  $line = preg_replace('/\‍)[\s\t]*type[\s\t]*=[\s\t]*(MyISAM|innodb).*;/i', ');', $line);
199  $line = preg_replace('/\‍)[\s\t]*engine[\s\t]*=[\s\t]*(MyISAM|innodb).*;/i', ');', $line);
200  $line = preg_replace('/,$/', '', $line);
201  }
202 
203  // Process case: "CREATE TABLE llx_mytable(rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,code..."
204  if (preg_match('/[\s\t\‍(]*(\w*)[\s\t]+int.*auto_increment/i', $line, $reg)) {
205  $newline = preg_replace('/([\s\t\‍(]*)([a-zA-Z_0-9]*)[\s\t]+int.*auto_increment[^,]*/i', '\\1 \\2 SERIAL PRIMARY KEY', $line);
206  //$line = "-- ".$line." replaced by --\n".$newline;
207  $line = $newline;
208  }
209 
210  if (preg_match('/[\s\t\‍(]*(\w*)[\s\t]+bigint.*auto_increment/i', $line, $reg)) {
211  $newline = preg_replace('/([\s\t\‍(]*)([a-zA-Z_0-9]*)[\s\t]+bigint.*auto_increment[^,]*/i', '\\1 \\2 BIGSERIAL PRIMARY KEY', $line);
212  //$line = "-- ".$line." replaced by --\n".$newline;
213  $line = $newline;
214  }
215 
216  // tinyint type conversion
217  $line = preg_replace('/tinyint\‍(?[0-9]*\‍)?/', 'smallint', $line);
218  $line = preg_replace('/tinyint/i', 'smallint', $line);
219 
220  // nuke unsigned
221  $line = preg_replace('/(int\w+|smallint|bigint)\s+unsigned/i', '\\1', $line);
222 
223  // blob -> text
224  $line = preg_replace('/\w*blob/i', 'text', $line);
225 
226  // tinytext/mediumtext -> text
227  $line = preg_replace('/tinytext/i', 'text', $line);
228  $line = preg_replace('/mediumtext/i', 'text', $line);
229  $line = preg_replace('/longtext/i', 'text', $line);
230 
231  $line = preg_replace('/text\‍([0-9]+\‍)/i', 'text', $line);
232 
233  // change not null datetime field to null valid ones
234  // (to support remapping of "zero time" to null
235  $line = preg_replace('/datetime not null/i', 'datetime', $line);
236  $line = preg_replace('/datetime/i', 'timestamp', $line);
237 
238  // double -> numeric
239  $line = preg_replace('/^double/i', 'numeric', $line);
240  $line = preg_replace('/(\s*)double/i', '\\1numeric', $line);
241  // float -> numeric
242  $line = preg_replace('/^float/i', 'numeric', $line);
243  $line = preg_replace('/(\s*)float/i', '\\1numeric', $line);
244 
245  //Check tms timestamp field case (in Mysql this field is defaulted to now and
246  // on update defaulted by now
247  $line = preg_replace('/(\s*)tms(\s*)timestamp/i', '\\1tms timestamp without time zone DEFAULT now() NOT NULL', $line);
248 
249  // nuke DEFAULT CURRENT_TIMESTAMP
250  $line = preg_replace('/(\s*)DEFAULT(\s*)CURRENT_TIMESTAMP/i', '\\1', $line);
251 
252  // nuke ON UPDATE CURRENT_TIMESTAMP
253  $line = preg_replace('/(\s*)ON(\s*)UPDATE(\s*)CURRENT_TIMESTAMP/i', '\\1', $line);
254 
255  // unique index(field1,field2)
256  if (preg_match('/unique index\s*\‍((\w+\s*,\s*\w+)\‍)/i', $line)) {
257  $line = preg_replace('/unique index\s*\‍((\w+\s*,\s*\w+)\‍)/i', 'UNIQUE\‍(\\1\‍)', $line);
258  }
259 
260  // We remove end of requests "AFTER fieldxxx"
261  $line = preg_replace('/\sAFTER [a-z0-9_]+/i', '', $line);
262 
263  // We remove start of requests "ALTER TABLE tablexxx" if this is a DROP INDEX
264  $line = preg_replace('/ALTER TABLE [a-z0-9_]+\s+DROP INDEX/i', 'DROP INDEX', $line);
265 
266  // Translate order to rename fields
267  if (preg_match('/ALTER TABLE ([a-z0-9_]+)\s+CHANGE(?: COLUMN)? ([a-z0-9_]+) ([a-z0-9_]+)(.*)$/i', $line, $reg)) {
268  $line = "-- ".$line." replaced by --\n";
269  $line .= "ALTER TABLE ".$reg[1]." RENAME COLUMN ".$reg[2]." TO ".$reg[3];
270  }
271 
272  // Translate order to modify field format
273  if (preg_match('/ALTER TABLE ([a-z0-9_]+)\s+MODIFY(?: COLUMN)? ([a-z0-9_]+) (.*)$/i', $line, $reg)) {
274  $line = "-- ".$line." replaced by --\n";
275  $newreg3 = $reg[3];
276  $newreg3 = preg_replace('/ DEFAULT NULL/i', '', $newreg3);
277  $newreg3 = preg_replace('/ NOT NULL/i', '', $newreg3);
278  $newreg3 = preg_replace('/ NULL/i', '', $newreg3);
279  $newreg3 = preg_replace('/ DEFAULT 0/i', '', $newreg3);
280  $newreg3 = preg_replace('/ DEFAULT \'?[0-9a-zA-Z_@]*\'?/i', '', $newreg3);
281  $line .= "ALTER TABLE ".$reg[1]." ALTER COLUMN ".$reg[2]." TYPE ".$newreg3;
282  // TODO Add alter to set default value or null/not null if there is this in $reg[3]
283  }
284 
285  // alter table add primary key (field1, field2 ...) -> We remove the primary key name not accepted by PostGreSQL
286  // ALTER TABLE llx_dolibarr_modules ADD PRIMARY KEY pk_dolibarr_modules (numero, entity)
287  if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+PRIMARY\s+KEY\s*(.*)\s*\‍((.*)$/i', $line, $reg)) {
288  $line = "-- ".$line." replaced by --\n";
289  $line .= "ALTER TABLE ".$reg[1]." ADD PRIMARY KEY (".$reg[3];
290  }
291 
292  // Translate order to drop primary keys
293  // ALTER TABLE llx_dolibarr_modules DROP PRIMARY KEY pk_xxx
294  if (preg_match('/ALTER\s+TABLE\s*(.*)\s*DROP\s+PRIMARY\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 drop foreign keys
300  // ALTER TABLE llx_dolibarr_modules DROP FOREIGN KEY fk_xxx
301  if (preg_match('/ALTER\s+TABLE\s*(.*)\s*DROP\s+FOREIGN\s+KEY\s*(.*)$/i', $line, $reg)) {
302  $line = "-- ".$line." replaced by --\n";
303  $line .= "ALTER TABLE ".$reg[1]." DROP CONSTRAINT ".$reg[2];
304  }
305 
306  // Translate order to add foreign keys
307  // ALTER TABLE llx_tablechild ADD CONSTRAINT fk_tablechild_fk_fieldparent FOREIGN KEY (fk_fieldparent) REFERENCES llx_tableparent (rowid)
308  if (preg_match('/ALTER\s+TABLE\s+(.*)\s*ADD CONSTRAINT\s+(.*)\s*FOREIGN\s+KEY\s*(.*)$/i', $line, $reg)) {
309  $line = preg_replace('/;$/', '', $line);
310  $line .= " DEFERRABLE INITIALLY IMMEDIATE;";
311  }
312 
313  // alter table add [unique] [index] (field1, field2 ...)
314  // ALTER TABLE llx_accountingaccount ADD INDEX idx_accountingaccount_fk_pcg_version (fk_pcg_version)
315  if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+(UNIQUE INDEX|INDEX|UNIQUE)\s+(.*)\s*\‍(([\w,\s]+)\‍)/i', $line, $reg)) {
316  $fieldlist = $reg[4];
317  $idxname = $reg[3];
318  $tablename = $reg[1];
319  $line = "-- ".$line." replaced by --\n";
320  $line .= "CREATE ".(preg_match('/UNIQUE/', $reg[2]) ? 'UNIQUE ' : '')."INDEX ".$idxname." ON ".$tablename." (".$fieldlist.")";
321  }
322  }
323 
324  // To have PostgreSQL case sensitive
325  $count_like = 0;
326  $line = str_replace(' LIKE \'', ' ILIKE \'', $line, $count_like);
327  if (getDolGlobalString('PSQL_USE_UNACCENT') && $count_like > 0) {
328  // @see https://docs.PostgreSQL.fr/11/unaccent.html : 'unaccent()' function must be installed before
329  $line = preg_replace('/\s+(\‍(+\s*)([a-zA-Z0-9\-\_\.]+) ILIKE /', ' \1unaccent(\2) ILIKE ', $line);
330  }
331 
332  $line = str_replace(' LIKE BINARY \'', ' LIKE \'', $line);
333 
334  // Replace INSERT IGNORE into INSERT
335  $line = preg_replace('/^INSERT IGNORE/', 'INSERT', $line);
336 
337  // Delete using criteria on other table must not declare twice the deleted table
338  // DELETE FROM tabletodelete USING tabletodelete, othertable -> DELETE FROM tabletodelete USING othertable
339  if (preg_match('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', $line, $reg)) {
340  if ($reg[1] == $reg[2]) { // If same table, we remove second one
341  $line = preg_replace('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', 'DELETE FROM \\1 USING \\3', $line);
342  }
343  }
344 
345  // Remove () in the tables in FROM if 1 table
346  $line = preg_replace('/FROM\s*\‍((([a-z_]+)\s+as\s+([a-z_]+)\s*)\‍)/i', 'FROM \\1', $line);
347  //print $line."\n";
348 
349  // Remove () in the tables in FROM if 2 table
350  $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);
351  //print $line."\n";
352 
353  // Remove () in the tables in FROM if 3 table
354  $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);
355  //print $line."\n";
356 
357  // Remove () in the tables in FROM if 4 table
358  $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);
359  //print $line."\n";
360 
361  // Remove () in the tables in FROM if 5 table
362  $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);
363  //print $line."\n";
364 
365  // Replace spacing ' with ''.
366  // By default we do not (should be already done by db->escape function if required
367  // except for sql insert in data file that are mysql escaped so we removed them to
368  // be compatible with standard_conforming_strings=on that considers \ as ordinary character).
369  if ($unescapeslashquot) {
370  $line = preg_replace("/\\\'/", "''", $line);
371  }
372 
373  //print "type=".$type." newline=".$line."<br>\n";
374  }
375 
376  return $line;
377  }
378 
379  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
388  public function select_db($database)
389  {
390  // phpcs:enable
391  if ($database == $this->database_name) {
392  return true;
393  } else {
394  return false;
395  }
396  }
397 
409  public function connect($host, $login, $passwd, $name, $port = 0)
410  {
411  // use pg_pconnect() instead of pg_connect() if you want to use persistent connection costing 1ms, instead of 30ms for non persistent
412 
413  $this->db = false;
414 
415  // connections parameters must be protected (only \ and ' according to pg_connect() manual)
416  $host = str_replace(array("\\", "'"), array("\\\\", "\\'"), $host);
417  $login = str_replace(array("\\", "'"), array("\\\\", "\\'"), $login);
418  $passwd = str_replace(array("\\", "'"), array("\\\\", "\\'"), $passwd);
419  $name = str_replace(array("\\", "'"), array("\\\\", "\\'"), $name);
420  $port = str_replace(array("\\", "'"), array("\\\\", "\\'"), (string) $port);
421 
422  if (!$name) {
423  $name = "postgres"; // When try to connect using admin user
424  }
425 
426  // try first Unix domain socket (local)
427  if ((!empty($host) && $host == "socket") && !defined('NOLOCALSOCKETPGCONNECT')) {
428  $con_string = "dbname='".$name."' user='".$login."' password='".$passwd."'"; // $name may be empty
429  try {
430  $this->db = @pg_connect($con_string);
431  } catch (Exception $e) {
432  // No message
433  }
434  }
435 
436  // if local connection failed or not requested, use TCP/IP
437  if (empty($this->db)) {
438  if (!$host) {
439  $host = "localhost";
440  }
441  if (!$port) {
442  $port = 5432;
443  }
444 
445  $con_string = "host='".$host."' port='".$port."' dbname='".$name."' user='".$login."' password='".$passwd."'";
446  try {
447  $this->db = @pg_connect($con_string);
448  } catch (Exception $e) {
449  print $e->getMessage();
450  }
451  }
452 
453  // now we test if at least one connect method was a success
454  if ($this->db) {
455  $this->database_name = $name;
456  pg_set_error_verbosity($this->db, PGSQL_ERRORS_VERBOSE); // Set verbosity to max
457  pg_query($this->db, "set datestyle = 'ISO, YMD';");
458  }
459 
460  return $this->db;
461  }
462 
468  public function getVersion()
469  {
470  $resql = $this->query('SHOW server_version');
471  if ($resql) {
472  $liste = $this->fetch_array($resql);
473  return $liste['server_version'];
474  }
475  return '';
476  }
477 
483  public function getDriverInfo()
484  {
485  return 'pgsql php driver';
486  }
487 
494  public function close()
495  {
496  if ($this->db) {
497  if ($this->transaction_opened > 0) {
498  dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened, LOG_ERR);
499  }
500  $this->connected = false;
501  return pg_close($this->db);
502  }
503  return false;
504  }
505 
515  public function query($query, $usesavepoint = 0, $type = 'auto', $result_mode = 0)
516  {
517  global $dolibarr_main_db_readonly;
518 
519  $query = trim($query);
520 
521  // Convert MySQL syntax to PostgreSQL syntax
522  $query = $this->convertSQLFromMysql($query, $type, ($this->unescapeslashquot && $this->standard_conforming_strings));
523  //print "After convertSQLFromMysql:\n".$query."<br>\n";
524 
525  if (getDolGlobalString('MAIN_DB_AUTOFIX_BAD_SQL_REQUEST')) {
526  // Fix bad formed requests. If request contains a date without quotes, we fix this but this should not occurs.
527  $loop = true;
528  while ($loop) {
529  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)) {
530  $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);
531  dol_syslog("Warning: Bad formed request converted into ".$query, LOG_WARNING);
532  } else {
533  $loop = false;
534  }
535  }
536  }
537 
538  if ($usesavepoint && $this->transaction_opened) {
539  @pg_query($this->db, 'SAVEPOINT mysavepoint');
540  }
541 
542  if (!in_array($query, array('BEGIN', 'COMMIT', 'ROLLBACK'))) {
543  $SYSLOG_SQL_LIMIT = 10000; // limit log to 10kb per line to limit DOS attacks
544  dol_syslog('sql='.substr($query, 0, $SYSLOG_SQL_LIMIT), LOG_DEBUG);
545  }
546  if (empty($query)) {
547  return false; // Return false = error if empty request
548  }
549 
550  if (!empty($dolibarr_main_db_readonly)) {
551  if (preg_match('/^(INSERT|UPDATE|REPLACE|DELETE|CREATE|ALTER|TRUNCATE|DROP)/i', $query)) {
552  $this->lasterror = 'Application in read-only mode';
553  $this->lasterrno = 'APPREADONLY';
554  $this->lastquery = $query;
555  return false;
556  }
557  }
558 
559  $ret = @pg_query($this->db, $query);
560 
561  //print $query;
562  if (!preg_match("/^COMMIT/i", $query) && !preg_match("/^ROLLBACK/i", $query)) { // Si requete utilisateur, on la sauvegarde ainsi que son resultset
563  if (!$ret) {
564  if ($this->errno() != 'DB_ERROR_25P02') { // Do not overwrite errors if this is a consecutive error
565  $this->lastqueryerror = $query;
566  $this->lasterror = $this->error();
567  $this->lasterrno = $this->errno();
568 
569  if (getDolGlobalInt('SYSLOG_LEVEL') < LOG_DEBUG) {
570  dol_syslog(get_class($this)."::query SQL Error query: ".$query, LOG_ERR); // Log of request was not yet done previously
571  }
572  dol_syslog(get_class($this)."::query SQL Error message: ".$this->lasterror." (".$this->lasterrno.")", LOG_ERR);
573  dol_syslog(get_class($this)."::query SQL Error usesavepoint = ".$usesavepoint, LOG_ERR);
574  }
575 
576  if ($usesavepoint && $this->transaction_opened) { // Warning, after that errno will be erased
577  @pg_query($this->db, 'ROLLBACK TO SAVEPOINT mysavepoint');
578  }
579  }
580  $this->lastquery = $query;
581  $this->_results = $ret;
582  }
583 
584  return $ret;
585  }
586 
587  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
594  public function fetch_object($resultset)
595  {
596  // phpcs:enable
597  // If resultset not provided, we take the last used by connection
598  if (!is_resource($resultset) && !is_object($resultset)) {
599  $resultset = $this->_results;
600  }
601  return pg_fetch_object($resultset);
602  }
603 
604  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
611  public function fetch_array($resultset)
612  {
613  // phpcs:enable
614  // If resultset not provided, we take the last used by connection
615  if (!is_resource($resultset) && !is_object($resultset)) {
616  $resultset = $this->_results;
617  }
618  return pg_fetch_array($resultset);
619  }
620 
621  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
628  public function fetch_row($resultset)
629  {
630  // phpcs:enable
631  // Si le resultset n'est pas fourni, on prend le dernier utilise sur cette connection
632  if (!is_resource($resultset) && !is_object($resultset)) {
633  $resultset = $this->_results;
634  }
635  return pg_fetch_row($resultset);
636  }
637 
638  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
646  public function num_rows($resultset)
647  {
648  // phpcs:enable
649  // If resultset not provided, we take the last used by connection
650  if (!is_resource($resultset) && !is_object($resultset)) {
651  $resultset = $this->_results;
652  }
653  return pg_num_rows($resultset);
654  }
655 
656  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
664  public function affected_rows($resultset)
665  {
666  // phpcs:enable
667  // If resultset not provided, we take the last used by connection
668  if (!is_resource($resultset) && !is_object($resultset)) {
669  $resultset = $this->_results;
670  }
671  // pgsql necessite un resultset pour cette fonction contrairement
672  // a mysql qui prend un link de base
673  return pg_affected_rows($resultset);
674  }
675 
676 
683  public function free($resultset = null)
684  {
685  // If resultset not provided, we take the last used by connection
686  if (!is_resource($resultset) && !is_object($resultset)) {
687  $resultset = $this->_results;
688  }
689  // Si resultset en est un, on libere la memoire
690  if (is_resource($resultset) || is_object($resultset)) {
691  pg_free_result($resultset);
692  }
693  }
694 
695 
703  public function plimit($limit = 0, $offset = 0)
704  {
705  global $conf;
706  if (empty($limit)) {
707  return "";
708  }
709  if ($limit < 0) {
710  $limit = $conf->liste_limit;
711  }
712  if ($offset > 0) {
713  return " LIMIT ".$limit." OFFSET ".$offset." ";
714  } else {
715  return " LIMIT $limit ";
716  }
717  }
718 
719 
726  public function escape($stringtoencode)
727  {
728  return pg_escape_string($this->db, $stringtoencode);
729  }
730 
737  public function escapeforlike($stringtoencode)
738  {
739  return str_replace(array('\\', '_', '%'), array('\\\\', '\_', '\%'), (string) $stringtoencode);
740  }
741 
750  public function ifsql($test, $resok, $resko)
751  {
752  return '(CASE WHEN '.$test.' THEN '.$resok.' ELSE '.$resko.' END)';
753  }
754 
763  public function regexpsql($subject, $pattern, $sqlstring = 0)
764  {
765  if ($sqlstring) {
766  return "(". $subject ." ~ '" . $this->escape($pattern) . "')";
767  }
768 
769  return "('". $this->escape($subject) ."' ~ '" . $this->escape($pattern) . "')";
770  }
771 
772 
778  public function errno()
779  {
780  if (!$this->connected) {
781  // Si il y a eu echec de connection, $this->db n'est pas valide.
782  return 'DB_ERROR_FAILED_TO_CONNECT';
783  } else {
784  // Constants to convert error code to a generic Dolibarr error code
785  $errorcode_map = array(
786  1004 => 'DB_ERROR_CANNOT_CREATE',
787  1005 => 'DB_ERROR_CANNOT_CREATE',
788  1006 => 'DB_ERROR_CANNOT_CREATE',
789  1007 => 'DB_ERROR_ALREADY_EXISTS',
790  1008 => 'DB_ERROR_CANNOT_DROP',
791  1025 => 'DB_ERROR_NO_FOREIGN_KEY_TO_DROP',
792  1044 => 'DB_ERROR_ACCESSDENIED',
793  1046 => 'DB_ERROR_NODBSELECTED',
794  1048 => 'DB_ERROR_CONSTRAINT',
795  '42P07' => 'DB_ERROR_TABLE_OR_KEY_ALREADY_EXISTS',
796  '42703' => 'DB_ERROR_NOSUCHFIELD',
797  1060 => 'DB_ERROR_COLUMN_ALREADY_EXISTS',
798  42701 => 'DB_ERROR_COLUMN_ALREADY_EXISTS',
799  '42710' => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
800  '23505' => 'DB_ERROR_RECORD_ALREADY_EXISTS',
801  '42704' => 'DB_ERROR_NO_INDEX_TO_DROP', // May also be Type xxx does not exists
802  '42601' => 'DB_ERROR_SYNTAX',
803  '42P16' => 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS',
804  1075 => 'DB_ERROR_CANT_DROP_PRIMARY_KEY',
805  1091 => 'DB_ERROR_NOSUCHFIELD',
806  1100 => 'DB_ERROR_NOT_LOCKED',
807  1136 => 'DB_ERROR_VALUE_COUNT_ON_ROW',
808  '42P01' => 'DB_ERROR_NOSUCHTABLE',
809  '23503' => 'DB_ERROR_NO_PARENT',
810  1217 => 'DB_ERROR_CHILD_EXISTS',
811  1451 => 'DB_ERROR_CHILD_EXISTS',
812  '42P04' => 'DB_DATABASE_ALREADY_EXISTS'
813  );
814 
815  $errorlabel = pg_last_error($this->db);
816  $errorcode = '';
817  $reg = array();
818  if (preg_match('/: *([0-9P]+):/', $errorlabel, $reg)) {
819  $errorcode = $reg[1];
820  if (isset($errorcode_map[$errorcode])) {
821  return $errorcode_map[$errorcode];
822  }
823  }
824  $errno = $errorcode ? $errorcode : $errorlabel;
825  return ($errno ? 'DB_ERROR_'.$errno : '0');
826  }
827  // '/(Table does not exist\.|Relation [\"\'].*[\"\'] does not exist|sequence does not exist|class ".+" not found)$/' => 'DB_ERROR_NOSUCHTABLE',
828  // '/table [\"\'].*[\"\'] does not exist/' => 'DB_ERROR_NOSUCHTABLE',
829  // '/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*/' => 'DB_ERROR_RECORD_ALREADY_EXISTS',
830  // '/divide by zero$/' => 'DB_ERROR_DIVZERO',
831  // '/pg_atoi: error in .*: can\'t parse /' => 'DB_ERROR_INVALID_NUMBER',
832  // '/ttribute [\"\'].*[\"\'] not found$|Relation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => 'DB_ERROR_NOSUCHFIELD',
833  // '/parser: parse error at or near \"/' => 'DB_ERROR_SYNTAX',
834  // '/referential integrity violation/' => 'DB_ERROR_CONSTRAINT'
835  }
836 
842  public function error()
843  {
844  return pg_last_error($this->db);
845  }
846 
847  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
855  public function last_insert_id($tab, $fieldid = 'rowid')
856  {
857  // phpcs:enable
858  //$result = pg_query($this->db,"SELECT MAX(".$fieldid.") FROM ".$tab);
859  $result = pg_query($this->db, "SELECT currval('".$tab."_".$fieldid."_seq')");
860  if (!$result) {
861  print pg_last_error($this->db);
862  exit;
863  }
864  //$nbre = pg_num_rows($result);
865  $row = pg_fetch_result($result, 0, 0);
866  return (int) $row;
867  }
868 
877  public function encrypt($fieldorvalue, $withQuotes = 1)
878  {
879  global $conf;
880 
881  // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
882  //$cryptType = ($conf->db->dolibarr_main_db_encryption ? $conf->db->dolibarr_main_db_encryption : 0);
883 
884  //Encryption key
885  //$cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : '');
886 
887  $return = $fieldorvalue;
888  return ($withQuotes ? "'" : "").$this->escape($return).($withQuotes ? "'" : "");
889  }
890 
891 
898  public function decrypt($value)
899  {
900  global $conf;
901 
902  // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
903  //$cryptType = ($conf->db->dolibarr_main_db_encryption ? $conf->db->dolibarr_main_db_encryption : 0);
904 
905  //Encryption key
906  //$cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : '');
907 
908  $return = $value;
909  return $return;
910  }
911 
912 
913  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
919  public function DDLGetConnectId()
920  {
921  // phpcs:enable
922  return '?';
923  }
924 
925 
926 
927  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
939  public function DDLCreateDb($database, $charset = '', $collation = '', $owner = '')
940  {
941  // phpcs:enable
942  if (empty($charset)) {
943  $charset = $this->forcecharset;
944  }
945  if (empty($collation)) {
946  $collation = $this->forcecollate;
947  }
948 
949  // Test charset match LC_TYPE (pgsql error otherwise)
950  //print $charset.' '.setlocale(LC_CTYPE,'0'); exit;
951 
952  // NOTE: Do not use ' around the database name
953  $sql = "CREATE DATABASE ".$this->escape($database)." OWNER '".$this->escape($owner)."' ENCODING '".$this->escape($charset)."'";
954 
955  dol_syslog($sql, LOG_DEBUG);
956  $ret = $this->query($sql);
957 
958  return $ret;
959  }
960 
961  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
969  public function DDLListTables($database, $table = '')
970  {
971  // phpcs:enable
972  $listtables = array();
973 
974  $escapedlike = '';
975  if ($table) {
976  $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table);
977 
978  $escapedlike = " AND table_name LIKE '".$this->escape($tmptable)."'";
979  }
980  $result = pg_query($this->db, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'".$escapedlike." ORDER BY table_name");
981  if ($result) {
982  while ($row = $this->fetch_row($result)) {
983  $listtables[] = $row[0];
984  }
985  }
986  return $listtables;
987  }
988 
989  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
997  public function DDLListTablesFull($database, $table = '')
998  {
999  // phpcs:enable
1000  $listtables = array();
1001 
1002  $escapedlike = '';
1003  if ($table) {
1004  $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table);
1005 
1006  $escapedlike = " AND table_name LIKE '".$this->escape($tmptable)."'";
1007  }
1008  $result = pg_query($this->db, "SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = 'public'".$escapedlike." ORDER BY table_name");
1009  if ($result) {
1010  while ($row = $this->fetch_row($result)) {
1011  $listtables[] = $row;
1012  }
1013  }
1014  return $listtables;
1015  }
1016 
1017  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1025  public function DDLInfoTable($table)
1026  {
1027  // phpcs:enable
1028  $infotables = array();
1029 
1030  $sql = "SELECT ";
1031  $sql .= " infcol.column_name as \"Column\",";
1032  $sql .= " CASE WHEN infcol.character_maximum_length IS NOT NULL THEN infcol.udt_name || '('||infcol.character_maximum_length||')'";
1033  $sql .= " ELSE infcol.udt_name";
1034  $sql .= " END as \"Type\",";
1035  $sql .= " infcol.collation_name as \"Collation\",";
1036  $sql .= " infcol.is_nullable as \"Null\",";
1037  $sql .= " '' as \"Key\",";
1038  $sql .= " infcol.column_default as \"Default\",";
1039  $sql .= " '' as \"Extra\",";
1040  $sql .= " '' as \"Privileges\"";
1041  $sql .= " FROM information_schema.columns infcol";
1042  $sql .= " WHERE table_schema = 'public' ";
1043  $sql .= " AND table_name = '".$this->escape($table)."'";
1044  $sql .= " ORDER BY ordinal_position;";
1045 
1046  dol_syslog($sql, LOG_DEBUG);
1047  $result = $this->query($sql);
1048  if ($result) {
1049  while ($row = $this->fetch_row($result)) {
1050  $infotables[] = $row;
1051  }
1052  }
1053  return $infotables;
1054  }
1055 
1056 
1057  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1070  public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null)
1071  {
1072  // phpcs:enable
1073  // @TODO: $fulltext_keys parameter is unused
1074 
1075  $sqlk = array();
1076  $sqluq = array();
1077 
1078  // Keys found into the array $fields: type,value,attribute,null,default,extra
1079  // ex. : $fields['rowid'] = array(
1080  // 'type'=>'int' or 'integer',
1081  // 'value'=>'11',
1082  // 'null'=>'not null',
1083  // 'extra'=> 'auto_increment'
1084  // );
1085  $sql = "CREATE TABLE ".$this->sanitize($table)."(";
1086  $i = 0;
1087  $sqlfields = array();
1088  foreach ($fields as $field_name => $field_desc) {
1089  $sqlfields[$i] = $this->sanitize($field_name)." ";
1090  $sqlfields[$i] .= $this->sanitize($field_desc['type']);
1091  if (isset($field_desc['value']) && $field_desc['value'] !== '') {
1092  $sqlfields[$i] .= "(".$this->sanitize($field_desc['value']).")";
1093  }
1094  if (isset($field_desc['attribute']) && $field_desc['attribute'] !== '') {
1095  $sqlfields[$i] .= " ".$this->sanitize($field_desc['attribute']);
1096  }
1097  if (isset($field_desc['default']) && $field_desc['default'] !== '') {
1098  if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1099  $sqlfields[$i] .= " DEFAULT ".((float) $field_desc['default']);
1100  } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') {
1101  $sqlfields[$i] .= " DEFAULT ".$this->sanitize($field_desc['default']);
1102  } else {
1103  $sqlfields[$i] .= " DEFAULT '".$this->escape($field_desc['default'])."'";
1104  }
1105  }
1106  if (isset($field_desc['null']) && $field_desc['null'] !== '') {
1107  $sqlfields[$i] .= " ".$this->sanitize($field_desc['null'], 0, 0, 1);
1108  }
1109  if (isset($field_desc['extra']) && $field_desc['extra'] !== '') {
1110  $sqlfields[$i] .= " ".$this->sanitize($field_desc['extra'], 0, 0, 1);
1111  }
1112  if (!empty($primary_key) && $primary_key == $field_name) {
1113  $sqlfields[$i] .= " AUTO_INCREMENT PRIMARY KEY"; // mysql instruction that will be converted by driver late
1114  }
1115  $i++;
1116  }
1117 
1118  if (is_array($unique_keys)) {
1119  $i = 0;
1120  foreach ($unique_keys as $key => $value) {
1121  $sqluq[$i] = "UNIQUE KEY '".$this->sanitize($key)."' ('".$this->escape($value)."')";
1122  $i++;
1123  }
1124  }
1125  if (is_array($keys)) {
1126  $i = 0;
1127  foreach ($keys as $key => $value) {
1128  $sqlk[$i] = "KEY ".$this->sanitize($key)." (".$value.")";
1129  $i++;
1130  }
1131  }
1132  $sql .= implode(', ', $sqlfields);
1133  if ($unique_keys != "") {
1134  $sql .= ",".implode(',', $sqluq);
1135  }
1136  if (is_array($keys)) {
1137  $sql .= ",".implode(',', $sqlk);
1138  }
1139  $sql .= ")";
1140  //$sql .= " engine=".$this->sanitize($type);
1141 
1142  if (!$this->query($sql, 1)) {
1143  return -1;
1144  } else {
1145  return 1;
1146  }
1147  }
1148 
1149  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1156  public function DDLDropTable($table)
1157  {
1158  // phpcs:enable
1159  $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table);
1160 
1161  $sql = "DROP TABLE ".$this->sanitize($tmptable);
1162 
1163  if (!$this->query($sql, 1)) {
1164  return -1;
1165  } else {
1166  return 1;
1167  }
1168  }
1169 
1170  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1178  public function DDLDescTable($table, $field = "")
1179  {
1180  // phpcs:enable
1181  $sql = "SELECT attname FROM pg_attribute, pg_type WHERE typname = '".$this->escape($table)."' AND attrelid = typrelid";
1182  $sql .= " AND attname NOT IN ('cmin', 'cmax', 'ctid', 'oid', 'tableoid', 'xmin', 'xmax')";
1183  if ($field) {
1184  $sql .= " AND attname = '".$this->escape($field)."'";
1185  }
1186 
1187  dol_syslog($sql, LOG_DEBUG);
1188  $this->_results = $this->query($sql);
1189  return $this->_results;
1190  }
1191 
1192  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1202  public function DDLAddField($table, $field_name, $field_desc, $field_position = "")
1203  {
1204  // phpcs:enable
1205  // cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra
1206  // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
1207  $sql = "ALTER TABLE ".$this->sanitize($table)." ADD ".$this->sanitize($field_name)." ";
1208  $sql .= $this->sanitize($field_desc['type']);
1209  if (isset($field_desc['value']) && preg_match("/^[^\s]/i", $field_desc['value'])) {
1210  if (!in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'date', 'datetime')) && $field_desc['value']) {
1211  $sql .= "(".$this->sanitize($field_desc['value']).")";
1212  }
1213  }
1214  if (isset($field_desc['attribute']) && preg_match("/^[^\s]/i", $field_desc['attribute'])) {
1215  $sql .= " ".$this->sanitize($field_desc['attribute']);
1216  }
1217  if (isset($field_desc['null']) && preg_match("/^[^\s]/i", $field_desc['null'])) {
1218  $sql .= " ".$field_desc['null'];
1219  }
1220  if (isset($field_desc['default']) && preg_match("/^[^\s]/i", $field_desc['default'])) {
1221  if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1222  $sql .= " DEFAULT ".((float) $field_desc['default']);
1223  } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') {
1224  $sql .= " DEFAULT ".$this->sanitize($field_desc['default']);
1225  } else {
1226  $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'";
1227  }
1228  }
1229  if (isset($field_desc['extra']) && preg_match("/^[^\s]/i", $field_desc['extra'])) {
1230  $sql .= " ".$this->sanitize($field_desc['extra'], 0, 0, 1);
1231  }
1232  $sql .= " ".$this->sanitize($field_position, 0, 0, 1);
1233 
1234  dol_syslog($sql, LOG_DEBUG);
1235  if (!$this -> query($sql)) {
1236  return -1;
1237  }
1238  return 1;
1239  }
1240 
1241  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1250  public function DDLUpdateField($table, $field_name, $field_desc)
1251  {
1252  // phpcs:enable
1253  $sql = "ALTER TABLE ".$this->sanitize($table);
1254  $sql .= " ALTER COLUMN ".$this->sanitize($field_name)." TYPE ".$this->sanitize($field_desc['type']);
1255  if (isset($field_desc['value']) && preg_match("/^[^\s]/i", $field_desc['value'])) {
1256  if (!in_array($field_desc['type'], array('smallint', 'int', 'date', 'datetime')) && $field_desc['value']) {
1257  $sql .= "(".$this->sanitize($field_desc['value']).")";
1258  }
1259  }
1260 
1261  if (isset($field_desc['value']) && ($field_desc['null'] == 'not null' || $field_desc['null'] == 'NOT NULL')) {
1262  // 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
1263  if ($field_desc['type'] == 'varchar' || $field_desc['type'] == 'text') {
1264  $sqlbis = "UPDATE ".$this->sanitize($table)." SET ".$this->escape($field_name)." = '".$this->escape(isset($field_desc['default']) ? $field_desc['default'] : '')."' WHERE ".$this->escape($field_name)." IS NULL";
1265  $this->query($sqlbis);
1266  } elseif (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1267  $sqlbis = "UPDATE ".$this->sanitize($table)." SET ".$this->escape($field_name)." = ".((float) $this->escape(isset($field_desc['default']) ? $field_desc['default'] : 0))." WHERE ".$this->escape($field_name)." IS NULL";
1268  $this->query($sqlbis);
1269  }
1270  }
1271 
1272  if (isset($field_desc['default']) && $field_desc['default'] != '') {
1273  if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1274  $sql .= ", ALTER COLUMN ".$this->sanitize($field_name)." SET DEFAULT ".((float) $field_desc['default']);
1275  } elseif ($field_desc['type'] != 'text') { // Default not supported on text fields ?
1276  $sql .= ", ALTER COLUMN ".$this->sanitize($field_name)." SET DEFAULT '".$this->escape($field_desc['default'])."'";
1277  }
1278  }
1279 
1280  dol_syslog($sql, LOG_DEBUG);
1281  if (!$this->query($sql)) {
1282  return -1;
1283  }
1284  return 1;
1285  }
1286 
1287  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1295  public function DDLDropField($table, $field_name)
1296  {
1297  // phpcs:enable
1298  $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name);
1299 
1300  $sql = "ALTER TABLE ".$this->sanitize($table)." DROP COLUMN ".$this->sanitize($tmp_field_name);
1301  if (!$this->query($sql)) {
1302  $this->error = $this->lasterror();
1303  return -1;
1304  }
1305  return 1;
1306  }
1307 
1308  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1318  public function DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name)
1319  {
1320  // phpcs:enable
1321  // Note: using ' on user does not works with pgsql
1322  $sql = "CREATE USER ".$this->sanitize($dolibarr_main_db_user)." with password '".$this->escape($dolibarr_main_db_pass)."'";
1323 
1324  dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log
1325  $resql = $this->query($sql);
1326  if (!$resql) {
1327  return -1;
1328  }
1329 
1330  return 1;
1331  }
1332 
1339  {
1340  $resql = $this->query('SHOW SERVER_ENCODING');
1341  if ($resql) {
1342  $liste = $this->fetch_array($resql);
1343  return $liste['server_encoding'];
1344  } else {
1345  return '';
1346  }
1347  }
1348 
1354  public function getListOfCharacterSet()
1355  {
1356  $resql = $this->query('SHOW SERVER_ENCODING');
1357  $liste = array();
1358  if ($resql) {
1359  $i = 0;
1360  while ($obj = $this->fetch_object($resql)) {
1361  $liste[$i]['charset'] = $obj->server_encoding;
1362  $liste[$i]['description'] = 'Default database charset';
1363  $i++;
1364  }
1365  $this->free($resql);
1366  } else {
1367  return null;
1368  }
1369  return $liste;
1370  }
1371 
1378  {
1379  $resql = $this->query('SHOW LC_COLLATE');
1380  if ($resql) {
1381  $liste = $this->fetch_array($resql);
1382  return $liste['lc_collate'];
1383  } else {
1384  return '';
1385  }
1386  }
1387 
1393  public function getListOfCollation()
1394  {
1395  $resql = $this->query('SHOW LC_COLLATE');
1396  $liste = array();
1397  if ($resql) {
1398  $i = 0;
1399  while ($obj = $this->fetch_object($resql)) {
1400  $liste[$i]['collation'] = $obj->lc_collate;
1401  $i++;
1402  }
1403  $this->free($resql);
1404  } else {
1405  return null;
1406  }
1407  return $liste;
1408  }
1409 
1415  public function getPathOfDump()
1416  {
1417  $fullpathofdump = '/pathtopgdump/pg_dump';
1418 
1419  if (file_exists('/usr/bin/pg_dump')) {
1420  $fullpathofdump = '/usr/bin/pg_dump';
1421  } else {
1422  // TODO L'utilisateur de la base doit etre un superadmin pour lancer cette commande
1423  $resql = $this->query('SHOW data_directory');
1424  if ($resql) {
1425  $liste = $this->fetch_array($resql);
1426  $basedir = $liste['data_directory'];
1427  $fullpathofdump = preg_replace('/data$/', 'bin', $basedir).'/pg_dump';
1428  }
1429  }
1430 
1431  return $fullpathofdump;
1432  }
1433 
1439  public function getPathOfRestore()
1440  {
1441  //$tool='pg_restore';
1442  $tool = 'psql';
1443 
1444  $fullpathofdump = '/pathtopgrestore/'.$tool;
1445 
1446  if (file_exists('/usr/bin/'.$tool)) {
1447  $fullpathofdump = '/usr/bin/'.$tool;
1448  } else {
1449  // TODO L'utilisateur de la base doit etre un superadmin pour lancer cette commande
1450  $resql = $this->query('SHOW data_directory');
1451  if ($resql) {
1452  $liste = $this->fetch_array($resql);
1453  $basedir = $liste['data_directory'];
1454  $fullpathofdump = preg_replace('/data$/', 'bin', $basedir).'/'.$tool;
1455  }
1456  }
1457 
1458  return $fullpathofdump;
1459  }
1460 
1467  public function getServerParametersValues($filter = '')
1468  {
1469  $result = array();
1470 
1471  $resql = 'select name,setting from pg_settings';
1472  if ($filter) {
1473  $resql .= " WHERE name = '".$this->escape($filter)."'";
1474  }
1475  $resql = $this->query($resql);
1476  if ($resql) {
1477  while ($obj = $this->fetch_object($resql)) {
1478  $result[$obj->name] = $obj->setting;
1479  }
1480  }
1481 
1482  return $result;
1483  }
1484 
1491  public function getServerStatusValues($filter = '')
1492  {
1493  /* This is to return current running requests.
1494  $sql='SELECT datname,procpid,current_query FROM pg_stat_activity ORDER BY procpid';
1495  if ($filter) $sql.=" LIKE '".$this->escape($filter)."'";
1496  $resql=$this->query($sql);
1497  if ($resql)
1498  {
1499  $obj=$this->fetch_object($resql);
1500  $result[$obj->Variable_name]=$obj->Value;
1501  }
1502  */
1503 
1504  return array();
1505  }
1506 }
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()
sanitize($stringtosanitize, $allowsimplequote=0, $allowsequals=0, $allowsspace=0)
Sanitize a string for SQL forging.
Class to drive a PostgreSQL database for Dolibarr.
Definition: pgsql.class.php:38
errno()
Renvoie le code erreur generique de l'operation precedente.
DDLListTablesFull($database, $table='')
List tables into a database.
DDLGetConnectId()
Return connection ID.
num_rows($resultset)
Return number of lines for result of a SELECT.
const VERSIONMIN
Version min database.
Definition: pgsql.class.php:52
__construct($type, $host, $user, $pass, $name='', $port=0)
Constructor.
Definition: pgsql.class.php:80
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 PostgreSQL does not have an equivalent for mysql_select_db Only compare if the chos...
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 connection.
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.
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:40
DDLInfoTable($table)
List information of columns into a table.
connect($host, $login, $passwd, $name, $port=0)
Connection to server.
getVersion()
Return version of database server.
$forcecharset
Charset.
Definition: pgsql.class.php:46
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.
regexpsql($subject, $pattern, $sqlstring=0)
Format a SQL REGEXP.
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:43
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 connection.
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.
convertSQLFromMysql($line, $type='auto', $unescapeslashquot=false)
Convert a SQL request in Mysql syntax to native syntax.
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:49
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('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:744
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
div float
Buy price without taxes.
Definition: style.css.php:959