dolibarr 25.0.0-alpha
sqlite3.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-2011 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
6 * Copyright (C) 2005-2009 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
8 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
9 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 */
24
30require_once DOL_DOCUMENT_ROOT.'/core/db/DoliDB.class.php';
31
35class DoliDBSqlite3 extends DoliDB
36{
38 public $type = 'sqlite3';
40 const LABEL = 'Sqlite3';
42 const VERSIONMIN = '3.0.0';
43
47 private $_results;
48
52 private $queryString;
53
54 const WEEK_MONDAY_FIRST = 1;
55 const WEEK_YEAR = 2;
56 const WEEK_FIRST_WEEKDAY = 4;
57
58
71 public function __construct($type, $host, $user, $pass, $name = '', $port = 0, $forcenew = false) // @phpstan-ignore constructor.unusedParameter, constructor.unusedParameter
72 {
73 global $conf;
74
75 // Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
76 if (!empty($conf->db->character_set)) {
77 $this->forcecharset = $conf->db->character_set;
78 }
79 if (!empty($conf->db->dolibarr_main_db_collation)) {
80 $this->forcecollate = $conf->db->dolibarr_main_db_collation;
81 }
82
83 $this->database_user = $user;
84 $this->database_host = $host;
85 $this->database_port = $port;
86
87 $this->transaction_opened = 0;
88
89 //print "Name DB: $host,$user,$pass,$name<br>";
90
91 /*if (! function_exists("sqlite_query"))
92 {
93 $this->connected = false;
94 $this->ok = false;
95 $this->error="Sqlite PHP functions for using Sqlite driver are not available in this version of PHP. Try to use another driver.";
96 dol_syslog(get_class($this)."::DoliDBSqlite3 : Sqlite PHP functions for using Sqlite driver are not available in this version of PHP. Try to use another driver.",LOG_ERR);
97 return;
98 }*/
99
100 /*if (! $host)
101 {
102 $this->connected = false;
103 $this->ok = false;
104 $this->error=$langs->trans("ErrorWrongHostParameter");
105 dol_syslog(get_class($this)."::DoliDBSqlite3 : Erreur Connect, wrong host parameters",LOG_ERR);
106 return;
107 }*/
108
109 // Essai connection serveur
110 // We do not try to connect to database, only to server. Connect to database is done later in constructor
111 $this->db = $this->connect($host, $user, $pass, $name, $port);
112
113 if ($this->db) {
114 $this->connected = true;
115 $this->ok = true;
116 $this->database_selected = true;
117 $this->database_name = $name;
118
119 $this->addCustomFunction('IF');
120 $this->addCustomFunction('MONTH');
121 $this->addCustomFunction('CURTIME');
122 $this->addCustomFunction('CURDATE');
123 $this->addCustomFunction('WEEK', 1);
124 $this->addCustomFunction('WEEK', 2);
125 $this->addCustomFunction('WEEKDAY');
126 $this->addCustomFunction('date_format');
127 //$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
128 } else {
129 // host, login ou password incorrect
130 $this->connected = false;
131 $this->ok = false;
132 $this->database_selected = false;
133 $this->database_name = '';
134 //$this->error=sqlite_connect_error();
135 dol_syslog(get_class($this)."::DoliDBSqlite3 : Error Connect ".$this->error, LOG_ERR);
136 }
137 }
138
139
147 public function convertSQLFromMysql($line, $type = 'ddl')
148 {
149 // Removed empty line if this is a comment line for SVN tagging
150 if (preg_match('/^--\s\$Id/i', $line)) {
151 return '';
152 }
153 // Return line if this is a comment
154 if (preg_match('/^#/i', $line) || preg_match('/^$/i', $line) || preg_match('/^--/i', $line)) {
155 return $line;
156 }
157 if ($line != "") {
158 if ($type == 'auto') {
159 if (preg_match('/ALTER TABLE/i', $line)) {
160 $type = 'dml';
161 } elseif (preg_match('/CREATE TABLE/i', $line)) {
162 $type = 'dml';
163 } elseif (preg_match('/DROP TABLE/i', $line)) {
164 $type = 'dml';
165 }
166 }
167
168 if ($type == 'dml') {
169 $line = preg_replace('/\s/', ' ', $line); // Replace tabulation with space
170
171 // we are inside create table statement so let's process datatypes
172 if (preg_match('/(ISAM|innodb)/i', $line)) { // end of create table sequence
173 $line = preg_replace('/\‍)[\s\t]*type[\s\t]*=[\s\t]*(MyISAM|innodb);/i', ');', $line);
174 $line = preg_replace('/\‍)[\s\t]*engine[\s\t]*=[\s\t]*(MyISAM|innodb);/i', ');', $line);
175 $line = preg_replace('/,$/', '', $line);
176 }
177
178 // Process case: "CREATE TABLE llx_mytable(rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,code..."
179 if (preg_match('/[\s\t\‍(]*(\w*)[\s\t]+int.*auto_increment/i', $line, $reg)) {
180 $newline = preg_replace('/([\s\t\‍(]*)([a-zA-Z_0-9]*)[\s\t]+int.*auto_increment[^,]*/i', '\\1 \\2 integer PRIMARY KEY AUTOINCREMENT', $line);
181 //$line = "-- ".$line." replaced by --\n".$newline;
182 $line = $newline;
183 }
184
185 // tinyint type conversion
186 $line = str_replace('tinyint', 'smallint', $line);
187
188 // nuke unsigned
189 $line = preg_replace('/(int\w+|smallint)\s+unsigned/i', '\\1', $line);
190
191 // blob -> text
192 $line = preg_replace('/\w*blob/i', 'text', $line);
193
194 // tinytext/mediumtext -> text
195 $line = preg_replace('/tinytext/i', 'text', $line);
196 $line = preg_replace('/mediumtext/i', 'text', $line);
197
198 // change not null datetime field to null valid ones
199 // (to support remapping of "zero time" to null
200 $line = preg_replace('/datetime not null/i', 'datetime', $line);
201 $line = preg_replace('/datetime/i', 'timestamp', $line);
202
203 // double -> numeric
204 $line = preg_replace('/^double/i', 'numeric', $line);
205 $line = preg_replace('/(\s*)double/i', '\\1numeric', $line);
206 // float -> numeric
207 $line = preg_replace('/^float/i', 'numeric', $line);
208 $line = preg_replace('/(\s*)float/i', '\\1numeric', $line);
209
210 // unique index(field1,field2)
211 if (preg_match('/unique index\s*\‍((\w+\s*,\s*\w+)\‍)/i', $line)) {
212 $line = preg_replace('/unique index\s*\‍((\w+\s*,\s*\w+)\‍)/i', 'UNIQUE\‍(\\1\‍)', $line);
213 }
214
215 // We remove end of requests "AFTER fieldxxx"
216 $line = preg_replace('/AFTER [a-z0-9_]+/i', '', $line);
217
218 // We remove start of requests "ALTER TABLE tablexxx" if this is a DROP INDEX
219 $line = preg_replace('/ALTER TABLE [a-z0-9_]+ DROP INDEX/i', 'DROP INDEX', $line);
220
221 // Translate order to rename fields
222 if (preg_match('/ALTER TABLE ([a-z0-9_]+) CHANGE(?: COLUMN)? ([a-z0-9_]+) ([a-z0-9_]+)(.*)$/i', $line, $reg)) {
223 $line = "-- ".$line." replaced by --\n";
224 $line .= "ALTER TABLE ".$reg[1]." RENAME COLUMN ".$reg[2]." TO ".$reg[3];
225 }
226
227 // Translate order to modify field format
228 if (preg_match('/ALTER TABLE ([a-z0-9_]+) MODIFY(?: COLUMN)? ([a-z0-9_]+) (.*)$/i', $line, $reg)) {
229 $line = "-- ".$line." replaced by --\n";
230 $newreg3 = $reg[3];
231 $newreg3 = preg_replace('/ DEFAULT NULL/i', '', $newreg3);
232 $newreg3 = preg_replace('/ NOT NULL/i', '', $newreg3);
233 $newreg3 = preg_replace('/ NULL/i', '', $newreg3);
234 $newreg3 = preg_replace('/ DEFAULT 0/i', '', $newreg3);
235 $newreg3 = preg_replace('/ DEFAULT \'[0-9a-zA-Z_@]*\'/i', '', $newreg3);
236 $line .= "ALTER TABLE ".$reg[1]." ALTER COLUMN ".$reg[2]." TYPE ".$newreg3;
237 // TODO Add alter to set default value or null/not null if there is this in $reg[3]
238 }
239
240 // alter table add primary key (field1, field2 ...) -> We create a unique index instead as dynamic creation of primary key is not supported
241 // ALTER TABLE llx_dolibarr_modules ADD PRIMARY KEY pk_dolibarr_modules (numero, entity);
242 if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+PRIMARY\s+KEY\s*(.*)\s*\‍((.*)$/i', $line, $reg)) {
243 $line = "-- ".$line." replaced by --\n";
244 $line .= "CREATE UNIQUE INDEX ".$reg[2]." ON ".$reg[1]."(".$reg[3];
245 }
246
247 // Translate order to drop foreign keys
248 // ALTER TABLE llx_dolibarr_modules DROP FOREIGN KEY fk_xxx;
249 if (preg_match('/ALTER\s+TABLE\s*(.*)\s*DROP\s+FOREIGN\s+KEY\s*(.*)$/i', $line, $reg)) {
250 $line = "-- ".$line." replaced by --\n";
251 $line .= "ALTER TABLE ".$reg[1]." DROP CONSTRAINT ".$reg[2];
252 }
253
254 // alter table add [unique] [index] (field1, field2 ...)
255 // ALTER TABLE llx_accountingaccount ADD INDEX idx_accountingaccount_fk_pcg_version (fk_pcg_version)
256 if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+(UNIQUE INDEX|INDEX|UNIQUE)\s+(.*)\s*\‍(([\w,\s]+)\‍)/i', $line, $reg)) {
257 $fieldlist = $reg[4];
258 $idxname = $reg[3];
259 $tablename = $reg[1];
260 $line = "-- ".$line." replaced by --\n";
261 $line .= "CREATE ".(preg_match('/UNIQUE/', $reg[2]) ? 'UNIQUE ' : '')."INDEX ".$idxname." ON ".$tablename." (".$fieldlist.")";
262 }
263 if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+CONSTRAINT\s+(.*)\s*FOREIGN\s+KEY\s*\‍(([\w,\s]+)\‍)\s*REFERENCES\s+(\w+)\s*\‍(([\w,\s]+)\‍)/i', $line, $reg)) {
264 // Constraints are not yet created
265 dol_syslog(get_class().'::query line emptied');
266 $line = 'SELECT 0;';
267 }
268
269 //if (preg_match('/rowid\s+.*\s+PRIMARY\s+KEY,/i', $line)) {
270 //preg_replace('/(rowid\s+.*\s+PRIMARY\s+KEY\s*,)/i', '/* \\1 */', $line);
271 //}
272 }
273
274 // Delete using criteria on other table must not declare twice the deleted table
275 // DELETE FROM tabletodelete USING tabletodelete, othertable -> DELETE FROM tabletodelete USING othertable
276 if (preg_match('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', $line, $reg)) {
277 if ($reg[1] == $reg[2]) { // If same table, we remove second one
278 $line = preg_replace('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', 'DELETE FROM \\1 USING \\3', $line);
279 }
280 }
281
282 // Remove () in the tables in FROM if one table
283 $line = preg_replace('/FROM\s*\‍((([a-z_]+)\s+as\s+([a-z_]+)\s*)\‍)/i', 'FROM \\1', $line);
284 //print $line."\n";
285
286 // Remove () in the tables in FROM if two table
287 $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);
288 //print $line."\n";
289
290 // Remove () in the tables in FROM if two table
291 $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);
292 //print $line."\n";
293
294 //print "type=".$type." newline=".$line."<br>\n";
295 }
296
297 return $line;
298 }
299
300 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
307 public function select_db($database)
308 {
309 // phpcs:enable
310 dol_syslog(get_class($this)."::select_db database=".$database, LOG_DEBUG);
311 // sqlite_select_db() does not exist
312 //return sqlite_select_db($this->db,$database);
313 return true;
314 }
315
316
329 public function connect($host, $login, $passwd, $name, $port = 0, $forcenew = false)
330 {
331 global $main_data_dir;
332
333 dol_syslog(get_class($this)."::connect name=".$name, LOG_DEBUG);
334
335 $dir = $main_data_dir;
336 if (empty($dir)) {
337 $dir = DOL_DATA_ROOT;
338 }
339 // With sqlite, port must be in connect parameters
340 //if (! $newport) $newport=3306;
341 $database_name = $dir.'/database_'.$name.'.sdb';
342 try {
343 /*** connect to SQLite database ***/
344 //$this->db = new PDO("sqlite:".$dir.'/database_'.$name.'.sdb');
345 $this->db = new SQLite3($database_name);
346 //$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
347 } catch (Throwable $e) {
348 $this->error = self::LABEL.' '.$e->getMessage().' current dir='.$database_name;
349 return false;
350 }
351
352 //print "Result of connect function: ".$this->db;
353 return $this->db;
354 }
355
356
362 public function getVersion()
363 {
364 $tmp = $this->db->version();
365 return $tmp['versionString'];
366 }
367
373 public function getDriverInfo()
374 {
375 return 'sqlite3 php driver';
376 }
377
378
385 public function close()
386 {
387 if ($this->db) {
388 if ($this->transaction_opened > 0) {
389 dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened, LOG_ERR);
390 }
391 $this->connected = false;
392 $this->db->close();
393 unset($this->db); // Clean this->db
394 return true;
395 }
396 return false;
397 }
398
409 public function query($query, $usesavepoint = 0, $type = 'auto', $result_mode = 0)
410 {
411 global $conf, $dolibarr_main_db_readonly;
412
413 $ret = false;
414
415 $query = trim($query);
416
417 $this->error = '';
418
419 // Convert MySQL syntax to SQLite syntax
420 $reg = array();
421 if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+CONSTRAINT\s+(.*)\s*FOREIGN\s+KEY\s*\‍(([\w,\s]+)\‍)\s*REFERENCES\s+(\w+)\s*\‍(([\w,\s]+)\‍)/i', $query, $reg)) {
422 // Adding a foreign key to the table
423 // table replacement procedure to add the constraint
424 // Example : ALTER TABLE llx_adherent ADD CONSTRAINT adherent_fk_soc FOREIGN KEY (fk_soc) REFERENCES llx_societe (rowid)
425 // -> CREATE TABLE ( ... ,CONSTRAINT adherent_fk_soc FOREIGN KEY (fk_soc) REFERENCES llx_societe (rowid))
426 $foreignFields = $reg[5];
427 $foreignTable = $reg[4];
428 $localfields = $reg[3];
429 $constraintname = trim($reg[2]);
430 $tablename = trim($reg[1]);
431
432 $descTable = $this->db->querySingle("SELECT sql FROM sqlite_master WHERE name='".$this->escape($tablename)."'");
433
434 // 1- Rename the table to a temporary name
435 $this->query("ALTER TABLE ".$tablename." RENAME TO tmp_".$tablename);
436
437 // 2- Recreate the table with the new constraint
438
439 // Adjust the SQL request to add the constraint
440 $descTable = substr($descTable, 0, strlen($descTable) - 1);
441 $descTable .= ", CONSTRAINT ".$constraintname." FOREIGN KEY (".$localfields.") REFERENCES ".$foreignTable."(".$foreignFields.")";
442
443 // Add closing parenthesis for SQL query
444 $descTable .= ')';
445
446 // Perform query to create the table
447 $this->query($descTable);
448
449 // 3- Copy the data from the temporary table (before adding constraint)
450 $this->query("INSERT INTO ".$tablename." SELECT * FROM tmp_".$tablename);
451
452 // 4- Delete the original (now temporary) table
453 $this->query("DROP TABLE tmp_".$tablename);
454
455 // dummy statement
456 $query = "SELECT 0";
457 } else {
458 $query = $this->convertSQLFromMysql($query, $type);
459 }
460 //print "After convertSQLFromMysql:\n".$query."<br>\n";
461
462 if (!in_array($query, array('BEGIN', 'COMMIT', 'ROLLBACK'))) {
463 $SYSLOG_SQL_LIMIT = 10000; // limit log to 10kb per line to limit DOS attacks
464 dol_syslog('sql='.substr($query, 0, $SYSLOG_SQL_LIMIT), LOG_DEBUG);
465 }
466 if (empty($query)) {
467 return false; // Return false = error if empty request
468 }
469
470 if (!empty($dolibarr_main_db_readonly)) {
471 if (preg_match('/^(INSERT|UPDATE|REPLACE|DELETE|CREATE|ALTER|TRUNCATE|DROP)/i', $query)) {
472 $this->lasterror = 'Application in read-only mode';
473 $this->lasterrno = 'APPREADONLY';
474 $this->lastquery = $query;
475 return false;
476 }
477 }
478
479 // SQL statement that does not require a connection to a database (example: CREATE DATABASE)
480 try {
481 //$ret = $this->db->exec($query);
482 $ret = $this->db->query($query); // $ret is a Sqlite3Result
483 if ($ret) {
484 $this->queryString = $query;
485 }
486 } catch (Throwable $e) {
487 $this->error = $this->db->lastErrorMsg();
488 }
489
490 if (!preg_match("/^COMMIT/i", $query) && !preg_match("/^ROLLBACK/i", $query)) {
491 // If it is a user query, save it along with its resultset
492 if (!is_object($ret) || $this->error) {
493 $this->lastqueryerror = $query;
494 $this->lasterror = $this->error();
495 $this->lasterrno = $this->errno();
496
497 dol_syslog(get_class($this)."::query SQL Error query: ".$query, LOG_ERR);
498
499 $errormsg = get_class($this)."::query SQL Error message: ".$this->lasterror;
500
501 if (preg_match('/[0-9]/', $this->lasterrno)) {
502 $errormsg .= ' ('.$this->lasterrno.')';
503 }
504
505 if (getDolGlobalString('SYSLOG_LEVEL') < LOG_DEBUG) {
506 dol_syslog(get_class($this)."::query SQL Error query: ".$query, LOG_ERR); // Log of request was not yet done previously
507 }
508 dol_syslog(get_class($this)."::query SQL Error message: ".$errormsg, LOG_ERR);
509 }
510 $this->lastquery = $query;
511 $this->_results = $ret;
512 }
513
514 return $ret;
515 }
516
517 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
524 public function fetch_object($resultset)
525 {
526 // phpcs:enable
527 // If the resultset is not provided, use the last one used on this connection
528 if (!is_object($resultset)) {
529 $resultset = $this->_results;
530 }
531 //return $resultset->fetch(PDO::FETCH_OBJ);
532 $ret = $resultset->fetchArray(SQLITE3_ASSOC);
533 if ($ret) {
534 return (object) $ret;
535 }
536 return false;
537 }
538
539
540 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
547 public function fetch_array($resultset)
548 {
549 // phpcs:enable
550 // If resultset not provided, we take the last used by connection
551 if (!is_object($resultset)) {
552 $resultset = $this->_results;
553 }
554 //return $resultset->fetch(PDO::FETCH_ASSOC);
555 $ret = $resultset->fetchArray(SQLITE3_ASSOC);
556 return $ret;
557 }
558
559 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
566 public function fetch_row($resultset)
567 {
568 // phpcs:enable
569 // If resultset not provided, we take the last used by connection
570 if (!is_bool($resultset)) {
571 if (!is_object($resultset)) {
572 $resultset = $this->_results;
573 }
574 return $resultset->fetchArray(SQLITE3_NUM);
575 } else {
576 // If the cursor is a boolean, return 0
577 return 0;
578 }
579 }
580
581 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
589 public function num_rows($resultset)
590 {
591 // phpcs:enable
592
593 // If resultset not provided, we take the last used by connection
594 if (!is_object($resultset)) {
595 $resultset = $this->_results;
596 }
597 // Ignore Phan - queryString is added as dynamic property @phan-suppress-next-line PhanUndeclaredProperty
598 if (preg_match("/^SELECT/i", $resultset->queryString)) {
599 // Ignore Phan - queryString is added as dynamic property @phan-suppress-next-line PhanUndeclaredProperty
600 return $this->db->querySingle("SELECT count(*) FROM (".$resultset->queryString.") q");
601 }
602 return 0;
603 }
604
605 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
613 public function affected_rows($resultset)
614 {
615 // phpcs:enable
616
617 // If resultset not provided, we take the last used by connection
618 if (!is_object($resultset)) {
619 $resultset = $this->_results;
620 }
621 if (preg_match("/^SELECT/i", $this->queryString)) {
622 return $this->num_rows($resultset);
623 }
624 // mysql requires a base link for this function, unlike pgsql which takes a resultset
625 return $this->db->changes();
626 }
627
628
635 public function free($resultset = null)
636 {
637 // If resultset not provided, we take the last used by connection
638 if (!is_object($resultset)) {
639 $resultset = $this->_results;
640 }
641 // If resultset is one, free the memory
642 if ($resultset && is_object($resultset)) {
643 $resultset->finalize();
644 }
645 }
646
653 public function escape($stringtoencode)
654 {
655 return SQLite3::escapeString((string) $stringtoencode);
656 }
657
664 public function escapeforlike($stringtoencode)
665 {
666 return str_replace(array('\\', '_', '%'), array('\\\\', '\_', '\%'), (string) $stringtoencode);
667 }
668
674 public function errno()
675 {
676 if (!$this->connected) {
677 // If the connection failed, $this->db is not valid.
678 return 'DB_ERROR_FAILED_TO_CONNECT';
679 } else {
680 // Constants to convert error code to a generic Dolibarr error code
681 /*$errorcode_map = array(
682 1004 => 'DB_ERROR_CANNOT_CREATE',
683 1005 => 'DB_ERROR_CANNOT_CREATE',
684 1006 => 'DB_ERROR_CANNOT_CREATE',
685 1007 => 'DB_ERROR_ALREADY_EXISTS',
686 1008 => 'DB_ERROR_CANNOT_DROP',
687 1025 => 'DB_ERROR_NO_FOREIGN_KEY_TO_DROP',
688 1044 => 'DB_ERROR_ACCESSDENIED',
689 1046 => 'DB_ERROR_NODBSELECTED',
690 1048 => 'DB_ERROR_CONSTRAINT',
691 'HY000' => 'DB_ERROR_TABLE_ALREADY_EXISTS',
692 1051 => 'DB_ERROR_NOSUCHTABLE',
693 1054 => 'DB_ERROR_NOSUCHFIELD',
694 1060 => 'DB_ERROR_COLUMN_ALREADY_EXISTS',
695 1061 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
696 1062 => 'DB_ERROR_RECORD_ALREADY_EXISTS',
697 1064 => 'DB_ERROR_SYNTAX',
698 1068 => 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS',
699 1075 => 'DB_ERROR_CANT_DROP_PRIMARY_KEY',
700 1091 => 'DB_ERROR_NOSUCHFIELD',
701 1100 => 'DB_ERROR_NOT_LOCKED',
702 1136 => 'DB_ERROR_VALUE_COUNT_ON_ROW',
703 1146 => 'DB_ERROR_NOSUCHTABLE',
704 1216 => 'DB_ERROR_NO_PARENT',
705 1217 => 'DB_ERROR_CHILD_EXISTS',
706 1451 => 'DB_ERROR_CHILD_EXISTS'
707 );
708
709 if (isset($errorcode_map[$this->db->errorCode()]))
710 {
711 return $errorcode_map[$this->db->errorCode()];
712 }*/
713 $errno = $this->db->lastErrorCode();
714 if ($errno == 'HY000' || $errno == 0) {
715 if (preg_match('/table.*already exists/i', $this->error)) {
716 return 'DB_ERROR_TABLE_ALREADY_EXISTS';
717 } elseif (preg_match('/index.*already exists/i', $this->error)) {
718 return 'DB_ERROR_KEY_NAME_ALREADY_EXISTS';
719 } elseif (preg_match('/syntax error/i', $this->error)) {
720 return 'DB_ERROR_SYNTAX';
721 }
722 }
723 if ($errno == '23000') {
724 if (preg_match('/column.* not unique/i', $this->error)) {
725 return 'DB_ERROR_RECORD_ALREADY_EXISTS';
726 } elseif (preg_match('/PRIMARY KEY must be unique/i', $this->error)) {
727 return 'DB_ERROR_RECORD_ALREADY_EXISTS';
728 }
729 }
730 if ($errno > 1) {
731 // TODO See the list of error messages
732 }
733
734 return ($errno ? 'DB_ERROR_'.$errno : '0');
735 }
736 }
737
743 public function error()
744 {
745 if (!$this->connected) {
746 // If the connection failed, $this->db is not valid for sqlite_error.
747 return 'Not connected. Check setup parameters in conf/conf.php file and your sqlite version';
748 } else {
749 return $this->error;
750 }
751 }
752
753 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
761 public function last_insert_id($tab, $fieldid = 'rowid')
762 {
763 // phpcs:enable
764 return $this->db->lastInsertRowId();
765 }
766
775 public function encrypt($fieldorvalue, $withQuotes = 1)
776 {
777 global $conf;
778
779 // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
780 $cryptType = (!empty($conf->db->dolibarr_main_db_encryption) ? $conf->db->dolibarr_main_db_encryption : 0);
781
782 //Encryption key
783 $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : '');
784
785 $escapedstringwithquotes = ($withQuotes ? "'" : "").$this->escape($fieldorvalue).($withQuotes ? "'" : "");
786
787 if ($cryptType && !empty($cryptKey)) {
788 if ($cryptType == 2) {
789 $escapedstringwithquotes = "AES_ENCRYPT(".$escapedstringwithquotes.", '".$this->escape($cryptKey)."')";
790 } elseif ($cryptType == 1) {
791 $escapedstringwithquotes = "DES_ENCRYPT(".$escapedstringwithquotes.", '".$this->escape($cryptKey)."')";
792 }
793 }
794
795 return $escapedstringwithquotes;
796 }
797
804 public function decrypt($value)
805 {
806 global $conf;
807
808 // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
809 $cryptType = ($conf->db->dolibarr_main_db_encryption ? $conf->db->dolibarr_main_db_encryption : 0);
810
811 //Encryption key
812 $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : '');
813
814 $return = $value;
815
816 if ($cryptType && !empty($cryptKey)) {
817 if ($cryptType == 2) {
818 $return = 'AES_DECRYPT('.$value.',\''.$cryptKey.'\')';
819 } elseif ($cryptType == 1) {
820 $return = 'DES_DECRYPT('.$value.',\''.$cryptKey.'\')';
821 }
822 }
823
824 return $return;
825 }
826
827
828 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
834 public function DDLGetConnectId()
835 {
836 // phpcs:enable
837 return '?';
838 }
839
840
841 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
853 public function DDLCreateDb($database, $charset = '', $collation = '', $owner = '')
854 {
855 // phpcs:enable
856 if (empty($charset)) {
857 $charset = $this->forcecharset;
858 }
859 if (empty($collation)) {
860 $collation = $this->forcecollate;
861 }
862
863 // ALTER DATABASE dolibarr_db DEFAULT CHARACTER SET latin DEFAULT COLLATE latin1_swedish_ci
864 $sql = "CREATE DATABASE ".$this->sanitize($database);
865 $sql .= " DEFAULT CHARACTER SET ".$this->sanitize($charset)." DEFAULT COLLATE ".$this->sanitize($collation);
866
867 dol_syslog($sql, LOG_DEBUG);
868 $ret = $this->query($sql);
869
870 return $ret;
871 }
872
873 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
881 public function DDLListTables($database, $table = '')
882 {
883 // phpcs:enable
884 $listtables = array();
885
886 $sanitizedlike = '';
887 if ($table) {
888 $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table);
889
890 $sanitizedlike = "LIKE '".$this->escape($tmptable)."'";
891 }
892 $sanitizedtmpdatabase = preg_replace('/[^a-z0-9\.\-\_]/i', '', $database); // @phan-suppress-current-line SqlInjection
893
894 $sql = "SHOW TABLES FROM ".$sanitizedtmpdatabase." ".$sanitizedlike.";";
895 //print $sql;
896 $result = $this->query($sql);
897 if ($result) {
898 while ($row = $this->fetch_row($result)) {
899 $listtables[] = $row[0];
900 }
901 }
902 return $listtables;
903 }
904
905 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
913 public function DDLListTablesFull($database, $table = '')
914 {
915 // phpcs:enable
916 $listtables = array();
917
918 $sanitizedlike = '';
919 if ($table) {
920 $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table);
921
922 $sanitizedlike = "LIKE '".$this->escape($tmptable)."'";
923 }
924 $sanitizedtmpdatabase = preg_replace('/[^a-z0-9\.\-\_]/i', '', $database); // @phan-suppress-current-line SqlInjection
925
926 $sql = "SHOW FULL TABLES FROM ".$sanitizedtmpdatabase." ".$sanitizedlike.";";
927 //print $sql;
928 $result = $this->query($sql);
929 if ($result) {
930 while ($row = $this->fetch_row($result)) {
931 $listtables[] = $row;
932 }
933 }
934 return $listtables;
935 }
936
937 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
946 public function DDLInfoTable($table)
947 {
948 // phpcs:enable
949 $infotables = array();
950
951 $sanitizedtmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table);
952
953 $sql = "SHOW FULL COLUMNS FROM ".$sanitizedtmptable.";";
954
955 dol_syslog($sql, LOG_DEBUG);
956 $result = $this->query($sql);
957 if ($result) {
958 while ($row = $this->fetch_row($result)) {
959 $infotables[] = $row;
960 }
961 }
962 return $infotables;
963 }
964
965 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
978 public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null)
979 {
980 // phpcs:enable
981 // @TODO: $fulltext_keys parameter is unused
982
983 $sqlk = array();
984 $sqluq = array();
985
986 // Keys found into the array $fields: type,value,attribute,null,default,extra
987 // ex. : $fields['rowid'] = array(
988 // 'type'=>'int' or 'integer',
989 // 'value'=>'11',
990 // 'null'=>'not null',
991 // 'extra'=> 'auto_increment'
992 // );
993 $sql = "CREATE TABLE ".$this->sanitize($table)."(";
994 $i = 0;
995 $sqlfields = array();
996 foreach ($fields as $field_name => $field_desc) {
997 $sqlfields[$i] = $this->sanitize($field_name)." ";
998 $sqlfields[$i] .= $this->sanitize($field_desc['type']);
999 if (!is_null($field_desc['value']) && $field_desc['value'] !== '') {
1000 $sqlfields[$i] .= "(".$this->sanitize($field_desc['value']).")";
1001 }
1002 if (!is_null($field_desc['attribute']) && $field_desc['attribute'] !== '') {
1003 $sqlfields[$i] .= " ".$this->sanitize($field_desc['attribute']);
1004 }
1005 if (!is_null($field_desc['default']) && $field_desc['default'] !== '') {
1006 if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1007 $sqlfields[$i] .= " DEFAULT ".((float) $field_desc['default']);
1008 } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') {
1009 $sqlfields[$i] .= " DEFAULT ".$this->sanitize($field_desc['default']);
1010 } else {
1011 $sqlfields[$i] .= " DEFAULT '".$this->escape($field_desc['default'])."'";
1012 }
1013 }
1014 if (!is_null($field_desc['null']) && $field_desc['null'] !== '') {
1015 $sqlfields[$i] .= " ".$this->sanitize($field_desc['null'], 0, 0, 1);
1016 }
1017 if (!is_null($field_desc['extra']) && $field_desc['extra'] !== '') {
1018 $sqlfields[$i] .= " ".$this->sanitize($field_desc['extra'], 0, 0, 1);
1019 }
1020 $i++;
1021 }
1022 if ($primary_key != "") {
1023 $sanitizedpk = "PRIMARY KEY(".$this->sanitize($primary_key).")";
1024 } else {
1025 $sanitizedpk = "";
1026 }
1027
1028
1029 if (is_array($unique_keys)) {
1030 $i = 0;
1031 foreach ($unique_keys as $key => $value) {
1032 $sqluq[$i] = "UNIQUE KEY '".$this->sanitize($key)."' ('".$this->escape($value)."')";
1033 $i++;
1034 }
1035 }
1036 if (is_array($keys)) {
1037 $i = 0;
1038 foreach ($keys as $key => $value) {
1039 $sqlk[$i] = "KEY ".$this->sanitize($key)." (".$value.")";
1040 $i++;
1041 }
1042 }
1043 $sql .= implode(',', $sqlfields);
1044 if ($primary_key != "") {
1045 $sql .= ",".$sanitizedpk;
1046 }
1047 if ($unique_keys != "") {
1048 $sql .= ",".implode(',', $sqluq);
1049 }
1050 if (is_array($keys)) {
1051 $sql .= ",".implode(',', $sqlk);
1052 }
1053 $sql .= ")";
1054 //$sql .= " engine=".$this->sanitize($type);
1055
1056 if (!$this->query($sql)) {
1057 return -1;
1058 } else {
1059 return 1;
1060 }
1061 }
1062
1063 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1070 public function DDLDropTable($table)
1071 {
1072 // phpcs:enable
1073 $sanitizedtmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table);
1074
1075 $sql = "DROP TABLE ".$sanitizedtmptable;
1076
1077 if (!$this->query($sql)) {
1078 return -1;
1079 } else {
1080 return 1;
1081 }
1082 }
1083
1084 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1092 public function DDLDescTable($table, $field = "")
1093 {
1094 // phpcs:enable
1095 $sql = "DESC ".$this->sanitize($table)." ".$this->sanitize($field);
1096
1097 dol_syslog(get_class($this)."::DDLDescTable ".$sql, LOG_DEBUG);
1098 $this->_results = $this->query($sql);
1099 return $this->_results;
1100 }
1101
1102 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1112 public function DDLAddField($table, $field_name, $field_desc, $field_position = "")
1113 {
1114 // phpcs:enable
1115 // keys looked up in the descriptions array (field_desc): type,value,attribute,null,default,extra
1116 // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
1117 $sql = "ALTER TABLE ".$this->sanitize($table)." ADD ".$this->sanitize($field_name)." ";
1118
1119 if ($field_desc['type'] !== 'datetimegmt') {
1120 $sql .= $this->sanitize($field_desc['type']);
1121 } else {
1122 $sql .= 'datetime';
1123 }
1124
1125 if (in_array($field_desc['type'], array('double', 'int', 'varchar')) && array_key_exists('value', $field_desc) && !empty($field_desc['value'])) {
1126 $sql .= "(".$this->sanitize($field_desc['value']).")";
1127 }
1128 if (isset($field_desc['attribute']) && preg_match("/^[^\s]/i", $field_desc['attribute'])) {
1129 $sql .= " ".$this->sanitize($field_desc['attribute']);
1130 }
1131 if (isset($field_desc['null']) && preg_match("/^[^\s]/i", $field_desc['null'])) {
1132 if ($field_desc['null'] == 'NOT NULL') {
1133 $sql .= " ".$this->sanitize($field_desc['null'], 0, 0, 1);
1134 } else {
1135 $sql .= " ".$this->sanitize($field_desc['null']);
1136 }
1137 }
1138 if (isset($field_desc['default']) && preg_match("/^[^\s]/i", $field_desc['default'])) {
1139 if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1140 $sql .= " DEFAULT ".((float) $field_desc['default']);
1141 } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') {
1142 $sql .= " DEFAULT ".$this->sanitize($field_desc['default']);
1143 } else {
1144 $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'";
1145 }
1146 }
1147 if (isset($field_desc['extra']) && preg_match("/^[^\s]/i", $field_desc['extra'])) {
1148 $sql .= " ".$this->sanitize($field_desc['extra'], 0, 0, 1);
1149 }
1150 $sql .= " ".$this->sanitize($field_position, 0, 0, 1);
1151
1152 dol_syslog(get_class($this)."::DDLAddField ".$sql, LOG_DEBUG);
1153 if (!$this->query($sql)) {
1154 return -1;
1155 }
1156 return 1;
1157 }
1158
1159 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1168 public function DDLUpdateField($table, $field_name, $field_desc)
1169 {
1170 // phpcs:enable
1171 $sql = "ALTER TABLE ".$this->sanitize($table);
1172 $sql .= " MODIFY COLUMN ".$this->sanitize($field_name)." ";
1173
1174 if ($field_desc['type'] !== 'datetimegmt') {
1175 $sql .= $this->sanitize($field_desc['type']);
1176 } else {
1177 $sql .= 'datetime';
1178 }
1179
1180 if (in_array($field_desc['type'], array('double', 'int', 'varchar')) && array_key_exists('value', $field_desc) && !empty($field_desc['value'])) {
1181 $sql .= "(".$this->sanitize($field_desc['value']).")";
1182 }
1183
1184 dol_syslog(get_class($this)."::DDLUpdateField ".$sql, LOG_DEBUG);
1185 if (!$this->query($sql)) {
1186 return -1;
1187 }
1188 return 1;
1189 }
1190
1191 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1199 public function DDLDropField($table, $field_name)
1200 {
1201 // phpcs:enable
1202 $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name);
1203
1204 $sql = "ALTER TABLE ".$this->sanitize($table)." DROP COLUMN `".$this->sanitize($tmp_field_name)."`";
1205 if (!$this->query($sql)) {
1206 $this->error = $this->lasterror();
1207 return -1;
1208 }
1209 return 1;
1210 }
1211
1212
1213 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1223 public function DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name)
1224 {
1225 // phpcs:enable
1226 $sql = "INSERT INTO user ";
1227 $sql .= "(Host,User,password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Index_Priv,Alter_priv,Lock_tables_priv)";
1228 $sql .= " VALUES ('".$this->escape($dolibarr_main_db_host)."','".$this->escape($dolibarr_main_db_user)."',password('".$this->escape($dolibarr_main_db_pass)."')";
1229 $sql .= ",'Y','Y','Y','Y','Y','Y','Y','Y','Y')";
1230
1231 dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log
1232 $resql = $this->query($sql);
1233 if (!$resql) {
1234 return -1;
1235 }
1236
1237 $sql = "INSERT INTO db ";
1238 $sql .= "(Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Index_Priv,Alter_priv,Lock_tables_priv)";
1239 $sql .= " VALUES ('".$this->escape($dolibarr_main_db_host)."','".$this->escape($dolibarr_main_db_name)."','".$this->escape($dolibarr_main_db_user)."'";
1240 $sql .= ",'Y','Y','Y','Y','Y','Y','Y','Y','Y')";
1241
1242 dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG);
1243 $resql = $this->query($sql);
1244 if (!$resql) {
1245 return -1;
1246 }
1247
1248 $sql = "FLUSH Privileges";
1249
1250 dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG);
1251 $resql = $this->query($sql);
1252 if (!$resql) {
1253 return -1;
1254 }
1255 return 1;
1256 }
1257
1263 public function getDefaultCharacterSetDatabase()
1264 {
1265 return 'UTF-8';
1266 }
1267
1273 public function getListOfCharacterSet()
1274 {
1275 $liste = array();
1276 $i = 0;
1277 $liste[$i]['charset'] = 'UTF-8';
1278 $liste[$i]['description'] = 'UTF-8';
1279 return $liste;
1280 }
1281
1287 public function getDefaultCollationDatabase()
1288 {
1289 return 'UTF-8';
1290 }
1291
1297 public function getListOfCollation()
1298 {
1299 $liste = array();
1300 $i = 0;
1301 $liste[$i]['collation'] = 'UTF-8';
1302 return $liste;
1303 }
1304
1310 public function getPathOfDump()
1311 {
1312 // FIXME: not for SQLite
1313 $fullpathofdump = '/pathtomysqldump/mysqldump';
1314
1315 $resql = $this->query("SHOW VARIABLES LIKE 'basedir'");
1316 if ($resql) {
1317 $liste = $this->fetch_array($resql);
1318 $basedir = $liste['Value'];
1319 $fullpathofdump = $basedir.(preg_match('/\/$/', $basedir) ? '' : '/').'bin/mysqldump';
1320 }
1321 return $fullpathofdump;
1322 }
1323
1329 public function getPathOfRestore()
1330 {
1331 // FIXME: not for SQLite
1332 $fullpathofimport = '/pathtomysql/mysql';
1333
1334 $resql = $this->query("SHOW VARIABLES LIKE 'basedir'");
1335 if ($resql) {
1336 $liste = $this->fetch_array($resql);
1337 $basedir = $liste['Value'];
1338 $fullpathofimport = $basedir.(preg_match('/\/$/', $basedir) ? '' : '/').'bin/mysql';
1339 }
1340 return $fullpathofimport;
1341 }
1342
1349 public function getServerParametersValues($filter = '')
1350 {
1351 $result = array();
1352 static $pragmas;
1353 if (!isset($pragmas)) {
1354 // Define the list of pragmas used that return only a single value
1355 // independent of the database.
1356 // cf. http://www.sqlite.org/pragma.html
1357 $pragmas = array(
1358 'application_id', 'auto_vacuum', 'automatic_index', 'busy_timeout', 'cache_size',
1359 'cache_spill', 'case_sensitive_like', 'checkpoint_fullsync', 'collation_list',
1360 'compile_options', 'data_version', /*'database_list',*/
1361 'defer_foreign_keys', 'encoding', 'foreign_key_check', 'freelist_count',
1362 'full_column_names', 'fullsync', 'ingore_check_constraints', 'integrity_check',
1363 'journal_mode', 'journal_size_limit', 'legacy_file_format', 'locking_mode',
1364 'max_page_count', 'page_count', 'page_size', 'parser_trace',
1365 'query_only', 'quick_check', 'read_uncommitted', 'recursive_triggers',
1366 'reverse_unordered_selects', 'schema_version', 'user_version',
1367 'secure_delete', 'short_column_names', 'shrink_memory', 'soft_heap_limit',
1368 'synchronous', 'temp_store', /*'temp_store_directory',*/ 'threads',
1369 'vdbe_addoptrace', 'vdbe_debug', 'vdbe_listing', 'vdbe_trace',
1370 'wal_autocheckpoint',
1371 );
1372 }
1373
1374 // TODO prendre en compte le filtre
1375 foreach ($pragmas as $var) {
1376 $sql = "PRAGMA $var"; // @phan-suppress-current-line SqlInjection
1377 $resql = $this->query($sql);
1378 if ($resql) {
1379 $obj = $this->fetch_row($resql);
1380 //dol_syslog(get_class($this)."::select_db getServerParametersValues $var=". print_r($obj, true), LOG_DEBUG);
1381 $result[$var] = $obj[0];
1382 } else {
1383 // TODO Retrieve the message
1384 $result[$var] = 'FAIL';
1385 }
1386 }
1387 return $result;
1388 }
1389
1396 public function getServerStatusValues($filter = '')
1397 {
1398 $result = array();
1399 /*
1400 $sql='SHOW STATUS';
1401 if ($filter) {
1402 $sql.=" LIKE '".$this->escape($filter)."'";
1403 }
1404 $resql=$this->query($sql);
1405 if ($resql)
1406 {
1407 while ($obj=$this->fetch_object($resql)) $result[$obj->Variable_name]=$obj->Value;
1408 }
1409 */
1410
1411 return $result;
1412 }
1413
1425 private function addCustomFunction($name, $arg_count = -1)
1426 {
1427 if ($this->db) {
1428 $newname = preg_replace('/_/', '', $name);
1429 $localname = __CLASS__.'::db'.$newname;
1430 $reflectClass = new ReflectionClass(__CLASS__);
1431 $reflectFunction = $reflectClass->getMethod('db'.$newname);
1432 if ($arg_count < 0) {
1433 $arg_count = $reflectFunction->getNumberOfParameters();
1434 }
1435 if (!$this->db->createFunction($name, $localname, $arg_count)) {
1436 $this->error = "unable to create custom function '$name'";
1437 }
1438 }
1439 }
1440
1448 public function prepare($sql)
1449 {
1450 $sql = $this->convertSQLFromMysql($sql);
1451
1452 dol_syslog(get_class($this)."::prepare sql=".$sql, LOG_DEBUG);
1453
1454 try {
1455 $stmt = $this->db->prepare($sql);
1456 } catch (Throwable $e) {
1457 $stmt = false;
1458 $this->error = $e->getMessage();
1459 }
1460 if (!($stmt instanceof SQLite3Stmt)) {
1461 $this->lasterror = $this->error ? $this->error : $this->db->lastErrorMsg();
1462 $this->lastqueryerror = $sql;
1463 return false;
1464 }
1465 // Keep the query text so num_rows()/affected_rows() can tell a SELECT from the rest
1466 $this->queryString = $sql;
1467
1468 return $stmt;
1469 }
1470
1480 public function execute($stmt, $params = array())
1481 {
1482 if (!($stmt instanceof SQLite3Stmt)) {
1483 $this->lasterror = 'execute() called with an invalid statement';
1484 return false;
1485 }
1486
1487 $this->lasterror = '';
1488 $this->error = '';
1489
1490 $i = 1;
1491 foreach (array_values($params) as $v) {
1492 if (is_int($v) || is_bool($v)) {
1493 $stmt->bindValue($i, (int) $v, SQLITE3_INTEGER);
1494 } elseif (is_float($v)) {
1495 $stmt->bindValue($i, $v, SQLITE3_FLOAT);
1496 } elseif (is_null($v)) {
1497 $stmt->bindValue($i, null, SQLITE3_NULL);
1498 } else {
1499 $stmt->bindValue($i, (string) $v, SQLITE3_TEXT);
1500 }
1501 $i++;
1502 }
1503
1504 dol_syslog(get_class($this)."::execute (".($i - 1)." bound param(s))", LOG_DEBUG);
1505
1506 try {
1507 $res = $stmt->execute();
1508 } catch (Throwable $e) {
1509 $res = false;
1510 $this->error = $e->getMessage();
1511 }
1512 if (!($res instanceof SQLite3Result)) {
1513 $this->lasterror = $this->error ? $this->error : $this->db->lastErrorMsg();
1514 return false;
1515 }
1516
1517 $this->_results = $res;
1518
1519 return ($res->numColumns() > 0) ? $res : true;
1520 }
1521
1522 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1523 /* Unused/commented
1524 * calc_daynr
1525 *
1526 * param int $year Year
1527 * param int $month Month
1528 * param int $day Day
1529 * return int Formatted date
1530 */
1531 /*
1532 private static function calc_daynr($year, $month, $day)
1533 {
1534 // phpcs:enable
1535 $y = $year;
1536 if ($y == 0 && $month == 0) {
1537 return 0;
1538 }
1539 $num = (365 * $y + 31 * ($month - 1) + $day);
1540 if ($month <= 2) {
1541 $y--;
1542 } else {
1543 $num -= floor(($month * 4 + 23) / 10);
1544 }
1545 $temp = floor(($y / 100 + 1) * 3 / 4);
1546 return (int) ($num + floor($y / 4) - $temp);
1547 }
1548 */
1549
1550 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1551 /* Unused/commented
1552 * calc_weekday
1553 *
1554 * param int $daynr ???
1555 * param bool $sunday_first_day_of_week ???
1556 * return int
1557 */
1558 /*
1559 private static function calc_weekday($daynr, $sunday_first_day_of_week)
1560 {
1561 // phpcs:enable
1562 $ret = (int) floor(($daynr + 5 + ($sunday_first_day_of_week ? 1 : 0)) % 7);
1563 return $ret;
1564 }
1565 */
1566
1567 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1568 /* Unused/commented
1569 * calc_days_in_year
1570 *
1571 * param int $year Year
1572 * return int Nb of days in year
1573 */
1574 /*
1575 private static function calc_days_in_year($year)
1576 {
1577 // phpcs:enable
1578 return (($year & 3) == 0 && ($year % 100 || ($year % 400 == 0 && $year)) ? 366 : 365);
1579 }
1580 */
1581
1582 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1583 /* Unused/commented
1584 * calc_week
1585 *
1586 * param int $year Year
1587 * param int $month Month
1588 * param int $day Day
1589 * param int $week_behaviour Week behaviour, bit masks: WEEK_MONDAY_FIRST, WEEK_YEAR, WEEK_FIRST_WEEKDAY
1590 * param int $calc_year ??? Year where the week started
1591 * return int ??? Week number in year
1592 */
1593 /*
1594 private static function calc_week($year, $month, $day, $week_behaviour, &$calc_year)
1595 {
1596 // phpcs:enable
1597 $daynr = self::calc_daynr($year, $month, $day);
1598 $first_daynr = self::calc_daynr($year, 1, 1);
1599 $monday_first = ($week_behaviour & self::WEEK_MONDAY_FIRST) ? 1 : 0;
1600 $week_year = ($week_behaviour & self::WEEK_YEAR) ? 1 : 0;
1601 $first_weekday = ($week_behaviour & self::WEEK_FIRST_WEEKDAY) ? 1 : 0;
1602
1603 $weekday = self::calc_weekday($first_daynr, !$monday_first);
1604 $calc_year = $year;
1605
1606 if ($month == 1 && $day <= 7 - $weekday) {
1607 if (!$week_year && (($first_weekday && $weekday != 0) || (!$first_weekday && $weekday >= 4))) {
1608 return 0;
1609 }
1610 $week_year = 1;
1611 $calc_year--;
1612 $first_daynr -= ($days = self::calc_days_in_year($calc_year));
1613 $weekday = ($weekday + 53 * 7 - $days) % 7;
1614 }
1615
1616 if (($first_weekday && $weekday != 0) || (!$first_weekday && $weekday >= 4)) {
1617 $days = $daynr - ($first_daynr + (7 - $weekday));
1618 } else {
1619 $days = $daynr - ($first_daynr - $weekday);
1620 }
1621
1622 if ($week_year && $days >= 52 * 7) {
1623 $weekday = ($weekday + self::calc_days_in_year($calc_year)) % 7;
1624 if ((!$first_weekday && $weekday < 4) || ($first_weekday && $weekday == 0)) {
1625 $calc_year++;
1626 return 1;
1627 }
1628 }
1629 return (int) floor($days / 7 + 1);
1630 }
1631 */
1632}
foreach( $object->fields as $key=> $val)
@phan-var-force array<string, array{label:string, data-html:string, disable?:int, css?...
Definition list.php:113
$propal type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition propal.php:280
Class to manage Dolibarr database access.
lastqueryerror()
Return last query in error.
lasterror()
Return last error label.
lasterrno()
Return last error code.
lastquery()
Return last request executed with query()
Class to manage Dolibarr database access for a SQLite database.
fetch_object($resultset)
Returns the current line (as an object) for the resultset cursor.
escape($stringtoencode)
Escape a string to insert data.
__construct($type, $host, $user, $pass, $name='', $port=0, $forcenew=false)
Constructor.
fetch_array($resultset)
Return datas as an array.
query($query, $usesavepoint=0, $type='auto', $result_mode=0)
Execute a SQL request and return the resultset.
error()
Renvoie le texte de l'erreur mysql de l'operation precedente.
fetch_row($resultset)
Return datas as an array.
errno()
Renvoie le code erreur generique de l'operation precedente.
const LABEL
Database label.
close()
Close database connection.
execute($stmt, $params=array())
Execute a statement previously created with prepare().
escapeforlike($stringtoencode)
Escape a string to insert data into a like.
encrypt($fieldorvalue, $withQuotes=1)
Encrypt sensitive data in database Warning: This function includes the escape and add the SQL simple ...
last_insert_id($tab, $fieldid='rowid')
Get last ID after an insert INSERT.
affected_rows($resultset)
Return number of lines for result of a SELECT.
decrypt($value)
Decrypt sensitive data in database.
getDriverInfo()
Return version of database client driver.
addCustomFunction($name, $arg_count=-1)
Add a custom function in the database engine (STORED PROCEDURE) Notes:
select_db($database)
Select a database.
num_rows($resultset)
Return number of lines for result of a SELECT.
convertSQLFromMysql($line, $type='ddl')
Convert a SQL request in Mysql syntax to native syntax.
getVersion()
Return version of database server.
free($resultset=null)
Free last resultset used.
const VERSIONMIN
Version min database.
connect($host, $login, $passwd, $name, $port=0, $forcenew=false)
Connection to server.
$type
Database type.
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.