sqlite3.class.php 49 KB

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