| Total Complexity | 189 |
| Total Lines | 1342 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Complex classes like DoliDBPgsql often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use DoliDBPgsql, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 36 | class DoliDBPgsql extends DoliDB |
||
| 37 | { |
||
| 38 | //! Database type |
||
| 39 | public $type = 'pgsql'; // Name of manager |
||
| 40 | //! Database label |
||
| 41 | const LABEL = 'PostgreSQL'; // Label of manager |
||
| 42 | //! Charset |
||
| 43 | public $forcecharset = 'UTF8'; // Can't be static as it may be forced with a dynamic value |
||
| 44 | //! Collate used to force collate when creating database |
||
| 45 | public $forcecollate = ''; // Can't be static as it may be forced with a dynamic value |
||
| 46 | //! Version min database |
||
| 47 | const VERSIONMIN = '9.0.0'; // Version min database |
||
| 48 | /** @var resource Resultset of last query */ |
||
| 49 | private $_results; |
||
| 50 | |||
| 51 | public $unescapeslashquot; |
||
| 52 | public $standard_conforming_strings; |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Constructor. |
||
| 56 | * This create an opened connexion to a database server and eventually to a database |
||
| 57 | * |
||
| 58 | * @param string $type Type of database (mysql, pgsql...) |
||
| 59 | * @param string $host Address of database server |
||
| 60 | * @param string $user Nom de l'utilisateur autorise |
||
| 61 | * @param string $pass Mot de passe |
||
| 62 | * @param string $name Nom de la database |
||
| 63 | * @param int $port Port of database server |
||
| 64 | */ |
||
| 65 | public function __construct($type, $host, $user, $pass, $name = '', $port = 0) |
||
| 136 | } |
||
| 137 | |||
| 138 | |||
| 139 | /** |
||
| 140 | * Convert a SQL request in Mysql syntax to native syntax |
||
| 141 | * |
||
| 142 | * @param string $line SQL request line to convert |
||
| 143 | * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...) |
||
| 144 | * @param bool $unescapeslashquot Unescape slash quote with quote quote |
||
| 145 | * @return string SQL request line converted |
||
| 146 | */ |
||
| 147 | public static function convertSQLFromMysql($line, $type = 'auto', $unescapeslashquot = false) |
||
| 148 | { |
||
| 149 | global $conf; |
||
| 150 | |||
| 151 | // Removed empty line if this is a comment line for SVN tagging |
||
| 152 | if (preg_match('/^--\s\$Id/i', $line)) { |
||
| 153 | return ''; |
||
| 154 | } |
||
| 155 | // Return line if this is a comment |
||
| 156 | if (preg_match('/^#/i', $line) || preg_match('/^$/i', $line) || preg_match('/^--/i', $line)) |
||
| 157 | { |
||
| 158 | return $line; |
||
| 159 | } |
||
| 160 | if ($line != "") |
||
| 161 | { |
||
| 162 | // group_concat support (PgSQL >= 9.0) |
||
| 163 | // Replace group_concat(x) or group_concat(x SEPARATOR ',') with string_agg(x, ',') |
||
| 164 | $line = preg_replace('/GROUP_CONCAT/i', 'STRING_AGG', $line); |
||
| 165 | $line = preg_replace('/ SEPARATOR/i', ',', $line); |
||
| 166 | $line = preg_replace('/STRING_AGG\(([^,\)]+)\)/i', 'STRING_AGG(\\1, \',\')', $line); |
||
| 167 | //print $line."\n"; |
||
| 168 | |||
| 169 | if ($type == 'auto') |
||
| 170 | { |
||
| 171 | if (preg_match('/ALTER TABLE/i', $line)) $type = 'dml'; |
||
| 172 | elseif (preg_match('/CREATE TABLE/i', $line)) $type = 'dml'; |
||
| 173 | elseif (preg_match('/DROP TABLE/i', $line)) $type = 'dml'; |
||
| 174 | } |
||
| 175 | |||
| 176 | $line = preg_replace('/ as signed\)/i', ' as integer)', $line); |
||
| 177 | |||
| 178 | if ($type == 'dml') |
||
| 179 | { |
||
| 180 | $reg = array(); |
||
| 181 | |||
| 182 | $line = preg_replace('/\s/', ' ', $line); // Replace tabulation with space |
||
| 183 | |||
| 184 | // we are inside create table statement so lets process datatypes |
||
| 185 | if (preg_match('/(ISAM|innodb)/i', $line)) { // end of create table sequence |
||
| 186 | $line = preg_replace('/\)[\s\t]*type[\s\t]*=[\s\t]*(MyISAM|innodb).*;/i', ');', $line); |
||
| 187 | $line = preg_replace('/\)[\s\t]*engine[\s\t]*=[\s\t]*(MyISAM|innodb).*;/i', ');', $line); |
||
| 188 | $line = preg_replace('/,$/', '', $line); |
||
| 189 | } |
||
| 190 | |||
| 191 | // Process case: "CREATE TABLE llx_mytable(rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,code..." |
||
| 192 | if (preg_match('/[\s\t\(]*(\w*)[\s\t]+int.*auto_increment/i', $line, $reg)) { |
||
| 193 | $newline = preg_replace('/([\s\t\(]*)([a-zA-Z_0-9]*)[\s\t]+int.*auto_increment[^,]*/i', '\\1 \\2 SERIAL PRIMARY KEY', $line); |
||
| 194 | //$line = "-- ".$line." replaced by --\n".$newline; |
||
| 195 | $line = $newline; |
||
| 196 | } |
||
| 197 | |||
| 198 | if (preg_match('/[\s\t\(]*(\w*)[\s\t]+bigint.*auto_increment/i', $line, $reg)) { |
||
| 199 | $newline = preg_replace('/([\s\t\(]*)([a-zA-Z_0-9]*)[\s\t]+bigint.*auto_increment[^,]*/i', '\\1 \\2 BIGSERIAL PRIMARY KEY', $line); |
||
| 200 | //$line = "-- ".$line." replaced by --\n".$newline; |
||
| 201 | $line = $newline; |
||
| 202 | } |
||
| 203 | |||
| 204 | // tinyint type conversion |
||
| 205 | $line = preg_replace('/tinyint\(?[0-9]*\)?/', 'smallint', $line); |
||
| 206 | $line = preg_replace('/tinyint/i', 'smallint', $line); |
||
| 207 | |||
| 208 | // nuke unsigned |
||
| 209 | $line = preg_replace('/(int\w+|smallint)\s+unsigned/i', '\\1', $line); |
||
| 210 | |||
| 211 | // blob -> text |
||
| 212 | $line = preg_replace('/\w*blob/i', 'text', $line); |
||
| 213 | |||
| 214 | // tinytext/mediumtext -> text |
||
| 215 | $line = preg_replace('/tinytext/i', 'text', $line); |
||
| 216 | $line = preg_replace('/mediumtext/i', 'text', $line); |
||
| 217 | $line = preg_replace('/longtext/i', 'text', $line); |
||
| 218 | |||
| 219 | $line = preg_replace('/text\([0-9]+\)/i', 'text', $line); |
||
| 220 | |||
| 221 | // change not null datetime field to null valid ones |
||
| 222 | // (to support remapping of "zero time" to null |
||
| 223 | $line = preg_replace('/datetime not null/i', 'datetime', $line); |
||
| 224 | $line = preg_replace('/datetime/i', 'timestamp', $line); |
||
| 225 | |||
| 226 | // double -> numeric |
||
| 227 | $line = preg_replace('/^double/i', 'numeric', $line); |
||
| 228 | $line = preg_replace('/(\s*)double/i', '\\1numeric', $line); |
||
| 229 | // float -> numeric |
||
| 230 | $line = preg_replace('/^float/i', 'numeric', $line); |
||
| 231 | $line = preg_replace('/(\s*)float/i', '\\1numeric', $line); |
||
| 232 | |||
| 233 | //Check tms timestamp field case (in Mysql this field is defautled to now and |
||
| 234 | // on update defaulted by now |
||
| 235 | $line = preg_replace('/(\s*)tms(\s*)timestamp/i', '\\1tms timestamp without time zone DEFAULT now() NOT NULL', $line); |
||
| 236 | |||
| 237 | // nuke DEFAULT CURRENT_TIMESTAMP |
||
| 238 | $line = preg_replace('/(\s*)DEFAULT(\s*)CURRENT_TIMESTAMP/i', '\\1', $line); |
||
| 239 | |||
| 240 | // nuke ON UPDATE CURRENT_TIMESTAMP |
||
| 241 | $line = preg_replace('/(\s*)ON(\s*)UPDATE(\s*)CURRENT_TIMESTAMP/i', '\\1', $line); |
||
| 242 | |||
| 243 | // unique index(field1,field2) |
||
| 244 | if (preg_match('/unique index\s*\((\w+\s*,\s*\w+)\)/i', $line)) |
||
| 245 | { |
||
| 246 | $line = preg_replace('/unique index\s*\((\w+\s*,\s*\w+)\)/i', 'UNIQUE\(\\1\)', $line); |
||
| 247 | } |
||
| 248 | |||
| 249 | // We remove end of requests "AFTER fieldxxx" |
||
| 250 | $line = preg_replace('/\sAFTER [a-z0-9_]+/i', '', $line); |
||
| 251 | |||
| 252 | // We remove start of requests "ALTER TABLE tablexxx" if this is a DROP INDEX |
||
| 253 | $line = preg_replace('/ALTER TABLE [a-z0-9_]+\s+DROP INDEX/i', 'DROP INDEX', $line); |
||
| 254 | |||
| 255 | // Translate order to rename fields |
||
| 256 | if (preg_match('/ALTER TABLE ([a-z0-9_]+)\s+CHANGE(?: COLUMN)? ([a-z0-9_]+) ([a-z0-9_]+)(.*)$/i', $line, $reg)) |
||
| 257 | { |
||
| 258 | $line = "-- ".$line." replaced by --\n"; |
||
| 259 | $line .= "ALTER TABLE ".$reg[1]." RENAME COLUMN ".$reg[2]." TO ".$reg[3]; |
||
| 260 | } |
||
| 261 | |||
| 262 | // Translate order to modify field format |
||
| 263 | if (preg_match('/ALTER TABLE ([a-z0-9_]+)\s+MODIFY(?: COLUMN)? ([a-z0-9_]+) (.*)$/i', $line, $reg)) |
||
| 264 | { |
||
| 265 | $line = "-- ".$line." replaced by --\n"; |
||
| 266 | $newreg3 = $reg[3]; |
||
| 267 | $newreg3 = preg_replace('/ DEFAULT NULL/i', '', $newreg3); |
||
| 268 | $newreg3 = preg_replace('/ NOT NULL/i', '', $newreg3); |
||
| 269 | $newreg3 = preg_replace('/ NULL/i', '', $newreg3); |
||
| 270 | $newreg3 = preg_replace('/ DEFAULT 0/i', '', $newreg3); |
||
| 271 | $newreg3 = preg_replace('/ DEFAULT \'?[0-9a-zA-Z_@]*\'?/i', '', $newreg3); |
||
| 272 | $line .= "ALTER TABLE ".$reg[1]." ALTER COLUMN ".$reg[2]." TYPE ".$newreg3; |
||
| 273 | // TODO Add alter to set default value or null/not null if there is this in $reg[3] |
||
| 274 | } |
||
| 275 | |||
| 276 | // alter table add primary key (field1, field2 ...) -> We remove the primary key name not accepted by PostGreSQL |
||
| 277 | // ALTER TABLE llx_dolibarr_modules ADD PRIMARY KEY pk_dolibarr_modules (numero, entity) |
||
| 278 | if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+PRIMARY\s+KEY\s*(.*)\s*\((.*)$/i', $line, $reg)) |
||
| 279 | { |
||
| 280 | $line = "-- ".$line." replaced by --\n"; |
||
| 281 | $line .= "ALTER TABLE ".$reg[1]." ADD PRIMARY KEY (".$reg[3]; |
||
| 282 | } |
||
| 283 | |||
| 284 | // Translate order to drop primary keys |
||
| 285 | // ALTER TABLE llx_dolibarr_modules DROP PRIMARY KEY pk_xxx |
||
| 286 | if (preg_match('/ALTER\s+TABLE\s*(.*)\s*DROP\s+PRIMARY\s+KEY\s*([^;]+)$/i', $line, $reg)) |
||
| 287 | { |
||
| 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 | { |
||
| 296 | $line = "-- ".$line." replaced by --\n"; |
||
| 297 | $line .= "ALTER TABLE ".$reg[1]." DROP CONSTRAINT ".$reg[2]; |
||
| 298 | } |
||
| 299 | |||
| 300 | // Translate order to add foreign keys |
||
| 301 | // ALTER TABLE llx_tablechild ADD CONSTRAINT fk_tablechild_fk_fieldparent FOREIGN KEY (fk_fieldparent) REFERENCES llx_tableparent (rowid) |
||
| 302 | if (preg_match('/ALTER\s+TABLE\s+(.*)\s*ADD CONSTRAINT\s+(.*)\s*FOREIGN\s+KEY\s*(.*)$/i', $line, $reg)) |
||
| 303 | { |
||
| 304 | $line = preg_replace('/;$/', '', $line); |
||
| 305 | $line .= " DEFERRABLE INITIALLY IMMEDIATE;"; |
||
| 306 | } |
||
| 307 | |||
| 308 | // alter table add [unique] [index] (field1, field2 ...) |
||
| 309 | // ALTER TABLE llx_accountingaccount ADD INDEX idx_accountingaccount_fk_pcg_version (fk_pcg_version) |
||
| 310 | if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+(UNIQUE INDEX|INDEX|UNIQUE)\s+(.*)\s*\(([\w,\s]+)\)/i', $line, $reg)) |
||
| 311 | { |
||
| 312 | $fieldlist = $reg[4]; |
||
| 313 | $idxname = $reg[3]; |
||
| 314 | $tablename = $reg[1]; |
||
| 315 | $line = "-- ".$line." replaced by --\n"; |
||
| 316 | $line .= "CREATE ".(preg_match('/UNIQUE/', $reg[2]) ? 'UNIQUE ' : '')."INDEX ".$idxname." ON ".$tablename." (".$fieldlist.")"; |
||
| 317 | } |
||
| 318 | } |
||
| 319 | |||
| 320 | // To have postgresql case sensitive |
||
| 321 | $count_like = 0; |
||
| 322 | $line = str_replace(' LIKE \'', ' ILIKE \'', $line, $count_like); |
||
| 323 | if (!empty($conf->global->PSQL_USE_UNACCENT) && $count_like > 0) |
||
| 324 | { |
||
| 325 | // @see https://docs.postgresql.fr/11/unaccent.html : 'unaccent()' function must be installed before |
||
| 326 | $line = preg_replace('/\s+(\(+\s*)([a-zA-Z0-9\-\_\.]+) ILIKE /', ' \1unaccent(\2) ILIKE ', $line); |
||
| 327 | } |
||
| 328 | |||
| 329 | $line = str_replace(' LIKE BINARY \'', ' LIKE \'', $line); |
||
| 330 | |||
| 331 | // Replace INSERT IGNORE into INSERT |
||
| 332 | $line = preg_replace('/^INSERT IGNORE/', 'INSERT', $line); |
||
| 333 | |||
| 334 | // Delete using criteria on other table must not declare twice the deleted table |
||
| 335 | // DELETE FROM tabletodelete USING tabletodelete, othertable -> DELETE FROM tabletodelete USING othertable |
||
| 336 | if (preg_match('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', $line, $reg)) |
||
| 337 | { |
||
| 338 | if ($reg[1] == $reg[2]) // If same table, we remove second one |
||
| 339 | { |
||
| 340 | $line = preg_replace('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', 'DELETE FROM \\1 USING \\3', $line); |
||
| 341 | } |
||
| 342 | } |
||
| 343 | |||
| 344 | // Remove () in the tables in FROM if 1 table |
||
| 345 | $line = preg_replace('/FROM\s*\((([a-z_]+)\s+as\s+([a-z_]+)\s*)\)/i', 'FROM \\1', $line); |
||
| 346 | //print $line."\n"; |
||
| 347 | |||
| 348 | // Remove () in the tables in FROM if 2 table |
||
| 349 | $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); |
||
| 350 | //print $line."\n"; |
||
| 351 | |||
| 352 | // Remove () in the tables in FROM if 3 table |
||
| 353 | $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); |
||
| 354 | //print $line."\n"; |
||
| 355 | |||
| 356 | // Remove () in the tables in FROM if 4 table |
||
| 357 | $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); |
||
| 358 | //print $line."\n"; |
||
| 359 | |||
| 360 | // Remove () in the tables in FROM if 5 table |
||
| 361 | $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); |
||
| 362 | //print $line."\n"; |
||
| 363 | |||
| 364 | // Replace espacing \' by ''. |
||
| 365 | // By default we do not (should be already done by db->escape function if required |
||
| 366 | // except for sql insert in data file that are mysql escaped so we removed them to |
||
| 367 | // be compatible with standard_conforming_strings=on that considers \ as ordinary character). |
||
| 368 | if ($unescapeslashquot) $line = preg_replace("/\\\'/", "''", $line); |
||
| 369 | |||
| 370 | //print "type=".$type." newline=".$line."<br>\n"; |
||
| 371 | } |
||
| 372 | |||
| 373 | return $line; |
||
| 374 | } |
||
| 375 | |||
| 376 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 377 | /** |
||
| 378 | * Select a database |
||
| 379 | * Ici postgresql n'a aucune fonction equivalente de mysql_select_db |
||
| 380 | * On compare juste manuellement si la database choisie est bien celle activee par la connexion |
||
| 381 | * |
||
| 382 | * @param string $database Name of database |
||
| 383 | * @return bool true if OK, false if KO |
||
| 384 | */ |
||
| 385 | public function select_db($database) |
||
| 386 | { |
||
| 387 | // phpcs:enable |
||
| 388 | if ($database == $this->database_name) { |
||
| 389 | return true; |
||
| 390 | } else { |
||
| 391 | return false; |
||
| 392 | } |
||
| 393 | } |
||
| 394 | |||
| 395 | /** |
||
| 396 | * Connexion to server |
||
| 397 | * |
||
| 398 | * @param string $host Database server host |
||
| 399 | * @param string $login Login |
||
| 400 | * @param string $passwd Password |
||
| 401 | * @param string $name Name of database (not used for mysql, used for pgsql) |
||
| 402 | * @param integer $port Port of database server |
||
| 403 | * @return bool|resource Database access handler |
||
| 404 | * @see close() |
||
| 405 | */ |
||
| 406 | public function connect($host, $login, $passwd, $name, $port = 0) |
||
| 407 | { |
||
| 408 | // use pg_pconnect() instead of pg_connect() if you want to use persistent connection costing 1ms, instead of 30ms for non persistent |
||
| 409 | |||
| 410 | $this->db = false; |
||
| 411 | |||
| 412 | // connections parameters must be protected (only \ and ' according to pg_connect() manual) |
||
| 413 | $host = str_replace(array("\\", "'"), array("\\\\", "\\'"), $host); |
||
| 414 | $login = str_replace(array("\\", "'"), array("\\\\", "\\'"), $login); |
||
| 415 | $passwd = str_replace(array("\\", "'"), array("\\\\", "\\'"), $passwd); |
||
| 416 | $name = str_replace(array("\\", "'"), array("\\\\", "\\'"), $name); |
||
| 417 | $port = str_replace(array("\\", "'"), array("\\\\", "\\'"), $port); |
||
| 418 | |||
| 419 | if (!$name) $name = "postgres"; // When try to connect using admin user |
||
| 420 | |||
| 421 | // try first Unix domain socket (local) |
||
| 422 | if ((!empty($host) && $host == "socket") && !defined('NOLOCALSOCKETPGCONNECT')) |
||
| 423 | { |
||
| 424 | $con_string = "dbname='".$name."' user='".$login."' password='".$passwd."'"; // $name may be empty |
||
| 425 | $this->db = @pg_connect($con_string); |
||
| 426 | } |
||
| 427 | |||
| 428 | // if local connection failed or not requested, use TCP/IP |
||
| 429 | if (!$this->db) |
||
| 430 | { |
||
| 431 | if (!$host) $host = "localhost"; |
||
| 432 | if (!$port) $port = 5432; |
||
| 433 | |||
| 434 | $con_string = "host='".$host."' port='".$port."' dbname='".$name."' user='".$login."' password='".$passwd."'"; |
||
| 435 | $this->db = @pg_connect($con_string); |
||
| 436 | } |
||
| 437 | |||
| 438 | // now we test if at least one connect method was a success |
||
| 439 | if ($this->db) |
||
| 440 | { |
||
| 441 | $this->database_name = $name; |
||
| 442 | pg_set_error_verbosity($this->db, PGSQL_ERRORS_VERBOSE); // Set verbosity to max |
||
| 443 | pg_query($this->db, "set datestyle = 'ISO, YMD';"); |
||
| 444 | } |
||
| 445 | |||
| 446 | return $this->db; |
||
| 447 | } |
||
| 448 | |||
| 449 | /** |
||
| 450 | * Return version of database server |
||
| 451 | * |
||
| 452 | * @return string Version string |
||
| 453 | */ |
||
| 454 | public function getVersion() |
||
| 455 | { |
||
| 456 | $resql = $this->query('SHOW server_version'); |
||
| 457 | if ($resql) |
||
| 458 | { |
||
| 459 | $liste = $this->fetch_array($resql); |
||
| 460 | return $liste['server_version']; |
||
| 461 | } |
||
| 462 | return ''; |
||
| 463 | } |
||
| 464 | |||
| 465 | /** |
||
| 466 | * Return version of database client driver |
||
| 467 | * |
||
| 468 | * @return string Version string |
||
| 469 | */ |
||
| 470 | public function getDriverInfo() |
||
| 471 | { |
||
| 472 | return 'pgsql php driver'; |
||
| 473 | } |
||
| 474 | |||
| 475 | /** |
||
| 476 | * Close database connexion |
||
| 477 | * |
||
| 478 | * @return boolean True if disconnect successfull, false otherwise |
||
| 479 | * @see connect() |
||
| 480 | */ |
||
| 481 | public function close() |
||
| 482 | { |
||
| 483 | if ($this->db) |
||
| 484 | { |
||
| 485 | if ($this->transaction_opened > 0) dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened, LOG_ERR); |
||
| 486 | $this->connected = false; |
||
| 487 | return pg_close($this->db); |
||
| 488 | } |
||
| 489 | return false; |
||
| 490 | } |
||
| 491 | |||
| 492 | /** |
||
| 493 | * Convert request to PostgreSQL syntax, execute it and return the resultset |
||
| 494 | * |
||
| 495 | * @param string $query SQL query string |
||
| 496 | * @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollback to savepoint if error (this allow to have some request with errors inside global transactions). |
||
| 497 | * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...) |
||
| 498 | * @return false|resource Resultset of answer |
||
| 499 | */ |
||
| 500 | public function query($query, $usesavepoint = 0, $type = 'auto') |
||
| 501 | { |
||
| 502 | global $conf; |
||
| 503 | |||
| 504 | $query = trim($query); |
||
| 505 | |||
| 506 | // Convert MySQL syntax to PostgresSQL syntax |
||
| 507 | $query = $this->convertSQLFromMysql($query, $type, ($this->unescapeslashquot && $this->standard_conforming_strings)); |
||
| 508 | //print "After convertSQLFromMysql:\n".$query."<br>\n"; |
||
| 509 | |||
| 510 | if (!empty($conf->global->MAIN_DB_AUTOFIX_BAD_SQL_REQUEST)) |
||
| 511 | { |
||
| 512 | // Fix bad formed requests. If request contains a date without quotes, we fix this but this should not occurs. |
||
| 513 | $loop = true; |
||
| 514 | while ($loop) |
||
| 515 | { |
||
| 516 | 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)) |
||
| 517 | { |
||
| 518 | $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); |
||
| 519 | dol_syslog("Warning: Bad formed request converted into ".$query, LOG_WARNING); |
||
| 520 | } else $loop = false; |
||
| 521 | } |
||
| 522 | } |
||
| 523 | |||
| 524 | if ($usesavepoint && $this->transaction_opened) |
||
| 525 | { |
||
| 526 | @pg_query($this->db, 'SAVEPOINT mysavepoint'); |
||
| 527 | } |
||
| 528 | |||
| 529 | if (!in_array($query, array('BEGIN', 'COMMIT', 'ROLLBACK'))) |
||
| 530 | { |
||
| 531 | $SYSLOG_SQL_LIMIT = 10000; // limit log to 10kb per line to limit DOS attacks |
||
| 532 | dol_syslog('sql='.substr($query, 0, $SYSLOG_SQL_LIMIT), LOG_DEBUG); |
||
| 533 | } |
||
| 534 | |||
| 535 | $ret = @pg_query($this->db, $query); |
||
| 536 | |||
| 537 | //print $query; |
||
| 538 | if (!preg_match("/^COMMIT/i", $query) && !preg_match("/^ROLLBACK/i", $query)) // Si requete utilisateur, on la sauvegarde ainsi que son resultset |
||
| 539 | { |
||
| 540 | if (!$ret) |
||
| 541 | { |
||
| 542 | if ($this->errno() != 'DB_ERROR_25P02') // Do not overwrite errors if this is a consecutive error |
||
| 543 | { |
||
| 544 | $this->lastqueryerror = $query; |
||
| 545 | $this->lasterror = $this->error(); |
||
| 546 | $this->lasterrno = $this->errno(); |
||
| 547 | |||
| 548 | if ($conf->global->SYSLOG_LEVEL < LOG_DEBUG) dol_syslog(get_class($this)."::query SQL Error query: ".$query, LOG_ERR); // Log of request was not yet done previously |
||
| 549 | dol_syslog(get_class($this)."::query SQL Error message: ".$this->lasterror." (".$this->lasterrno.")", LOG_ERR); |
||
| 550 | dol_syslog(get_class($this)."::query SQL Error usesavepoint = ".$usesavepoint, LOG_ERR); |
||
| 551 | } |
||
| 552 | |||
| 553 | if ($usesavepoint && $this->transaction_opened) // Warning, after that errno will be erased |
||
| 554 | { |
||
| 555 | @pg_query($this->db, 'ROLLBACK TO SAVEPOINT mysavepoint'); |
||
| 556 | } |
||
| 557 | } |
||
| 558 | $this->lastquery = $query; |
||
| 559 | $this->_results = $ret; |
||
| 560 | } |
||
| 561 | |||
| 562 | return $ret; |
||
| 563 | } |
||
| 564 | |||
| 565 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 566 | /** |
||
| 567 | * Renvoie la ligne courante (comme un objet) pour le curseur resultset |
||
| 568 | * |
||
| 569 | * @param resource $resultset Curseur de la requete voulue |
||
| 570 | * @return false|object Object result line or false if KO or end of cursor |
||
| 571 | */ |
||
| 572 | public function fetch_object($resultset) |
||
| 573 | { |
||
| 574 | // phpcs:enable |
||
| 575 | // If resultset not provided, we take the last used by connexion |
||
| 576 | if (!is_resource($resultset)) { $resultset = $this->_results; } |
||
| 577 | return pg_fetch_object($resultset); |
||
| 578 | } |
||
| 579 | |||
| 580 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 581 | /** |
||
| 582 | * Return datas as an array |
||
| 583 | * |
||
| 584 | * @param resource $resultset Resultset of request |
||
| 585 | * @return false|array Array |
||
| 586 | */ |
||
| 587 | public function fetch_array($resultset) |
||
| 588 | { |
||
| 589 | // phpcs:enable |
||
| 590 | // If resultset not provided, we take the last used by connexion |
||
| 591 | if (!is_resource($resultset)) { $resultset = $this->_results; } |
||
| 592 | return pg_fetch_array($resultset); |
||
| 593 | } |
||
| 594 | |||
| 595 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 596 | /** |
||
| 597 | * Return datas as an array |
||
| 598 | * |
||
| 599 | * @param resource $resultset Resultset of request |
||
| 600 | * @return false|array Array |
||
| 601 | */ |
||
| 602 | public function fetch_row($resultset) |
||
| 608 | } |
||
| 609 | |||
| 610 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 611 | /** |
||
| 612 | * Return number of lines for result of a SELECT |
||
| 613 | * |
||
| 614 | * @param resource $resultset Resulset of requests |
||
| 615 | * @return int Nb of lines, -1 on error |
||
| 616 | * @see affected_rows() |
||
| 617 | */ |
||
| 618 | public function num_rows($resultset) |
||
| 619 | { |
||
| 620 | // phpcs:enable |
||
| 621 | // If resultset not provided, we take the last used by connexion |
||
| 622 | if (!is_resource($resultset)) { $resultset = $this->_results; } |
||
| 623 | return pg_num_rows($resultset); |
||
| 624 | } |
||
| 625 | |||
| 626 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 627 | /** |
||
| 628 | * Return the number of lines in the result of a request INSERT, DELETE or UPDATE |
||
| 629 | * |
||
| 630 | * @param resource $resultset Result set of request |
||
| 631 | * @return int Nb of lines |
||
| 632 | * @see num_rows() |
||
| 633 | */ |
||
| 634 | public function affected_rows($resultset) |
||
| 642 | } |
||
| 643 | |||
| 644 | |||
| 645 | /** |
||
| 646 | * Libere le dernier resultset utilise sur cette connexion |
||
| 647 | * |
||
| 648 | * @param resource $resultset Result set of request |
||
| 649 | * @return void |
||
| 650 | */ |
||
| 651 | public function free($resultset = null) |
||
| 657 | } |
||
| 658 | |||
| 659 | |||
| 660 | /** |
||
| 661 | * Define limits and offset of request |
||
| 662 | * |
||
| 663 | * @param int $limit Maximum number of lines returned (-1=conf->liste_limit, 0=no limit) |
||
| 664 | * @param int $offset Numero of line from where starting fetch |
||
| 665 | * @return string String with SQL syntax to add a limit and offset |
||
| 666 | */ |
||
| 667 | public function plimit($limit = 0, $offset = 0) |
||
| 668 | { |
||
| 669 | global $conf; |
||
| 670 | if (empty($limit)) return ""; |
||
| 671 | if ($limit < 0) $limit = $conf->liste_limit; |
||
| 672 | if ($offset > 0) return " LIMIT ".$limit." OFFSET ".$offset." "; |
||
| 673 | else return " LIMIT $limit "; |
||
| 674 | } |
||
| 675 | |||
| 676 | |||
| 677 | /** |
||
| 678 | * Escape a string to insert data |
||
| 679 | * |
||
| 680 | * @param string $stringtoencode String to escape |
||
| 681 | * @return string String escaped |
||
| 682 | */ |
||
| 683 | public function escape($stringtoencode) |
||
| 686 | } |
||
| 687 | |||
| 688 | /** |
||
| 689 | * Format a SQL IF |
||
| 690 | * |
||
| 691 | * @param string $test Test string (example: 'cd.statut=0', 'field IS NULL') |
||
| 692 | * @param string $resok resultat si test egal |
||
| 693 | * @param string $resko resultat si test non egal |
||
| 694 | * @return string chaine formate SQL |
||
| 695 | */ |
||
| 696 | public function ifsql($test, $resok, $resko) |
||
| 697 | { |
||
| 698 | return '(CASE WHEN '.$test.' THEN '.$resok.' ELSE '.$resko.' END)'; |
||
| 699 | } |
||
| 700 | |||
| 701 | /** |
||
| 702 | * Renvoie le code erreur generique de l'operation precedente. |
||
| 703 | * |
||
| 704 | * @return string Error code (Exemples: DB_ERROR_TABLE_ALREADY_EXISTS, DB_ERROR_RECORD_ALREADY_EXISTS...) |
||
| 705 | */ |
||
| 706 | public function errno() |
||
| 707 | { |
||
| 708 | if (!$this->connected) { |
||
| 709 | // Si il y a eu echec de connexion, $this->db n'est pas valide. |
||
| 710 | return 'DB_ERROR_FAILED_TO_CONNECT'; |
||
| 711 | } else { |
||
| 712 | // Constants to convert error code to a generic Dolibarr error code |
||
| 713 | $errorcode_map = array( |
||
| 714 | 1004 => 'DB_ERROR_CANNOT_CREATE', |
||
| 715 | 1005 => 'DB_ERROR_CANNOT_CREATE', |
||
| 716 | 1006 => 'DB_ERROR_CANNOT_CREATE', |
||
| 717 | 1007 => 'DB_ERROR_ALREADY_EXISTS', |
||
| 718 | 1008 => 'DB_ERROR_CANNOT_DROP', |
||
| 719 | 1025 => 'DB_ERROR_NO_FOREIGN_KEY_TO_DROP', |
||
| 720 | 1044 => 'DB_ERROR_ACCESSDENIED', |
||
| 721 | 1046 => 'DB_ERROR_NODBSELECTED', |
||
| 722 | 1048 => 'DB_ERROR_CONSTRAINT', |
||
| 723 | '42P07' => 'DB_ERROR_TABLE_OR_KEY_ALREADY_EXISTS', |
||
| 724 | '42703' => 'DB_ERROR_NOSUCHFIELD', |
||
| 725 | 1060 => 'DB_ERROR_COLUMN_ALREADY_EXISTS', |
||
| 726 | 42701=> 'DB_ERROR_COLUMN_ALREADY_EXISTS', |
||
| 727 | '42710' => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS', |
||
| 728 | '23505' => 'DB_ERROR_RECORD_ALREADY_EXISTS', |
||
| 729 | '42704' => 'DB_ERROR_NO_INDEX_TO_DROP', // May also be Type xxx does not exists |
||
| 730 | '42601' => 'DB_ERROR_SYNTAX', |
||
| 731 | '42P16' => 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS', |
||
| 732 | 1075 => 'DB_ERROR_CANT_DROP_PRIMARY_KEY', |
||
| 733 | 1091 => 'DB_ERROR_NOSUCHFIELD', |
||
| 734 | 1100 => 'DB_ERROR_NOT_LOCKED', |
||
| 735 | 1136 => 'DB_ERROR_VALUE_COUNT_ON_ROW', |
||
| 736 | '42P01' => 'DB_ERROR_NOSUCHTABLE', |
||
| 737 | '23503' => 'DB_ERROR_NO_PARENT', |
||
| 738 | 1217 => 'DB_ERROR_CHILD_EXISTS', |
||
| 739 | 1451 => 'DB_ERROR_CHILD_EXISTS', |
||
| 740 | '42P04' => 'DB_DATABASE_ALREADY_EXISTS' |
||
| 741 | ); |
||
| 742 | |||
| 743 | $errorlabel = pg_last_error($this->db); |
||
| 744 | $errorcode = ''; |
||
| 745 | if (preg_match('/: *([0-9P]+):/', $errorlabel, $reg)) |
||
| 746 | { |
||
| 747 | $errorcode = $reg[1]; |
||
| 748 | if (isset($errorcode_map[$errorcode])) |
||
| 749 | { |
||
| 750 | return $errorcode_map[$errorcode]; |
||
| 751 | } |
||
| 752 | } |
||
| 753 | $errno = $errorcode ? $errorcode : $errorlabel; |
||
| 754 | return ($errno ? 'DB_ERROR_'.$errno : '0'); |
||
| 755 | } |
||
| 756 | // '/(Table does not exist\.|Relation [\"\'].*[\"\'] does not exist|sequence does not exist|class ".+" not found)$/' => 'DB_ERROR_NOSUCHTABLE', |
||
| 757 | // '/table [\"\'].*[\"\'] does not exist/' => 'DB_ERROR_NOSUCHTABLE', |
||
| 758 | // '/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*/' => 'DB_ERROR_RECORD_ALREADY_EXISTS', |
||
| 759 | // '/divide by zero$/' => 'DB_ERROR_DIVZERO', |
||
| 760 | // '/pg_atoi: error in .*: can\'t parse /' => 'DB_ERROR_INVALID_NUMBER', |
||
| 761 | // '/ttribute [\"\'].*[\"\'] not found$|Relation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => 'DB_ERROR_NOSUCHFIELD', |
||
| 762 | // '/parser: parse error at or near \"/' => 'DB_ERROR_SYNTAX', |
||
| 763 | // '/referential integrity violation/' => 'DB_ERROR_CONSTRAINT' |
||
| 764 | } |
||
| 765 | |||
| 766 | /** |
||
| 767 | * Renvoie le texte de l'erreur pgsql de l'operation precedente |
||
| 768 | * |
||
| 769 | * @return string Error text |
||
| 770 | */ |
||
| 771 | public function error() |
||
| 772 | { |
||
| 773 | return pg_last_error($this->db); |
||
| 774 | } |
||
| 775 | |||
| 776 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 777 | /** |
||
| 778 | * Get last ID after an insert INSERT |
||
| 779 | * |
||
| 780 | * @param string $tab Table name concerned by insert. Ne sert pas sous MySql mais requis pour compatibilite avec Postgresql |
||
| 781 | * @param string $fieldid Field name |
||
| 782 | * @return string Id of row |
||
| 783 | */ |
||
| 784 | public function last_insert_id($tab, $fieldid = 'rowid') |
||
| 785 | { |
||
| 786 | // phpcs:enable |
||
| 787 | //$result = pg_query($this->db,"SELECT MAX(".$fieldid.") FROM ".$tab); |
||
| 788 | $result = pg_query($this->db, "SELECT currval('".$tab."_".$fieldid."_seq')"); |
||
| 789 | if (!$result) |
||
| 790 | { |
||
| 791 | print pg_last_error($this->db); |
||
| 792 | exit; |
||
| 793 | } |
||
| 794 | //$nbre = pg_num_rows($result); |
||
| 795 | $row = pg_fetch_result($result, 0, 0); |
||
| 796 | return $row; |
||
| 797 | } |
||
| 798 | |||
| 799 | /** |
||
| 800 | * Encrypt sensitive data in database |
||
| 801 | * Warning: This function includes the escape, so it must use direct value |
||
| 802 | * |
||
| 803 | * @param string $fieldorvalue Field name or value to encrypt |
||
| 804 | * @param int $withQuotes Return string with quotes |
||
| 805 | * @return string XXX(field) or XXX('value') or field or 'value' |
||
| 806 | */ |
||
| 807 | public function encrypt($fieldorvalue, $withQuotes = 0) |
||
| 808 | { |
||
| 809 | global $conf; |
||
| 810 | |||
| 811 | // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption) |
||
| 812 | $cryptType = ($conf->db->dolibarr_main_db_encryption ? $conf->db->dolibarr_main_db_encryption : 0); |
||
| 813 | |||
| 814 | //Encryption key |
||
| 815 | $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : ''); |
||
| 816 | |||
| 817 | $return = $fieldorvalue; |
||
| 818 | return ($withQuotes ? "'" : "").$this->escape($return).($withQuotes ? "'" : ""); |
||
| 819 | } |
||
| 820 | |||
| 821 | |||
| 822 | /** |
||
| 823 | * Decrypt sensitive data in database |
||
| 824 | * |
||
| 825 | * @param int $value Value to decrypt |
||
| 826 | * @return string Decrypted value if used |
||
| 827 | */ |
||
| 828 | public function decrypt($value) |
||
| 829 | { |
||
| 830 | global $conf; |
||
| 831 | |||
| 832 | // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption) |
||
| 833 | $cryptType = ($conf->db->dolibarr_main_db_encryption ? $conf->db->dolibarr_main_db_encryption : 0); |
||
| 834 | |||
| 835 | //Encryption key |
||
| 836 | $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : ''); |
||
| 837 | |||
| 838 | $return = $value; |
||
| 839 | return $return; |
||
| 840 | } |
||
| 841 | |||
| 842 | |||
| 843 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 844 | /** |
||
| 845 | * Return connexion ID |
||
| 846 | * |
||
| 847 | * @return string Id connexion |
||
| 848 | */ |
||
| 849 | public function DDLGetConnectId() |
||
| 850 | { |
||
| 851 | // phpcs:enable |
||
| 852 | return '?'; |
||
| 853 | } |
||
| 854 | |||
| 855 | |||
| 856 | |||
| 857 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 858 | /** |
||
| 859 | * Create a new database |
||
| 860 | * Do not use function xxx_create_db (xxx=mysql, ...) as they are deprecated |
||
| 861 | * We force to create database with charset this->forcecharset and collate this->forcecollate |
||
| 862 | * |
||
| 863 | * @param string $database Database name to create |
||
| 864 | * @param string $charset Charset used to store data |
||
| 865 | * @param string $collation Charset used to sort data |
||
| 866 | * @param string $owner Username of database owner |
||
| 867 | * @return false|resource resource defined if OK, null if KO |
||
| 868 | */ |
||
| 869 | public function DDLCreateDb($database, $charset = '', $collation = '', $owner = '') |
||
| 870 | { |
||
| 871 | // phpcs:enable |
||
| 872 | if (empty($charset)) $charset = $this->forcecharset; |
||
| 873 | if (empty($collation)) $collation = $this->forcecollate; |
||
| 874 | |||
| 875 | // Test charset match LC_TYPE (pgsql error otherwise) |
||
| 876 | //print $charset.' '.setlocale(LC_CTYPE,'0'); exit; |
||
| 877 | |||
| 878 | $sql = 'CREATE DATABASE "'.$database.'" OWNER "'.$owner.'" ENCODING \''.$charset.'\''; |
||
| 879 | dol_syslog($sql, LOG_DEBUG); |
||
| 880 | $ret = $this->query($sql); |
||
| 881 | return $ret; |
||
| 882 | } |
||
| 883 | |||
| 884 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 885 | /** |
||
| 886 | * List tables into a database |
||
| 887 | * |
||
| 888 | * @param string $database Name of database |
||
| 889 | * @param string $table Name of table filter ('xxx%') |
||
| 890 | * @return array List of tables in an array |
||
| 891 | */ |
||
| 892 | public function DDLListTables($database, $table = '') |
||
| 893 | { |
||
| 894 | // phpcs:enable |
||
| 895 | $listtables = array(); |
||
| 896 | |||
| 897 | $like = ''; |
||
| 898 | if ($table) $like = " AND table_name LIKE '".$this->escape($table)."'"; |
||
| 899 | $result = pg_query($this->db, "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'".$like." ORDER BY table_name"); |
||
| 900 | if ($result) |
||
| 901 | { |
||
| 902 | while ($row = $this->fetch_row($result)) |
||
| 903 | { |
||
| 904 | $listtables[] = $row[0]; |
||
| 905 | } |
||
| 906 | } |
||
| 907 | return $listtables; |
||
| 908 | } |
||
| 909 | |||
| 910 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 911 | /** |
||
| 912 | * List information of columns into a table. |
||
| 913 | * |
||
| 914 | * @param string $table Name of table |
||
| 915 | * @return array Tableau des informations des champs de la table |
||
| 916 | * |
||
| 917 | */ |
||
| 918 | public function DDLInfoTable($table) |
||
| 919 | { |
||
| 920 | // phpcs:enable |
||
| 921 | $infotables = array(); |
||
| 922 | |||
| 923 | $sql = "SELECT "; |
||
| 924 | $sql .= " infcol.column_name as \"Column\","; |
||
| 925 | $sql .= " CASE WHEN infcol.character_maximum_length IS NOT NULL THEN infcol.udt_name || '('||infcol.character_maximum_length||')'"; |
||
| 926 | $sql .= " ELSE infcol.udt_name"; |
||
| 927 | $sql .= " END as \"Type\","; |
||
| 928 | $sql .= " infcol.collation_name as \"Collation\","; |
||
| 929 | $sql .= " infcol.is_nullable as \"Null\","; |
||
| 930 | $sql .= " '' as \"Key\","; |
||
| 931 | $sql .= " infcol.column_default as \"Default\","; |
||
| 932 | $sql .= " '' as \"Extra\","; |
||
| 933 | $sql .= " '' as \"Privileges\""; |
||
| 934 | $sql .= " FROM information_schema.columns infcol"; |
||
| 935 | $sql .= " WHERE table_schema='public' "; |
||
| 936 | $sql .= " AND table_name='".$this->escape($table)."'"; |
||
| 937 | $sql .= " ORDER BY ordinal_position;"; |
||
| 938 | |||
| 939 | dol_syslog($sql, LOG_DEBUG); |
||
| 940 | $result = $this->query($sql); |
||
| 941 | if ($result) |
||
| 942 | { |
||
| 943 | while ($row = $this->fetch_row($result)) |
||
| 944 | { |
||
| 945 | $infotables[] = $row; |
||
| 946 | } |
||
| 947 | } |
||
| 948 | return $infotables; |
||
| 949 | } |
||
| 950 | |||
| 951 | |||
| 952 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 953 | /** |
||
| 954 | * Create a table into database |
||
| 955 | * |
||
| 956 | * @param string $table Nom de la table |
||
| 957 | * @param array $fields Tableau associatif [nom champ][tableau des descriptions] |
||
| 958 | * @param string $primary_key Nom du champ qui sera la clef primaire |
||
| 959 | * @param string $type Type de la table |
||
| 960 | * @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur |
||
| 961 | * @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext |
||
| 962 | * @param array $keys Tableau des champs cles noms => valeur |
||
| 963 | * @return int <0 if KO, >=0 if OK |
||
| 964 | */ |
||
| 965 | public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null) |
||
| 966 | { |
||
| 967 | // phpcs:enable |
||
| 968 | // FIXME: $fulltext_keys parameter is unused |
||
| 969 | |||
| 970 | // cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra |
||
| 971 | // ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment'); |
||
| 972 | $sql = "create table ".$table."("; |
||
| 973 | $i = 0; |
||
| 974 | foreach ($fields as $field_name => $field_desc) |
||
| 975 | { |
||
| 976 | $sqlfields[$i] = $field_name." "; |
||
| 977 | $sqlfields[$i] .= $field_desc['type']; |
||
| 978 | if (preg_match("/^[^\s]/i", $field_desc['value'])) |
||
| 979 | $sqlfields[$i] .= "(".$field_desc['value'].")"; |
||
| 980 | elseif (preg_match("/^[^\s]/i", $field_desc['attribute'])) |
||
| 981 | $sqlfields[$i] .= " ".$field_desc['attribute']; |
||
| 982 | elseif (preg_match("/^[^\s]/i", $field_desc['default'])) |
||
| 983 | { |
||
| 984 | if (preg_match("/null/i", $field_desc['default'])) |
||
| 985 | $sqlfields[$i] .= " default ".$field_desc['default']; |
||
| 986 | else $sqlfields[$i] .= " default '".$this->escape($field_desc['default'])."'"; |
||
| 987 | } elseif (preg_match("/^[^\s]/i", $field_desc['null'])) |
||
| 988 | $sqlfields[$i] .= " ".$field_desc['null']; |
||
| 989 | |||
| 990 | elseif (preg_match("/^[^\s]/i", $field_desc['extra'])) |
||
| 991 | $sqlfields[$i] .= " ".$field_desc['extra']; |
||
| 992 | $i++; |
||
| 993 | } |
||
| 994 | if ($primary_key != "") |
||
| 995 | $pk = "primary key(".$primary_key.")"; |
||
| 996 | |||
| 997 | if (is_array($unique_keys)) |
||
| 998 | { |
||
| 999 | $i = 0; |
||
| 1000 | foreach ($unique_keys as $key => $value) |
||
| 1001 | { |
||
| 1002 | $sqluq[$i] = "UNIQUE KEY '".$key."' ('".$this->escape($value)."')"; |
||
| 1003 | $i++; |
||
| 1004 | } |
||
| 1005 | } |
||
| 1006 | if (is_array($keys)) |
||
| 1007 | { |
||
| 1008 | $i = 0; |
||
| 1009 | foreach ($keys as $key => $value) |
||
| 1010 | { |
||
| 1011 | $sqlk[$i] = "KEY ".$key." (".$value.")"; |
||
| 1012 | $i++; |
||
| 1013 | } |
||
| 1014 | } |
||
| 1015 | $sql .= implode(',', $sqlfields); |
||
| 1016 | if ($primary_key != "") |
||
| 1017 | $sql .= ",".$pk; |
||
| 1018 | if (is_array($unique_keys)) |
||
| 1019 | $sql .= ",".implode(',', $sqluq); |
||
| 1020 | if (is_array($keys)) |
||
| 1021 | $sql .= ",".implode(',', $sqlk); |
||
| 1022 | $sql .= ") type=".$type; |
||
| 1023 | |||
| 1024 | dol_syslog($sql, LOG_DEBUG); |
||
| 1025 | if (!$this->query($sql)) |
||
| 1026 | return -1; |
||
| 1027 | else return 1; |
||
| 1028 | } |
||
| 1029 | |||
| 1030 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 1031 | /** |
||
| 1032 | * Drop a table into database |
||
| 1033 | * |
||
| 1034 | * @param string $table Name of table |
||
| 1035 | * @return int <0 if KO, >=0 if OK |
||
| 1036 | */ |
||
| 1037 | public function DDLDropTable($table) |
||
| 1038 | { |
||
| 1039 | // phpcs:enable |
||
| 1040 | $sql = "DROP TABLE ".$table; |
||
| 1041 | |||
| 1042 | if (!$this->query($sql)) |
||
| 1043 | return -1; |
||
| 1044 | else return 1; |
||
| 1045 | } |
||
| 1046 | |||
| 1047 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 1048 | /** |
||
| 1049 | * Create a user to connect to database |
||
| 1050 | * |
||
| 1051 | * @param string $dolibarr_main_db_host Ip server |
||
| 1052 | * @param string $dolibarr_main_db_user Name of user to create |
||
| 1053 | * @param string $dolibarr_main_db_pass Password of user to create |
||
| 1054 | * @param string $dolibarr_main_db_name Database name where user must be granted |
||
| 1055 | * @return int <0 if KO, >=0 if OK |
||
| 1056 | */ |
||
| 1057 | public function DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name) |
||
| 1058 | { |
||
| 1059 | // phpcs:enable |
||
| 1060 | // Note: using ' on user does not works with pgsql |
||
| 1061 | $sql = "CREATE USER ".$this->escape($dolibarr_main_db_user)." with password '".$this->escape($dolibarr_main_db_pass)."'"; |
||
| 1062 | |||
| 1063 | dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log |
||
| 1064 | $resql = $this->query($sql); |
||
| 1065 | if (!$resql) |
||
| 1066 | { |
||
| 1067 | return -1; |
||
| 1068 | } |
||
| 1069 | |||
| 1070 | return 1; |
||
| 1071 | } |
||
| 1072 | |||
| 1073 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 1074 | /** |
||
| 1075 | * Return a pointer of line with description of a table or field |
||
| 1076 | * |
||
| 1077 | * @param string $table Name of table |
||
| 1078 | * @param string $field Optionnel : Name of field if we want description of field |
||
| 1079 | * @return false|resource Resultset x (x->attname) |
||
| 1080 | */ |
||
| 1081 | public function DDLDescTable($table, $field = "") |
||
| 1082 | { |
||
| 1083 | // phpcs:enable |
||
| 1084 | $sql = "SELECT attname FROM pg_attribute, pg_type WHERE typname = '".$this->escape($table)."' AND attrelid = typrelid"; |
||
| 1085 | $sql .= " AND attname NOT IN ('cmin', 'cmax', 'ctid', 'oid', 'tableoid', 'xmin', 'xmax')"; |
||
| 1086 | if ($field) $sql .= " AND attname = '".$this->escape($field)."'"; |
||
| 1087 | |||
| 1088 | dol_syslog($sql, LOG_DEBUG); |
||
| 1089 | $this->_results = $this->query($sql); |
||
| 1090 | return $this->_results; |
||
| 1091 | } |
||
| 1092 | |||
| 1093 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 1094 | /** |
||
| 1095 | * Create a new field into table |
||
| 1096 | * |
||
| 1097 | * @param string $table Name of table |
||
| 1098 | * @param string $field_name Name of field to add |
||
| 1099 | * @param string $field_desc Tableau associatif de description du champ a inserer[nom du parametre][valeur du parametre] |
||
| 1100 | * @param string $field_position Optionnel ex.: "after champtruc" |
||
| 1101 | * @return int <0 if KO, >0 if OK |
||
| 1102 | */ |
||
| 1103 | public function DDLAddField($table, $field_name, $field_desc, $field_position = "") |
||
| 1104 | { |
||
| 1105 | // phpcs:enable |
||
| 1106 | // cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra |
||
| 1107 | // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment'); |
||
| 1108 | $sql = "ALTER TABLE ".$table." ADD ".$field_name." "; |
||
| 1109 | $sql .= $field_desc['type']; |
||
| 1110 | if (preg_match("/^[^\s]/i", $field_desc['value'])) { |
||
| 1111 | if (!in_array($field_desc['type'], array('int', 'date', 'datetime')) && $field_desc['value']) |
||
| 1112 | { |
||
| 1113 | $sql .= "(".$field_desc['value'].")"; |
||
| 1114 | } |
||
| 1115 | } |
||
| 1116 | if (preg_match("/^[^\s]/i", $field_desc['attribute'])) |
||
| 1117 | $sql .= " ".$field_desc['attribute']; |
||
| 1118 | if (preg_match("/^[^\s]/i", $field_desc['null'])) |
||
| 1119 | $sql .= " ".$field_desc['null']; |
||
| 1120 | if (preg_match("/^[^\s]/i", $field_desc['default'])) { |
||
| 1121 | if (preg_match("/null/i", $field_desc['default'])) { |
||
| 1122 | $sql .= " default ".$field_desc['default']; |
||
| 1123 | } else { |
||
| 1124 | $sql .= " default '".$this->escape($field_desc['default'])."'"; |
||
| 1125 | } |
||
| 1126 | } |
||
| 1127 | if (preg_match("/^[^\s]/i", $field_desc['extra'])) { |
||
| 1128 | $sql .= " ".$field_desc['extra']; |
||
| 1129 | } |
||
| 1130 | $sql .= " ".$field_position; |
||
| 1131 | |||
| 1132 | dol_syslog($sql, LOG_DEBUG); |
||
| 1133 | if (!$this -> query($sql)) |
||
| 1134 | return -1; |
||
| 1135 | return 1; |
||
| 1136 | } |
||
| 1137 | |||
| 1138 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 1139 | /** |
||
| 1140 | * Update format of a field into a table |
||
| 1141 | * |
||
| 1142 | * @param string $table Name of table |
||
| 1143 | * @param string $field_name Name of field to modify |
||
| 1144 | * @param string $field_desc Array with description of field format |
||
| 1145 | * @return int <0 if KO, >0 if OK |
||
| 1146 | */ |
||
| 1147 | public function DDLUpdateField($table, $field_name, $field_desc) |
||
| 1148 | { |
||
| 1149 | // phpcs:enable |
||
| 1150 | $sql = "ALTER TABLE ".$table; |
||
| 1151 | $sql .= " MODIFY COLUMN ".$field_name." ".$field_desc['type']; |
||
| 1152 | if (in_array($field_desc['type'], array('double', 'tinyint', 'int', 'varchar')) && $field_desc['value']) { |
||
| 1153 | $sql .= "(".$field_desc['value'].")"; |
||
| 1154 | } |
||
| 1155 | |||
| 1156 | if ($field_desc['null'] == 'not null' || $field_desc['null'] == 'NOT NULL') |
||
| 1157 | { |
||
| 1158 | // 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 |
||
| 1159 | if ($field_desc['type'] == 'varchar' || $field_desc['type'] == 'text') |
||
| 1160 | { |
||
| 1161 | $sqlbis = "UPDATE ".$table." SET ".$field_name." = '".$this->escape($field_desc['default'] ? $field_desc['default'] : '')."' WHERE ".$field_name." IS NULL"; |
||
| 1162 | $this->query($sqlbis); |
||
| 1163 | } elseif ($field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') |
||
| 1164 | { |
||
| 1165 | $sqlbis = "UPDATE ".$table." SET ".$field_name." = ".((int) $this->escape($field_desc['default'] ? $field_desc['default'] : 0))." WHERE ".$field_name." IS NULL"; |
||
| 1166 | $this->query($sqlbis); |
||
| 1167 | } |
||
| 1168 | } |
||
| 1169 | |||
| 1170 | if ($field_desc['default'] != '') |
||
| 1171 | { |
||
| 1172 | if ($field_desc['type'] == 'double' || $field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') $sql .= " DEFAULT ".$this->escape($field_desc['default']); |
||
| 1173 | elseif ($field_desc['type'] != 'text') $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'"; // Default not supported on text fields |
||
| 1174 | } |
||
| 1175 | |||
| 1176 | dol_syslog($sql, LOG_DEBUG); |
||
| 1177 | if (!$this->query($sql)) |
||
| 1178 | return -1; |
||
| 1179 | return 1; |
||
| 1180 | } |
||
| 1181 | |||
| 1182 | // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
||
| 1183 | /** |
||
| 1184 | * Drop a field from table |
||
| 1185 | * |
||
| 1186 | * @param string $table Name of table |
||
| 1187 | * @param string $field_name Name of field to drop |
||
| 1188 | * @return int <0 if KO, >0 if OK |
||
| 1189 | */ |
||
| 1190 | public function DDLDropField($table, $field_name) |
||
| 1201 | } |
||
| 1202 | |||
| 1203 | /** |
||
| 1204 | * Return charset used to store data in database |
||
| 1205 | * |
||
| 1206 | * @return string Charset |
||
| 1207 | */ |
||
| 1208 | public function getDefaultCharacterSetDatabase() |
||
| 1209 | { |
||
| 1210 | $resql = $this->query('SHOW SERVER_ENCODING'); |
||
| 1211 | if ($resql) |
||
| 1212 | { |
||
| 1213 | $liste = $this->fetch_array($resql); |
||
| 1214 | return $liste['server_encoding']; |
||
| 1215 | } else return ''; |
||
| 1216 | } |
||
| 1217 | |||
| 1218 | /** |
||
| 1219 | * Return list of available charset that can be used to store data in database |
||
| 1220 | * |
||
| 1221 | * @return array List of Charset |
||
| 1222 | */ |
||
| 1223 | public function getListOfCharacterSet() |
||
| 1224 | { |
||
| 1225 | $resql = $this->query('SHOW SERVER_ENCODING'); |
||
| 1226 | $liste = array(); |
||
| 1227 | if ($resql) |
||
| 1228 | { |
||
| 1229 | $i = 0; |
||
| 1230 | while ($obj = $this->fetch_object($resql)) |
||
| 1231 | { |
||
| 1232 | $liste[$i]['charset'] = $obj->server_encoding; |
||
| 1233 | $liste[$i]['description'] = 'Default database charset'; |
||
| 1234 | $i++; |
||
| 1235 | } |
||
| 1236 | $this->free($resql); |
||
| 1237 | } else { |
||
| 1238 | return null; |
||
| 1239 | } |
||
| 1240 | return $liste; |
||
| 1241 | } |
||
| 1242 | |||
| 1243 | /** |
||
| 1244 | * Return collation used in database |
||
| 1245 | * |
||
| 1246 | * @return string Collation value |
||
| 1247 | */ |
||
| 1248 | public function getDefaultCollationDatabase() |
||
| 1249 | { |
||
| 1250 | $resql = $this->query('SHOW LC_COLLATE'); |
||
| 1251 | if ($resql) |
||
| 1252 | { |
||
| 1253 | $liste = $this->fetch_array($resql); |
||
| 1254 | return $liste['lc_collate']; |
||
| 1255 | } else return ''; |
||
| 1256 | } |
||
| 1257 | |||
| 1258 | /** |
||
| 1259 | * Return list of available collation that can be used for database |
||
| 1260 | * |
||
| 1261 | * @return array Liste of Collation |
||
| 1262 | */ |
||
| 1263 | public function getListOfCollation() |
||
| 1264 | { |
||
| 1265 | $resql = $this->query('SHOW LC_COLLATE'); |
||
| 1266 | $liste = array(); |
||
| 1267 | if ($resql) |
||
| 1268 | { |
||
| 1269 | $i = 0; |
||
| 1270 | while ($obj = $this->fetch_object($resql)) |
||
| 1271 | { |
||
| 1272 | $liste[$i]['collation'] = $obj->lc_collate; |
||
| 1273 | $i++; |
||
| 1274 | } |
||
| 1275 | $this->free($resql); |
||
| 1276 | } else { |
||
| 1277 | return null; |
||
| 1278 | } |
||
| 1279 | return $liste; |
||
| 1280 | } |
||
| 1281 | |||
| 1282 | /** |
||
| 1283 | * Return full path of dump program |
||
| 1284 | * |
||
| 1285 | * @return string Full path of dump program |
||
| 1286 | */ |
||
| 1287 | public function getPathOfDump() |
||
| 1288 | { |
||
| 1289 | $fullpathofdump = '/pathtopgdump/pg_dump'; |
||
| 1290 | |||
| 1291 | if (file_exists('/usr/bin/pg_dump')) |
||
| 1292 | { |
||
| 1293 | $fullpathofdump = '/usr/bin/pg_dump'; |
||
| 1294 | } else { |
||
| 1295 | // TODO L'utilisateur de la base doit etre un superadmin pour lancer cette commande |
||
| 1296 | $resql = $this->query('SHOW data_directory'); |
||
| 1297 | if ($resql) |
||
| 1298 | { |
||
| 1299 | $liste = $this->fetch_array($resql); |
||
| 1300 | $basedir = $liste['data_directory']; |
||
| 1301 | $fullpathofdump = preg_replace('/data$/', 'bin', $basedir).'/pg_dump'; |
||
| 1302 | } |
||
| 1303 | } |
||
| 1304 | |||
| 1305 | return $fullpathofdump; |
||
| 1306 | } |
||
| 1307 | |||
| 1308 | /** |
||
| 1309 | * Return full path of restore program |
||
| 1310 | * |
||
| 1311 | * @return string Full path of restore program |
||
| 1312 | */ |
||
| 1313 | public function getPathOfRestore() |
||
| 1314 | { |
||
| 1315 | //$tool='pg_restore'; |
||
| 1316 | $tool = 'psql'; |
||
| 1317 | |||
| 1318 | $fullpathofdump = '/pathtopgrestore/'.$tool; |
||
| 1319 | |||
| 1320 | if (file_exists('/usr/bin/'.$tool)) |
||
| 1321 | { |
||
| 1322 | $fullpathofdump = '/usr/bin/'.$tool; |
||
| 1323 | } else { |
||
| 1324 | // TODO L'utilisateur de la base doit etre un superadmin pour lancer cette commande |
||
| 1325 | $resql = $this->query('SHOW data_directory'); |
||
| 1326 | if ($resql) |
||
| 1327 | { |
||
| 1328 | $liste = $this->fetch_array($resql); |
||
| 1329 | $basedir = $liste['data_directory']; |
||
| 1330 | $fullpathofdump = preg_replace('/data$/', 'bin', $basedir).'/'.$tool; |
||
| 1331 | } |
||
| 1332 | } |
||
| 1333 | |||
| 1334 | return $fullpathofdump; |
||
| 1335 | } |
||
| 1336 | |||
| 1337 | /** |
||
| 1338 | * Return value of server parameters |
||
| 1339 | * |
||
| 1340 | * @param string $filter Filter list on a particular value |
||
| 1341 | * @return array Array of key-values (key=>value) |
||
| 1342 | */ |
||
| 1343 | public function getServerParametersValues($filter = '') |
||
| 1356 | } |
||
| 1357 | |||
| 1358 | /** |
||
| 1359 | * Return value of server status |
||
| 1360 | * |
||
| 1361 | * @param string $filter Filter list on a particular value |
||
| 1362 | * @return array Array of key-values (key=>value) |
||
| 1363 | */ |
||
| 1364 | public function getServerStatusValues($filter = '') |
||
| 1378 | } |
||
| 1379 | } |
||
| 1380 |
This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.