sqlite.class.php 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  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@capnetworks.com>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. /**
  22. * \file htdocs/core/db/sqlite.class.php
  23. * \brief Class file to manage Dolibarr database access for a Sqlite database
  24. */
  25. require_once DOL_DOCUMENT_ROOT .'/core/db/DoliDB.class.php';
  26. /**
  27. * Class to manage Dolibarr database access for a Sqlite database
  28. */
  29. class DoliDBSqlite extends DoliDB
  30. {
  31. //! Database type
  32. public $type='sqlite';
  33. //! Database label
  34. const LABEL='PDO Sqlite';
  35. //! Version min database
  36. const VERSIONMIN='3.0.0';
  37. /** @var PDOStatement Resultset of last query */
  38. private $_results;
  39. /**
  40. * Constructor.
  41. * This create an opened connexion to a database server and eventually to a database
  42. *
  43. * @param string $type Type of database (mysql, pgsql...)
  44. * @param string $host Address of database server
  45. * @param string $user Nom de l'utilisateur autorise
  46. * @param string $pass Mot de passe
  47. * @param string $name Nom de la database
  48. * @param int $port Port of database server
  49. * @return int 1 if OK, 0 if not
  50. */
  51. function __construct($type, $host, $user, $pass, $name='', $port=0)
  52. {
  53. global $conf;
  54. // Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
  55. if (! empty($conf->db->character_set)) $this->forcecharset=$conf->db->character_set;
  56. if (! empty($conf->db->dolibarr_main_db_collation)) $this->forcecollate=$conf->db->dolibarr_main_db_collation;
  57. $this->database_user=$user;
  58. $this->database_host=$host;
  59. $this->database_port=$port;
  60. $this->transaction_opened=0;
  61. //print "Name DB: $host,$user,$pass,$name<br>";
  62. /*if (! function_exists("sqlite_query"))
  63. {
  64. $this->connected = false;
  65. $this->ok = false;
  66. $this->error="Sqlite PHP functions for using Sqlite driver are not available in this version of PHP. Try to use another driver.";
  67. dol_syslog(get_class($this)."::DoliDBSqlite : Sqlite PHP functions for using Sqlite driver are not available in this version of PHP. Try to use another driver.",LOG_ERR);
  68. return $this->ok;
  69. }*/
  70. /*if (! $host)
  71. {
  72. $this->connected = false;
  73. $this->ok = false;
  74. $this->error=$langs->trans("ErrorWrongHostParameter");
  75. dol_syslog(get_class($this)."::DoliDBSqlite : Erreur Connect, wrong host parameters",LOG_ERR);
  76. return $this->ok;
  77. }*/
  78. // Essai connexion serveur
  79. // We do not try to connect to database, only to server. Connect to database is done later in constrcutor
  80. $this->db = $this->connect($host, $user, $pass, $name, $port);
  81. if ($this->db)
  82. {
  83. $this->connected = true;
  84. $this->ok = true;
  85. $this->database_selected = true;
  86. $this->database_name = $name;
  87. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  88. }
  89. else
  90. {
  91. // host, login ou password incorrect
  92. $this->connected = false;
  93. $this->ok = false;
  94. $this->database_selected = false;
  95. $this->database_name = '';
  96. //$this->error=sqlite_connect_error();
  97. dol_syslog(get_class($this)."::DoliDBSqlite : Erreur Connect ".$this->error,LOG_ERR);
  98. }
  99. return (int) $this->ok;
  100. }
  101. /**
  102. * Convert a SQL request in Mysql syntax to native syntax
  103. *
  104. * @param string $line SQL request line to convert
  105. * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
  106. * @return string SQL request line converted
  107. */
  108. static function convertSQLFromMysql($line,$type='ddl')
  109. {
  110. // Removed empty line if this is a comment line for SVN tagging
  111. if (preg_match('/^--\s\$Id/i',$line)) {
  112. return '';
  113. }
  114. // Return line if this is a comment
  115. if (preg_match('/^#/i',$line) || preg_match('/^$/i',$line) || preg_match('/^--/i',$line))
  116. {
  117. return $line;
  118. }
  119. if ($line != "")
  120. {
  121. if ($type == 'auto')
  122. {
  123. if (preg_match('/ALTER TABLE/i',$line)) $type='dml';
  124. else if (preg_match('/CREATE TABLE/i',$line)) $type='dml';
  125. else if (preg_match('/DROP TABLE/i',$line)) $type='dml';
  126. }
  127. if ($type == 'dml')
  128. {
  129. $line=preg_replace('/\s/',' ',$line); // Replace tabulation with space
  130. // we are inside create table statement so lets process datatypes
  131. if (preg_match('/(ISAM|innodb)/i',$line)) { // end of create table sequence
  132. $line=preg_replace('/\)[\s\t]*type[\s\t]*=[\s\t]*(MyISAM|innodb);/i',');',$line);
  133. $line=preg_replace('/\)[\s\t]*engine[\s\t]*=[\s\t]*(MyISAM|innodb);/i',');',$line);
  134. $line=preg_replace('/,$/','',$line);
  135. }
  136. // Process case: "CREATE TABLE llx_mytable(rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,code..."
  137. if (preg_match('/[\s\t\(]*(\w*)[\s\t]+int.*auto_increment/i',$line,$reg)) {
  138. $newline=preg_replace('/([\s\t\(]*)([a-zA-Z_0-9]*)[\s\t]+int.*auto_increment[^,]*/i','\\1 \\2 SERIAL PRIMARY KEY',$line);
  139. //$line = "-- ".$line." replaced by --\n".$newline;
  140. $line=$newline;
  141. }
  142. // tinyint type conversion
  143. $line=str_replace('tinyint','smallint',$line);
  144. // nuke unsigned
  145. $line=preg_replace('/(int\w+|smallint)\s+unsigned/i','\\1',$line);
  146. // blob -> text
  147. $line=preg_replace('/\w*blob/i','text',$line);
  148. // tinytext/mediumtext -> text
  149. $line=preg_replace('/tinytext/i','text',$line);
  150. $line=preg_replace('/mediumtext/i','text',$line);
  151. // change not null datetime field to null valid ones
  152. // (to support remapping of "zero time" to null
  153. $line=preg_replace('/datetime not null/i','datetime',$line);
  154. $line=preg_replace('/datetime/i','timestamp',$line);
  155. // double -> numeric
  156. $line=preg_replace('/^double/i','numeric',$line);
  157. $line=preg_replace('/(\s*)double/i','\\1numeric',$line);
  158. // float -> numeric
  159. $line=preg_replace('/^float/i','numeric',$line);
  160. $line=preg_replace('/(\s*)float/i','\\1numeric',$line);
  161. // unique index(field1,field2)
  162. if (preg_match('/unique index\s*\((\w+\s*,\s*\w+)\)/i',$line))
  163. {
  164. $line=preg_replace('/unique index\s*\((\w+\s*,\s*\w+)\)/i','UNIQUE\(\\1\)',$line);
  165. }
  166. // We remove end of requests "AFTER fieldxxx"
  167. $line=preg_replace('/AFTER [a-z0-9_]+/i','',$line);
  168. // We remove start of requests "ALTER TABLE tablexxx" if this is a DROP INDEX
  169. $line=preg_replace('/ALTER TABLE [a-z0-9_]+ DROP INDEX/i','DROP INDEX',$line);
  170. // Translate order to rename fields
  171. if (preg_match('/ALTER TABLE ([a-z0-9_]+) CHANGE(?: COLUMN)? ([a-z0-9_]+) ([a-z0-9_]+)(.*)$/i',$line,$reg))
  172. {
  173. $line = "-- ".$line." replaced by --\n";
  174. $line.= "ALTER TABLE ".$reg[1]." RENAME COLUMN ".$reg[2]." TO ".$reg[3];
  175. }
  176. // Translate order to modify field format
  177. if (preg_match('/ALTER TABLE ([a-z0-9_]+) MODIFY(?: COLUMN)? ([a-z0-9_]+) (.*)$/i',$line,$reg))
  178. {
  179. $line = "-- ".$line." replaced by --\n";
  180. $newreg3=$reg[3];
  181. $newreg3=preg_replace('/ DEFAULT NULL/i','',$newreg3);
  182. $newreg3=preg_replace('/ NOT NULL/i','',$newreg3);
  183. $newreg3=preg_replace('/ NULL/i','',$newreg3);
  184. $newreg3=preg_replace('/ DEFAULT 0/i','',$newreg3);
  185. $newreg3=preg_replace('/ DEFAULT \'[0-9a-zA-Z_@]*\'/i','',$newreg3);
  186. $line.= "ALTER TABLE ".$reg[1]." ALTER COLUMN ".$reg[2]." TYPE ".$newreg3;
  187. // TODO Add alter to set default value or null/not null if there is this in $reg[3]
  188. }
  189. // alter table add primary key (field1, field2 ...) -> We create a unique index instead as dynamic creation of primary key is not supported
  190. // ALTER TABLE llx_dolibarr_modules ADD PRIMARY KEY pk_dolibarr_modules (numero, entity);
  191. if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+PRIMARY\s+KEY\s*(.*)\s*\((.*)$/i',$line,$reg))
  192. {
  193. $line = "-- ".$line." replaced by --\n";
  194. $line.= "CREATE UNIQUE INDEX ".$reg[2]." ON ".$reg[1]."(".$reg[3];
  195. }
  196. // Translate order to drop foreign keys
  197. // ALTER TABLE llx_dolibarr_modules DROP FOREIGN KEY fk_xxx;
  198. if (preg_match('/ALTER\s+TABLE\s*(.*)\s*DROP\s+FOREIGN\s+KEY\s*(.*)$/i',$line,$reg))
  199. {
  200. $line = "-- ".$line." replaced by --\n";
  201. $line.= "ALTER TABLE ".$reg[1]." DROP CONSTRAINT ".$reg[2];
  202. }
  203. // alter table add [unique] [index] (field1, field2 ...)
  204. // ALTER TABLE llx_accountingaccount ADD INDEX idx_accountingaccount_fk_pcg_version (fk_pcg_version)
  205. if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+(UNIQUE INDEX|INDEX|UNIQUE)\s+(.*)\s*\(([\w,\s]+)\)/i',$line,$reg))
  206. {
  207. $fieldlist=$reg[4];
  208. $idxname=$reg[3];
  209. $tablename=$reg[1];
  210. $line = "-- ".$line." replaced by --\n";
  211. $line.= "CREATE ".(preg_match('/UNIQUE/',$reg[2])?'UNIQUE ':'')."INDEX ".$idxname." ON ".$tablename." (".$fieldlist.")";
  212. }
  213. }
  214. // To have postgresql case sensitive
  215. $line=str_replace(' LIKE \'',' ILIKE \'',$line);
  216. // Delete using criteria on other table must not declare twice the deleted table
  217. // DELETE FROM tabletodelete USING tabletodelete, othertable -> DELETE FROM tabletodelete USING othertable
  218. if (preg_match('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i',$line,$reg))
  219. {
  220. if ($reg[1] == $reg[2]) // If same table, we remove second one
  221. {
  222. $line=preg_replace('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i','DELETE FROM \\1 USING \\3', $line);
  223. }
  224. }
  225. // Remove () in the tables in FROM if one table
  226. $line=preg_replace('/FROM\s*\((([a-z_]+)\s+as\s+([a-z_]+)\s*)\)/i','FROM \\1',$line);
  227. //print $line."\n";
  228. // Remove () in the tables in FROM if two table
  229. $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);
  230. //print $line."\n";
  231. // Remove () in the tables in FROM if two table
  232. $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);
  233. //print $line."\n";
  234. //print "type=".$type." newline=".$line."<br>\n";
  235. }
  236. return $line;
  237. }
  238. /**
  239. * Select a database
  240. *
  241. * @param string $database Name of database
  242. * @return boolean true if OK, false if KO
  243. */
  244. function select_db($database)
  245. {
  246. dol_syslog(get_class($this)."::select_db database=".$database, LOG_DEBUG);
  247. // FIXME: sqlite_select_db() does not exist
  248. return sqlite_select_db($this->db,$database);
  249. }
  250. /**
  251. * Connexion to server
  252. *
  253. * @param string $host database server host
  254. * @param string $login login
  255. * @param string $passwd password
  256. * @param string $name name of database (not used for mysql, used for pgsql)
  257. * @param integer $port Port of database server
  258. * @return PDO Database access handler
  259. * @see close
  260. */
  261. function connect($host, $login, $passwd, $name, $port=0)
  262. {
  263. global $main_data_dir;
  264. dol_syslog(get_class($this)."::connect name=".$name,LOG_DEBUG);
  265. $dir=$main_data_dir;
  266. if (empty($dir)) $dir=DOL_DATA_ROOT;
  267. // With sqlite, port must be in connect parameters
  268. //if (! $newport) $newport=3306;
  269. try {
  270. /*** connect to SQLite database ***/
  271. $this->db = new PDO("sqlite:".$dir.'/database_'.$name.'.sdb');
  272. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  273. }
  274. catch(PDOException $e)
  275. {
  276. $this->error='PDO SQLITE '.$e->getMessage().' current dir='.$dir.'/database_'.$name.'.sdb';
  277. return '';
  278. }
  279. //print "Resultat fonction connect: ".$this->db;
  280. return $this->db;
  281. }
  282. /**
  283. * Return version of database server
  284. *
  285. * @return string Version string
  286. */
  287. function getVersion()
  288. {
  289. $resql=$this->query('SELECT sqlite_version() as sqliteversion');
  290. $row=$this->fetch_row($resql);
  291. return $row[0];
  292. }
  293. /**
  294. * Return version of database client driver
  295. *
  296. * @return string Version string
  297. */
  298. function getDriverInfo()
  299. {
  300. return 'sqlite php driver';
  301. }
  302. /**
  303. * Close database connexion
  304. *
  305. * @return bool True if disconnect successfull, false otherwise
  306. * @see connect
  307. */
  308. function close()
  309. {
  310. if ($this->db)
  311. {
  312. if ($this->transaction_opened > 0) dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened,LOG_ERR);
  313. $this->connected=false;
  314. $this->db=null; // Clean this->db
  315. return true;
  316. }
  317. return false;
  318. }
  319. /**
  320. * Execute a SQL request and return the resultset
  321. *
  322. * @param string $query SQL query string
  323. * @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollbock to savepoint if error (this allow to have some request with errors inside global transactions).
  324. * Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints.
  325. * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
  326. * @return PDOStatement Resultset of answer
  327. */
  328. function query($query,$usesavepoint=0,$type='auto')
  329. {
  330. $ret=null;
  331. $query = trim($query);
  332. $this->error = 0;
  333. // Convert MySQL syntax to SQLite syntax
  334. $query=$this->convertSQLFromMysql($query,$type);
  335. //print "After convertSQLFromMysql:\n".$query."<br>\n";
  336. dol_syslog('sql='.$query, LOG_DEBUG);
  337. // Ordre SQL ne necessitant pas de connexion a une base (exemple: CREATE DATABASE)
  338. try {
  339. //$ret = $this->db->exec($query);
  340. $ret = $this->db->query($query); // $ret is a PDO object
  341. }
  342. catch(PDOException $e)
  343. {
  344. $this->error=$e->getMessage();
  345. }
  346. if (! preg_match("/^COMMIT/i",$query) && ! preg_match("/^ROLLBACK/i",$query))
  347. {
  348. // Si requete utilisateur, on la sauvegarde ainsi que son resultset
  349. if (! is_object($ret) || $this->error)
  350. {
  351. $this->lastqueryerror = $query;
  352. $this->lasterror = $this->error();
  353. $this->lasterrno = $this->errno();
  354. dol_syslog(get_class($this)."::query SQL Error query: ".$query, LOG_ERR);
  355. $errormsg = get_class($this)."::query SQL Error message: ".$this->lasterror;
  356. if (preg_match('/[0-9]/',$this->lasterrno)) {
  357. $errormsg .= ' ('.$this->lasterrno.')';
  358. }
  359. dol_syslog($errormsg, LOG_ERR);
  360. }
  361. $this->lastquery=$query;
  362. $this->_results = $ret;
  363. }
  364. return $ret;
  365. }
  366. /**
  367. * Renvoie la ligne courante (comme un objet) pour le curseur resultset
  368. *
  369. * @param PDOStatement $resultset Curseur de la requete voulue
  370. * @return false|object Object result line or false if KO or end of cursor
  371. */
  372. function fetch_object($resultset)
  373. {
  374. // Si le resultset n'est pas fourni, on prend le dernier utilise sur cette connexion
  375. if (! is_object($resultset)) { $resultset=$this->_results; }
  376. return $resultset->fetch(PDO::FETCH_OBJ);
  377. }
  378. /**
  379. * Return datas as an array
  380. *
  381. * @param PDOStatement $resultset Resultset of request
  382. * @return false|array Array or false if KO or end of cursor
  383. */
  384. function fetch_array($resultset)
  385. {
  386. // If resultset not provided, we take the last used by connexion
  387. if (! is_object($resultset)) { $resultset=$this->_results; }
  388. return $resultset->fetch(PDO::FETCH_ASSOC);
  389. }
  390. /**
  391. * Return datas as an array
  392. *
  393. * @param PDOStatement $resultset Resultset of request
  394. * @return false|array Array or false if KO or end of cursor
  395. */
  396. function fetch_row($resultset)
  397. {
  398. // If resultset not provided, we take the last used by connexion
  399. if (! is_bool($resultset))
  400. {
  401. if (! is_object($resultset)) { $resultset=$this->_results; }
  402. return $resultset->fetch(PDO::FETCH_NUM);
  403. }
  404. else
  405. {
  406. // si le curseur est un booleen on retourne la valeur 0
  407. return 0;
  408. }
  409. }
  410. /**
  411. * Return number of lines for result of a SELECT
  412. *
  413. * @param PDOStatement $resultset Resulset of requests
  414. * @return int Nb of lines
  415. * @see affected_rows
  416. */
  417. function num_rows($resultset)
  418. {
  419. // If resultset not provided, we take the last used by connexion
  420. if (! is_object($resultset)) { $resultset=$this->_results; }
  421. return $resultset->rowCount();
  422. }
  423. /**
  424. * Return number of lines for result of a SELECT
  425. *
  426. * @param PDOStatement $resultset Resulset of requests
  427. * @return int Nb of lines
  428. * @see affected_rows
  429. */
  430. function affected_rows($resultset)
  431. {
  432. // If resultset not provided, we take the last used by connexion
  433. if (! is_object($resultset)) { $resultset=$this->_results; }
  434. // mysql necessite un link de base pour cette fonction contrairement
  435. // a pqsql qui prend un resultset
  436. return $resultset->rowCount();
  437. }
  438. /**
  439. * Free last resultset used.
  440. *
  441. * @param PDOStatement $resultset Curseur de la requete voulue
  442. * @return void
  443. */
  444. function free($resultset=null)
  445. {
  446. // If resultset not provided, we take the last used by connexion
  447. if (! is_object($resultset)) { $resultset=$this->_results; }
  448. // Si resultset en est un, on libere la memoire
  449. if (is_object($resultset)) $resultset->closeCursor();
  450. }
  451. /**
  452. * Escape a string to insert data
  453. *
  454. * @param string $stringtoencode String to escape
  455. * @return string String escaped
  456. */
  457. function escape($stringtoencode)
  458. {
  459. return $this->db->quote($stringtoencode);
  460. }
  461. /**
  462. * Renvoie le code erreur generique de l'operation precedente.
  463. *
  464. * @return string Error code (Exemples: DB_ERROR_TABLE_ALREADY_EXISTS, DB_ERROR_RECORD_ALREADY_EXISTS...)
  465. */
  466. function errno()
  467. {
  468. if (! $this->connected) {
  469. // Si il y a eu echec de connexion, $this->db n'est pas valide.
  470. return 'DB_ERROR_FAILED_TO_CONNECT';
  471. }
  472. else {
  473. // Constants to convert error code to a generic Dolibarr error code
  474. /*$errorcode_map = array(
  475. 1004 => 'DB_ERROR_CANNOT_CREATE',
  476. 1005 => 'DB_ERROR_CANNOT_CREATE',
  477. 1006 => 'DB_ERROR_CANNOT_CREATE',
  478. 1007 => 'DB_ERROR_ALREADY_EXISTS',
  479. 1008 => 'DB_ERROR_CANNOT_DROP',
  480. 1025 => 'DB_ERROR_NO_FOREIGN_KEY_TO_DROP',
  481. 1044 => 'DB_ERROR_ACCESSDENIED',
  482. 1046 => 'DB_ERROR_NODBSELECTED',
  483. 1048 => 'DB_ERROR_CONSTRAINT',
  484. 'HY000' => 'DB_ERROR_TABLE_ALREADY_EXISTS',
  485. 1051 => 'DB_ERROR_NOSUCHTABLE',
  486. 1054 => 'DB_ERROR_NOSUCHFIELD',
  487. 1060 => 'DB_ERROR_COLUMN_ALREADY_EXISTS',
  488. 1061 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
  489. 1062 => 'DB_ERROR_RECORD_ALREADY_EXISTS',
  490. 1064 => 'DB_ERROR_SYNTAX',
  491. 1068 => 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS',
  492. 1075 => 'DB_ERROR_CANT_DROP_PRIMARY_KEY',
  493. 1091 => 'DB_ERROR_NOSUCHFIELD',
  494. 1100 => 'DB_ERROR_NOT_LOCKED',
  495. 1136 => 'DB_ERROR_VALUE_COUNT_ON_ROW',
  496. 1146 => 'DB_ERROR_NOSUCHTABLE',
  497. 1216 => 'DB_ERROR_NO_PARENT',
  498. 1217 => 'DB_ERROR_CHILD_EXISTS',
  499. 1451 => 'DB_ERROR_CHILD_EXISTS'
  500. );
  501. if (isset($errorcode_map[$this->db->errorCode()]))
  502. {
  503. return $errorcode_map[$this->db->errorCode()];
  504. }*/
  505. $errno=$this->db->errorCode();
  506. if ($errno=='HY000')
  507. {
  508. if (preg_match('/table.*already exists/i',$this->error)) return 'DB_ERROR_TABLE_ALREADY_EXISTS';
  509. elseif (preg_match('/index.*already exists/i',$this->error)) return 'DB_ERROR_KEY_NAME_ALREADY_EXISTS';
  510. elseif (preg_match('/syntax error/i',$this->error)) return 'DB_ERROR_SYNTAX';
  511. }
  512. if ($errno=='23000')
  513. {
  514. if (preg_match('/column.* not unique/i',$this->error)) return 'DB_ERROR_RECORD_ALREADY_EXISTS';
  515. elseif (preg_match('/PRIMARY KEY must be unique/i',$this->error)) return 'DB_ERROR_RECORD_ALREADY_EXISTS';
  516. }
  517. return ($errno?'DB_ERROR_'.$errno:'0');
  518. }
  519. }
  520. /**
  521. * Renvoie le texte de l'erreur mysql de l'operation precedente.
  522. *
  523. * @return string Error text
  524. */
  525. function error()
  526. {
  527. if (! $this->connected) {
  528. // Si il y a eu echec de connexion, $this->db n'est pas valide pour sqlite_error.
  529. return 'Not connected. Check setup parameters in conf/conf.php file and your sqlite version';
  530. }
  531. else {
  532. return $this->error;
  533. }
  534. }
  535. /**
  536. * Get last ID after an insert INSERT
  537. *
  538. * @param string $tab Table name concerned by insert. Ne sert pas sous MySql mais requis pour compatibilite avec Postgresql
  539. * @param string $fieldid Field name
  540. * @return int Id of row
  541. */
  542. function last_insert_id($tab,$fieldid='rowid')
  543. {
  544. return $this->db->lastInsertId();
  545. }
  546. /**
  547. * Encrypt sensitive data in database
  548. * Warning: This function includes the escape, so it must use direct value
  549. *
  550. * @param string $fieldorvalue Field name or value to encrypt
  551. * @param int $withQuotes Return string with quotes
  552. * @return string XXX(field) or XXX('value') or field or 'value'
  553. */
  554. function encrypt($fieldorvalue, $withQuotes=0)
  555. {
  556. global $conf;
  557. // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
  558. $cryptType = ($conf->db->dolibarr_main_db_encryption?$conf->db->dolibarr_main_db_encryption:0);
  559. //Encryption key
  560. $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey)?$conf->db->dolibarr_main_db_cryptkey:'');
  561. $return = ($withQuotes?"'":"").$this->escape($fieldorvalue).($withQuotes?"'":"");
  562. if ($cryptType && !empty($cryptKey))
  563. {
  564. if ($cryptType == 2)
  565. {
  566. $return = 'AES_ENCRYPT('.$return.',\''.$cryptKey.'\')';
  567. }
  568. else if ($cryptType == 1)
  569. {
  570. $return = 'DES_ENCRYPT('.$return.',\''.$cryptKey.'\')';
  571. }
  572. }
  573. return $return;
  574. }
  575. /**
  576. * Decrypt sensitive data in database
  577. *
  578. * @param string $value Value to decrypt
  579. * @return string Decrypted value if used
  580. */
  581. function decrypt($value)
  582. {
  583. global $conf;
  584. // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
  585. $cryptType = ($conf->db->dolibarr_main_db_encryption?$conf->db->dolibarr_main_db_encryption:0);
  586. //Encryption key
  587. $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey)?$conf->db->dolibarr_main_db_cryptkey:'');
  588. $return = $value;
  589. if ($cryptType && !empty($cryptKey))
  590. {
  591. if ($cryptType == 2)
  592. {
  593. $return = 'AES_DECRYPT('.$value.',\''.$cryptKey.'\')';
  594. }
  595. else if ($cryptType == 1)
  596. {
  597. $return = 'DES_DECRYPT('.$value.',\''.$cryptKey.'\')';
  598. }
  599. }
  600. return $return;
  601. }
  602. /**
  603. * Return connexion ID
  604. *
  605. * @return string Id connexion
  606. */
  607. function DDLGetConnectId()
  608. {
  609. return '?';
  610. }
  611. /**
  612. * Create a new database
  613. * Do not use function xxx_create_db (xxx=mysql, ...) as they are deprecated
  614. * We force to create database with charset this->forcecharset and collate this->forcecollate
  615. *
  616. * @param string $database Database name to create
  617. * @param string $charset Charset used to store data
  618. * @param string $collation Charset used to sort data
  619. * @param string $owner Username of database owner
  620. * @return PDOStatement resource defined if OK, null if KO
  621. */
  622. function DDLCreateDb($database,$charset='',$collation='',$owner='')
  623. {
  624. if (empty($charset)) $charset=$this->forcecharset;
  625. if (empty($collation)) $collation=$this->forcecollate;
  626. // ALTER DATABASE dolibarr_db DEFAULT CHARACTER SET latin DEFAULT COLLATE latin1_swedish_ci
  627. $sql = 'CREATE DATABASE '.$database;
  628. $sql.= ' DEFAULT CHARACTER SET '.$charset.' DEFAULT COLLATE '.$collation;
  629. dol_syslog($sql,LOG_DEBUG);
  630. $ret=$this->query($sql);
  631. if (! $ret)
  632. {
  633. // We try again for compatibility with Mysql < 4.1.1
  634. $sql = 'CREATE DATABASE '.$database;
  635. $ret=$this->query($sql);
  636. dol_syslog($sql,LOG_DEBUG);
  637. }
  638. return $ret;
  639. }
  640. /**
  641. * List tables into a database
  642. *
  643. * @param string $database Name of database
  644. * @param string $table Name of table filter ('xxx%')
  645. * @return array List of tables in an array
  646. */
  647. function DDLListTables($database, $table='')
  648. {
  649. $listtables=array();
  650. $like = '';
  651. if ($table) $like = "LIKE '".$table."'";
  652. $sql="SHOW TABLES FROM ".$database." ".$like.";";
  653. //print $sql;
  654. $result = $this->query($sql);
  655. while($row = $this->fetch_row($result))
  656. {
  657. $listtables[] = $row[0];
  658. }
  659. return $listtables;
  660. }
  661. /**
  662. * List information of columns into a table.
  663. *
  664. * @param string $table Name of table
  665. * @return array Tableau des informations des champs de la table
  666. * TODO modify for sqlite
  667. */
  668. function DDLInfoTable($table)
  669. {
  670. $infotables=array();
  671. $sql="SHOW FULL COLUMNS FROM ".$table.";";
  672. dol_syslog($sql,LOG_DEBUG);
  673. $result = $this->query($sql);
  674. while($row = $this->fetch_row($result))
  675. {
  676. $infotables[] = $row;
  677. }
  678. return $infotables;
  679. }
  680. /**
  681. * Create a table into database
  682. *
  683. * @param string $table Nom de la table
  684. * @param array $fields Tableau associatif [nom champ][tableau des descriptions]
  685. * @param string $primary_key Nom du champ qui sera la clef primaire
  686. * @param string $type Type de la table
  687. * @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
  688. * @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
  689. * @param array $keys Tableau des champs cles noms => valeur
  690. * @return int <0 if KO, >=0 if OK
  691. */
  692. function DDLCreateTable($table,$fields,$primary_key,$type,$unique_keys=null,$fulltext_keys=null,$keys=null)
  693. {
  694. // FIXME: $fulltext_keys parameter is unused
  695. // cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra
  696. // ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
  697. $sql = "create table ".$table."(";
  698. $i=0;
  699. foreach($fields as $field_name => $field_desc)
  700. {
  701. $sqlfields[$i] = $field_name." ";
  702. $sqlfields[$i] .= $field_desc['type'];
  703. if( preg_match("/^[^\s]/i",$field_desc['value']))
  704. $sqlfields[$i] .= "(".$field_desc['value'].")";
  705. else if( preg_match("/^[^\s]/i",$field_desc['attribute']))
  706. $sqlfields[$i] .= " ".$field_desc['attribute'];
  707. else if( preg_match("/^[^\s]/i",$field_desc['default']))
  708. {
  709. if(preg_match("/null/i",$field_desc['default']))
  710. $sqlfields[$i] .= " default ".$field_desc['default'];
  711. else
  712. $sqlfields[$i] .= " default '".$field_desc['default']."'";
  713. }
  714. else if( preg_match("/^[^\s]/i",$field_desc['null']))
  715. $sqlfields[$i] .= " ".$field_desc['null'];
  716. else if( preg_match("/^[^\s]/i",$field_desc['extra']))
  717. $sqlfields[$i] .= " ".$field_desc['extra'];
  718. $i++;
  719. }
  720. if($primary_key != "")
  721. $pk = "primary key(".$primary_key.")";
  722. if(is_array($unique_keys))
  723. {
  724. $i = 0;
  725. foreach($unique_keys as $key => $value)
  726. {
  727. $sqluq[$i] = "UNIQUE KEY '".$key."' ('".$value."')";
  728. $i++;
  729. }
  730. }
  731. if(is_array($keys))
  732. {
  733. $i = 0;
  734. foreach($keys as $key => $value)
  735. {
  736. $sqlk[$i] = "KEY ".$key." (".$value.")";
  737. $i++;
  738. }
  739. }
  740. $sql .= implode(',',$sqlfields);
  741. if($primary_key != "")
  742. $sql .= ",".$pk;
  743. if(is_array($unique_keys))
  744. $sql .= ",".implode(',',$sqluq);
  745. if(is_array($keys))
  746. $sql .= ",".implode(',',$sqlk);
  747. $sql .=") type=".$type;
  748. dol_syslog($sql,LOG_DEBUG);
  749. if(! $this -> query($sql))
  750. return -1;
  751. else
  752. return 1;
  753. }
  754. /**
  755. * Return a pointer of line with description of a table or field
  756. *
  757. * @param string $table Name of table
  758. * @param string $field Optionnel : Name of field if we want description of field
  759. * @return resource Resource
  760. */
  761. function DDLDescTable($table,$field="")
  762. {
  763. $sql="DESC ".$table." ".$field;
  764. dol_syslog(get_class($this)."::DDLDescTable ".$sql,LOG_DEBUG);
  765. $this->_results = $this->query($sql);
  766. return $this->_results;
  767. }
  768. /**
  769. * Create a new field into table
  770. *
  771. * @param string $table Name of table
  772. * @param string $field_name Name of field to add
  773. * @param string $field_desc Tableau associatif de description du champ a inserer[nom du parametre][valeur du parametre]
  774. * @param string $field_position Optionnel ex.: "after champtruc"
  775. * @return int <0 if KO, >0 if OK
  776. */
  777. function DDLAddField($table,$field_name,$field_desc,$field_position="")
  778. {
  779. // cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra
  780. // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
  781. $sql= "ALTER TABLE ".$table." ADD ".$field_name." ";
  782. $sql.= $field_desc['type'];
  783. if(preg_match("/^[^\s]/i",$field_desc['value']))
  784. if (! in_array($field_desc['type'],array('date','datetime')))
  785. {
  786. $sql.= "(".$field_desc['value'].")";
  787. }
  788. if(preg_match("/^[^\s]/i",$field_desc['attribute']))
  789. $sql.= " ".$field_desc['attribute'];
  790. if(preg_match("/^[^\s]/i",$field_desc['null']))
  791. $sql.= " ".$field_desc['null'];
  792. if(preg_match("/^[^\s]/i",$field_desc['default']))
  793. {
  794. if(preg_match("/null/i",$field_desc['default']))
  795. $sql.= " default ".$field_desc['default'];
  796. else
  797. $sql.= " default '".$field_desc['default']."'";
  798. }
  799. if(preg_match("/^[^\s]/i",$field_desc['extra']))
  800. $sql.= " ".$field_desc['extra'];
  801. $sql.= " ".$field_position;
  802. dol_syslog(get_class($this)."::DDLAddField ".$sql,LOG_DEBUG);
  803. if(! $this->query($sql))
  804. {
  805. return -1;
  806. }
  807. else
  808. {
  809. return 1;
  810. }
  811. }
  812. /**
  813. * Update format of a field into a table
  814. *
  815. * @param string $table Name of table
  816. * @param string $field_name Name of field to modify
  817. * @param string $field_desc Array with description of field format
  818. * @return int <0 if KO, >0 if OK
  819. */
  820. function DDLUpdateField($table,$field_name,$field_desc)
  821. {
  822. $sql = "ALTER TABLE ".$table;
  823. $sql .= " MODIFY COLUMN ".$field_name." ".$field_desc['type'];
  824. if ($field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int' || $field_desc['type'] == 'varchar') {
  825. $sql.="(".$field_desc['value'].")";
  826. }
  827. dol_syslog(get_class($this)."::DDLUpdateField ".$sql,LOG_DEBUG);
  828. if (! $this->query($sql))
  829. return -1;
  830. else
  831. return 1;
  832. }
  833. /**
  834. * Drop a field from table
  835. *
  836. * @param string $table Name of table
  837. * @param string $field_name Name of field to drop
  838. * @return int <0 if KO, >0 if OK
  839. */
  840. function DDLDropField($table,$field_name)
  841. {
  842. $sql= "ALTER TABLE ".$table." DROP COLUMN `".$field_name."`";
  843. dol_syslog(get_class($this)."::DDLDropField ".$sql,LOG_DEBUG);
  844. if (! $this->query($sql))
  845. {
  846. $this->error=$this->lasterror();
  847. return -1;
  848. }
  849. else return 1;
  850. }
  851. /**
  852. * Create a user and privileges to connect to database (even if database does not exists yet)
  853. *
  854. * @param string $dolibarr_main_db_host Ip serveur
  855. * @param string $dolibarr_main_db_user Nom user a creer
  856. * @param string $dolibarr_main_db_pass Mot de passe user a creer
  857. * @param string $dolibarr_main_db_name Database name where user must be granted
  858. * @return int <0 if KO, >=0 if OK
  859. */
  860. function DDLCreateUser($dolibarr_main_db_host,$dolibarr_main_db_user,$dolibarr_main_db_pass,$dolibarr_main_db_name)
  861. {
  862. $sql = "INSERT INTO user ";
  863. $sql.= "(Host,User,password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Index_Priv,Alter_priv,Lock_tables_priv)";
  864. $sql.= " VALUES ('".$this->escape($dolibarr_main_db_host)."','".$this->escape($dolibarr_main_db_user)."',password('".addslashes($dolibarr_main_db_pass)."')";
  865. $sql.= ",'Y','Y','Y','Y','Y','Y','Y','Y','Y')";
  866. dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log
  867. $resql=$this->query($sql);
  868. if (! $resql)
  869. {
  870. return -1;
  871. }
  872. $sql = "INSERT INTO db ";
  873. $sql.= "(Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Index_Priv,Alter_priv,Lock_tables_priv)";
  874. $sql.= " VALUES ('".$this->escape($dolibarr_main_db_host)."','".$this->escape($dolibarr_main_db_name)."','".addslashes($dolibarr_main_db_user)."'";
  875. $sql.= ",'Y','Y','Y','Y','Y','Y','Y','Y','Y')";
  876. dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG);
  877. $resql=$this->query($sql);
  878. if (! $resql)
  879. {
  880. return -1;
  881. }
  882. $sql="FLUSH Privileges";
  883. dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG);
  884. $resql=$this->query($sql);
  885. if (! $resql)
  886. {
  887. return -1;
  888. }
  889. return 1;
  890. }
  891. /**
  892. * Return charset used to store data in database
  893. *
  894. * @return string Charset
  895. */
  896. function getDefaultCharacterSetDatabase()
  897. {
  898. return 'UTF-8';
  899. }
  900. /**
  901. * Return list of available charset that can be used to store data in database
  902. *
  903. * @return array List of Charset
  904. */
  905. function getListOfCharacterSet()
  906. {
  907. $liste = array();
  908. $i=0;
  909. $liste[$i]['charset'] = 'UTF-8';
  910. $liste[$i]['description'] = 'UTF-8';
  911. return $liste;
  912. }
  913. /**
  914. * Return collation used in database
  915. *
  916. * @return string Collation value
  917. */
  918. function getDefaultCollationDatabase()
  919. {
  920. return 'UTF-8';
  921. }
  922. /**
  923. * Return list of available collation that can be used for database
  924. *
  925. * @return array List of Collation
  926. */
  927. function getListOfCollation()
  928. {
  929. $liste = array();
  930. $i=0;
  931. $liste[$i]['charset'] = 'UTF-8';
  932. $liste[$i]['description'] = 'UTF-8';
  933. return $liste;
  934. }
  935. /**
  936. * Return full path of dump program
  937. *
  938. * @return string Full path of dump program
  939. */
  940. function getPathOfDump()
  941. {
  942. $fullpathofdump='/pathtomysqldump/mysqldump';
  943. $resql=$this->query('SHOW VARIABLES LIKE \'basedir\'');
  944. if ($resql)
  945. {
  946. $liste=$this->fetch_array($resql);
  947. $basedir=$liste['Value'];
  948. $fullpathofdump=$basedir.(preg_match('/\/$/',$basedir)?'':'/').'bin/mysqldump';
  949. }
  950. return $fullpathofdump;
  951. }
  952. /**
  953. * Return full path of restore program
  954. *
  955. * @return string Full path of restore program
  956. */
  957. function getPathOfRestore()
  958. {
  959. $fullpathofimport='/pathtomysql/mysql';
  960. $resql=$this->query('SHOW VARIABLES LIKE \'basedir\'');
  961. if ($resql)
  962. {
  963. $liste=$this->fetch_array($resql);
  964. $basedir=$liste['Value'];
  965. $fullpathofimport=$basedir.(preg_match('/\/$/',$basedir)?'':'/').'bin/mysql';
  966. }
  967. return $fullpathofimport;
  968. }
  969. /**
  970. * Return value of server parameters
  971. *
  972. * @param string $filter Filter list on a particular value
  973. * @return array Array of key-values (key=>value)
  974. */
  975. function getServerParametersValues($filter='')
  976. {
  977. $result=array();
  978. $sql='SHOW VARIABLES';
  979. if ($filter) $sql.=" LIKE '".$this->escape($filter)."'";
  980. $resql=$this->query($sql);
  981. if ($resql)
  982. {
  983. while ($obj=$this->fetch_object($resql)) $result[$obj->Variable_name]=$obj->Value;
  984. }
  985. return $result;
  986. }
  987. /**
  988. * Return value of server status
  989. *
  990. * @param string $filter Filter list on a particular value
  991. * @return array Array of key-values (key=>value)
  992. */
  993. function getServerStatusValues($filter='')
  994. {
  995. $result=array();
  996. $sql='SHOW STATUS';
  997. if ($filter) $sql.=" LIKE '".$this->escape($filter)."'";
  998. $resql=$this->query($sql);
  999. if ($resql)
  1000. {
  1001. while ($obj=$this->fetch_object($resql)) $result[$obj->Variable_name]=$obj->Value;
  1002. }
  1003. return $result;
  1004. }
  1005. }