Passed
Branch develop (5cbde9)
by
unknown
26:38
created

DoliDBSqlite3::DDLCreateDb()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
c 0
b 0
f 0
nc 8
nop 4
dl 0
loc 20
rs 9.9
1
<?php
2
/* Copyright (C) 2001      Fabien Seisen        <[email protected]>
3
 * Copyright (C) 2002-2005 Rodolphe Quiedeville <[email protected]>
4
 * Copyright (C) 2004-2011 Laurent Destailleur  <[email protected]>
5
 * Copyright (C) 2006      Andre Cianfarani     <[email protected]>
6
 * Copyright (C) 2005-2009 Regis Houssin        <[email protected]>
7
 * Copyright (C) 2015      Raphaël Doursenaud   <[email protected]>
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
/**
24
 *	\file       htdocs/core/db/sqlite.class.php
25
 *	\brief      Class file to manage Dolibarr database access for a SQLite database
26
 */
27
28
require_once DOL_DOCUMENT_ROOT .'/core/db/DoliDB.class.php';
29
30
/**
31
 *	Class to manage Dolibarr database access for a SQLite database
32
 */
33
class DoliDBSqlite3 extends DoliDB
34
{
35
    //! Database type
36
    public $type='sqlite3';
37
    //! Database label
38
    const LABEL='Sqlite3';
39
    //! Version min database
40
    const VERSIONMIN='3.0.0';
41
    /** @var SQLite3Result Resultset of last query */
42
    private $_results;
43
44
    const WEEK_MONDAY_FIRST=1;
45
    const WEEK_YEAR = 2;
46
    const WEEK_FIRST_WEEKDAY=4;
47
48
49
    /**
50
     *  Constructor.
51
     *  This create an opened connexion to a database server and eventually to a database
52
     *
53
     *  @param      string	$type		Type of database (mysql, pgsql...)
54
     *  @param	    string	$host		Address of database server
55
     *  @param	    string	$user		Nom de l'utilisateur autorise
56
     *  @param	    string	$pass		Mot de passe
57
     *  @param	    string	$name		Nom de la database
58
     *  @param	    int		$port		Port of database server
59
     */
60
    public function __construct($type, $host, $user, $pass, $name = '', $port = 0)
61
    {
62
        global $conf;
63
64
        // Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
65
        if (! empty($conf->db->character_set)) $this->forcecharset=$conf->db->character_set;
66
        if (! empty($conf->db->dolibarr_main_db_collation)) $this->forcecollate=$conf->db->dolibarr_main_db_collation;
67
68
        $this->database_user=$user;
69
        $this->database_host=$host;
70
        $this->database_port=$port;
71
72
        $this->transaction_opened=0;
73
74
        //print "Name DB: $host,$user,$pass,$name<br>";
75
76
        /*if (! function_exists("sqlite_query"))
77
        {
78
            $this->connected = false;
79
            $this->ok = false;
80
            $this->error="Sqlite PHP functions for using Sqlite driver are not available in this version of PHP. Try to use another driver.";
81
            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);
82
            return $this->ok;
83
        }*/
84
85
        /*if (! $host)
86
        {
87
            $this->connected = false;
88
            $this->ok = false;
89
            $this->error=$langs->trans("ErrorWrongHostParameter");
90
            dol_syslog(get_class($this)."::DoliDBSqlite3 : Erreur Connect, wrong host parameters",LOG_ERR);
91
            return $this->ok;
92
        }*/
93
94
        // Essai connexion serveur
95
        // We do not try to connect to database, only to server. Connect to database is done later in constrcutor
96
        $this->db = $this->connect($host, $user, $pass, $name, $port);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->connect($host, $user, $pass, $name, $port) of type SQLite3 is incompatible with the declared type resource of property $db.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
97
98
        if ($this->db)
99
        {
100
            $this->connected = true;
101
            $this->ok = true;
102
            $this->database_selected = true;
103
            $this->database_name = $name;
104
105
            $this->addCustomFunction('IF');
106
            $this->addCustomFunction('MONTH');
107
            $this->addCustomFunction('CURTIME');
108
            $this->addCustomFunction('CURDATE');
109
            $this->addCustomFunction('WEEK', 1);
110
            $this->addCustomFunction('WEEK', 2);
111
            $this->addCustomFunction('WEEKDAY');
112
            $this->addCustomFunction('date_format');
113
            //$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
114
        }
115
        else
116
        {
117
            // host, login ou password incorrect
118
            $this->connected = false;
119
            $this->ok = false;
120
            $this->database_selected = false;
121
            $this->database_name = '';
122
            //$this->error=sqlite_connect_error();
123
            dol_syslog(get_class($this)."::DoliDBSqlite3 : Error Connect ".$this->error, LOG_ERR);
124
        }
125
126
        return $this->ok;
127
    }
128
129
130
    /**
131
     *  Convert a SQL request in Mysql syntax to native syntax
132
     *
133
     *  @param     string	$line   SQL request line to convert
134
     *  @param     string	$type	Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
135
     *  @return    string   		SQL request line converted
136
     */
137
    public static function convertSQLFromMysql($line, $type = 'ddl')
138
    {
139
        // Removed empty line if this is a comment line for SVN tagging
140
        if (preg_match('/^--\s\$Id/i', $line)) {
141
            return '';
142
        }
143
        // Return line if this is a comment
144
        if (preg_match('/^#/i', $line) || preg_match('/^$/i', $line) || preg_match('/^--/i', $line))
145
        {
146
            return $line;
147
        }
148
        if ($line != "")
149
        {
150
            if ($type == 'auto')
151
            {
152
              if (preg_match('/ALTER TABLE/i', $line)) $type='dml';
153
              elseif (preg_match('/CREATE TABLE/i', $line)) $type='dml';
154
              elseif (preg_match('/DROP TABLE/i', $line)) $type='dml';
155
            }
156
157
            if ($type == 'dml')
158
            {
159
                $line=preg_replace('/\s/', ' ', $line);   // Replace tabulation with space
160
161
                // we are inside create table statement so lets process datatypes
162
                if (preg_match('/(ISAM|innodb)/i', $line)) { // end of create table sequence
163
                    $line=preg_replace('/\)[\s\t]*type[\s\t]*=[\s\t]*(MyISAM|innodb);/i', ');', $line);
164
                    $line=preg_replace('/\)[\s\t]*engine[\s\t]*=[\s\t]*(MyISAM|innodb);/i', ');', $line);
165
                    $line=preg_replace('/,$/', '', $line);
166
                }
167
168
                // Process case: "CREATE TABLE llx_mytable(rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,code..."
169
                if (preg_match('/[\s\t\(]*(\w*)[\s\t]+int.*auto_increment/i', $line, $reg)) {
170
                    $newline=preg_replace('/([\s\t\(]*)([a-zA-Z_0-9]*)[\s\t]+int.*auto_increment[^,]*/i', '\\1 \\2 integer PRIMARY KEY AUTOINCREMENT', $line);
171
                    //$line = "-- ".$line." replaced by --\n".$newline;
172
                    $line=$newline;
173
                }
174
175
                // tinyint type conversion
176
                $line=str_replace('tinyint', 'smallint', $line);
177
178
                // nuke unsigned
179
                $line=preg_replace('/(int\w+|smallint)\s+unsigned/i', '\\1', $line);
180
181
                // blob -> text
182
                $line=preg_replace('/\w*blob/i', 'text', $line);
183
184
                // tinytext/mediumtext -> text
185
                $line=preg_replace('/tinytext/i', 'text', $line);
186
                $line=preg_replace('/mediumtext/i', 'text', $line);
187
188
                // change not null datetime field to null valid ones
189
                // (to support remapping of "zero time" to null
190
                $line=preg_replace('/datetime not null/i', 'datetime', $line);
191
                $line=preg_replace('/datetime/i', 'timestamp', $line);
192
193
                // double -> numeric
194
                $line=preg_replace('/^double/i', 'numeric', $line);
195
                $line=preg_replace('/(\s*)double/i', '\\1numeric', $line);
196
                // float -> numeric
197
                $line=preg_replace('/^float/i', 'numeric', $line);
198
                $line=preg_replace('/(\s*)float/i', '\\1numeric', $line);
199
200
                // unique index(field1,field2)
201
                if (preg_match('/unique index\s*\((\w+\s*,\s*\w+)\)/i', $line))
202
                {
203
                    $line=preg_replace('/unique index\s*\((\w+\s*,\s*\w+)\)/i', 'UNIQUE\(\\1\)', $line);
204
                }
205
206
                // We remove end of requests "AFTER fieldxxx"
207
                $line=preg_replace('/AFTER [a-z0-9_]+/i', '', $line);
208
209
                // We remove start of requests "ALTER TABLE tablexxx" if this is a DROP INDEX
210
                $line=preg_replace('/ALTER TABLE [a-z0-9_]+ DROP INDEX/i', 'DROP INDEX', $line);
211
212
                // Translate order to rename fields
213
                if (preg_match('/ALTER TABLE ([a-z0-9_]+) CHANGE(?: COLUMN)? ([a-z0-9_]+) ([a-z0-9_]+)(.*)$/i', $line, $reg))
214
                {
215
                    $line = "-- ".$line." replaced by --\n";
216
                    $line.= "ALTER TABLE ".$reg[1]." RENAME COLUMN ".$reg[2]." TO ".$reg[3];
217
                }
218
219
                // Translate order to modify field format
220
                if (preg_match('/ALTER TABLE ([a-z0-9_]+) MODIFY(?: COLUMN)? ([a-z0-9_]+) (.*)$/i', $line, $reg))
221
                {
222
                    $line = "-- ".$line." replaced by --\n";
223
                    $newreg3=$reg[3];
224
                    $newreg3=preg_replace('/ DEFAULT NULL/i', '', $newreg3);
225
                    $newreg3=preg_replace('/ NOT NULL/i', '', $newreg3);
226
                    $newreg3=preg_replace('/ NULL/i', '', $newreg3);
227
                    $newreg3=preg_replace('/ DEFAULT 0/i', '', $newreg3);
228
                    $newreg3=preg_replace('/ DEFAULT \'[0-9a-zA-Z_@]*\'/i', '', $newreg3);
229
                    $line.= "ALTER TABLE ".$reg[1]." ALTER COLUMN ".$reg[2]." TYPE ".$newreg3;
230
                    // TODO Add alter to set default value or null/not null if there is this in $reg[3]
231
                }
232
233
                // alter table add primary key (field1, field2 ...) -> We create a unique index instead as dynamic creation of primary key is not supported
234
                // ALTER TABLE llx_dolibarr_modules ADD PRIMARY KEY pk_dolibarr_modules (numero, entity);
235
                if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+PRIMARY\s+KEY\s*(.*)\s*\((.*)$/i', $line, $reg))
236
                {
237
                    $line = "-- ".$line." replaced by --\n";
238
                    $line.= "CREATE UNIQUE INDEX ".$reg[2]." ON ".$reg[1]."(".$reg[3];
239
                }
240
241
                // Translate order to drop foreign keys
242
                // ALTER TABLE llx_dolibarr_modules DROP FOREIGN KEY fk_xxx;
243
                if (preg_match('/ALTER\s+TABLE\s*(.*)\s*DROP\s+FOREIGN\s+KEY\s*(.*)$/i', $line, $reg))
244
                {
245
                    $line = "-- ".$line." replaced by --\n";
246
                    $line.= "ALTER TABLE ".$reg[1]." DROP CONSTRAINT ".$reg[2];
247
                }
248
249
                // alter table add [unique] [index] (field1, field2 ...)
250
                // ALTER TABLE llx_accountingaccount ADD INDEX idx_accountingaccount_fk_pcg_version (fk_pcg_version)
251
                if (preg_match('/ALTER\s+TABLE\s*(.*)\s*ADD\s+(UNIQUE INDEX|INDEX|UNIQUE)\s+(.*)\s*\(([\w,\s]+)\)/i', $line, $reg))
252
                {
253
                    $fieldlist=$reg[4];
254
                    $idxname=$reg[3];
255
                    $tablename=$reg[1];
256
                    $line = "-- ".$line." replaced by --\n";
257
                    $line.= "CREATE ".(preg_match('/UNIQUE/', $reg[2])?'UNIQUE ':'')."INDEX ".$idxname." ON ".$tablename." (".$fieldlist.")";
258
                }
259
                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)) {
260
                    // Pour l'instant les contraintes ne sont pas créées
261
                    dol_syslog(get_class().'::query line emptied');
262
                    $line = 'SELECT 0;';
263
                }
264
265
                //if (preg_match('/rowid\s+.*\s+PRIMARY\s+KEY,/i', $line)) {
266
                    //preg_replace('/(rowid\s+.*\s+PRIMARY\s+KEY\s*,)/i', '/* \\1 */', $line);
267
                //}
268
            }
269
270
            // Delete using criteria on other table must not declare twice the deleted table
271
            // DELETE FROM tabletodelete USING tabletodelete, othertable -> DELETE FROM tabletodelete USING othertable
272
            if (preg_match('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', $line, $reg))
273
            {
274
				if ($reg[1] == $reg[2])	// If same table, we remove second one
275
                {
276
                    $line=preg_replace('/DELETE FROM ([a-z_]+) USING ([a-z_]+), ([a-z_]+)/i', 'DELETE FROM \\1 USING \\3', $line);
277
                }
278
            }
279
280
            // Remove () in the tables in FROM if one table
281
            $line=preg_replace('/FROM\s*\((([a-z_]+)\s+as\s+([a-z_]+)\s*)\)/i', 'FROM \\1', $line);
282
            //print $line."\n";
283
284
            // Remove () in the tables in FROM if two table
285
            $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);
286
            //print $line."\n";
287
288
            // Remove () in the tables in FROM if two table
289
            $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);
290
            //print $line."\n";
291
292
            //print "type=".$type." newline=".$line."<br>\n";
293
        }
294
295
        return $line;
296
    }
297
298
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
299
    /**
300
	 *	Select a database
301
     *
302
	 *	@param	    string	$database	Name of database
303
	 *	@return	    boolean  		    true if OK, false if KO
304
     */
305
    public function select_db($database)
306
    {
307
        // phpcs:enable
308
        dol_syslog(get_class($this)."::select_db database=".$database, LOG_DEBUG);
309
        // sqlite_select_db() does not exist
310
        //return sqlite_select_db($this->db,$database);
311
        return true;
312
    }
313
314
315
    /**
316
	 *	Connexion to server
317
     *
318
	 *	@param	    string	$host		database server host
319
	 *	@param	    string	$login		login
320
	 *	@param	    string	$passwd		password
321
	 *	@param		string	$name		name of database (not used for mysql, used for pgsql)
322
	 *	@param		integer	$port		Port of database server
323
	 *	@return		SQLite3				Database access handler
324
	 *	@see		close()
325
     */
326
    public function connect($host, $login, $passwd, $name, $port = 0)
327
    {
328
        global $main_data_dir;
329
330
        dol_syslog(get_class($this)."::connect name=".$name, LOG_DEBUG);
331
332
        $dir=$main_data_dir;
333
        if (empty($dir)) $dir=DOL_DATA_ROOT;
334
        // With sqlite, port must be in connect parameters
335
        //if (! $newport) $newport=3306;
336
        $database_name = $dir.'/database_'.$name.'.sdb';
337
        try {
338
            /*** connect to SQLite database ***/
339
            //$this->db = new PDO("sqlite:".$dir.'/database_'.$name.'.sdb');
340
			$this->db = new SQLite3($database_name);
0 ignored issues
show
Documentation Bug introduced by
It seems like new SQLite3($database_name) of type SQLite3 is incompatible with the declared type resource of property $db.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
341
            //$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
342
        }
343
        catch(Exception $e)
344
        {
345
            $this->error=  self::LABEL.' '.$e->getMessage().' current dir='.$database_name;
346
            return '';
347
        }
348
349
        //print "Resultat fonction connect: ".$this->db;
350
        return $this->db;
351
    }
352
353
354
    /**
355
	 *	Return version of database server
356
     *
357
	 *	@return	        string      Version string
358
     */
359
    public function getVersion()
360
    {
361
    	$tmp=$this->db->version();
362
        return $tmp['versionString'];
363
    }
364
365
    /**
366
     *	Return version of database client driver
367
     *
368
     *	@return	        string      Version string
369
     */
370
    public function getDriverInfo()
371
    {
372
        return 'sqlite3 php driver';
373
    }
374
375
376
    /**
377
     *  Close database connexion
378
     *
379
     *  @return     bool     True if disconnect successfull, false otherwise
380
     *  @see        connect()
381
     */
382
    public function close()
383
    {
384
        if ($this->db)
385
        {
386
            if ($this->transaction_opened > 0) dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened, LOG_ERR);
387
            $this->connected=false;
388
            $this->db->close();
389
            unset($this->db);    // Clean this->db
390
            return true;
391
        }
392
        return false;
393
    }
394
395
    /**
396
     *  Execute a SQL request and return the resultset
397
     *
398
     * 	@param	string	$query			SQL query string
399
     * 	@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).
400
     * 									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.
401
     *  @param  string	$type           Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
402
     *	@return	SQLite3Result			Resultset of answer
403
     */
404
    public function query($query, $usesavepoint = 0, $type = 'auto')
405
    {
406
    	global $conf;
407
408
        $ret=null;
409
410
        $query = trim($query);
411
412
        $this->error = '';
413
414
        // Convert MySQL syntax to SQLite syntax
415
        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)) {
416
            // Ajout d'une clef étrangère à la table
417
            // procédure de remplacement de la table pour ajouter la contrainte
418
            // Exemple : ALTER TABLE llx_adherent ADD CONSTRAINT adherent_fk_soc FOREIGN KEY (fk_soc) REFERENCES llx_societe (rowid)
419
            // -> CREATE TABLE ( ... ,CONSTRAINT adherent_fk_soc FOREIGN KEY (fk_soc) REFERENCES llx_societe (rowid))
420
            $foreignFields = $reg[5];
421
            $foreignTable = $reg[4];
422
            $localfields = $reg[3];
423
            $constraintname=trim($reg[2]);
424
            $tablename=trim($reg[1]);
425
426
            $descTable = $this->db->querySingle("SELECT sql FROM sqlite_master WHERE name='" . $tablename . "'");
427
428
            // 1- Renommer la table avec un nom temporaire
429
            $this->query('ALTER TABLE ' . $tablename . ' RENAME TO tmp_' . $tablename);
430
431
            // 2- Recréer la table avec la contrainte ajoutée
432
433
            // on bricole la requete pour ajouter la contrainte
434
            $descTable = substr($descTable, 0, strlen($descTable) - 1);
435
            $descTable .= ", CONSTRAINT " . $constraintname . " FOREIGN KEY (" . $localfields . ") REFERENCES " .$foreignTable . "(" . $foreignFields . ")";
436
437
            // fermeture de l'instruction
438
            $descTable .= ')';
439
440
            // Création proprement dite de la table
441
            $this->query($descTable);
442
443
            // 3- Transférer les données
444
            $this->query('INSERT INTO ' . $tablename . ' SELECT * FROM tmp_' . $tablename);
445
446
            // 4- Supprimer la table temporaire
447
            $this->query('DROP TABLE tmp_' . $tablename);
448
449
            // dummy statement
450
            $query="SELECT 0";
451
        } else {
452
            $query=$this->convertSQLFromMysql($query, $type);
453
        }
454
        //print "After convertSQLFromMysql:\n".$query."<br>\n";
455
456
        if (! in_array($query, array('BEGIN','COMMIT','ROLLBACK'))) dol_syslog('sql='.$query, LOG_DEBUG);
457
        if (empty($query)) return false;    // Return false = error if empty request
458
459
        // Ordre SQL ne necessitant pas de connexion a une base (exemple: CREATE DATABASE)
460
        try {
461
            //$ret = $this->db->exec($query);
462
            $ret = $this->db->query($query);        // $ret is a Sqlite3Result
463
            if ($ret) {
464
                $ret->queryString = $query;
465
            }
466
        }
467
        catch(Exception $e)
468
        {
469
            $this->error=$this->db->lastErrorMsg();
470
        }
471
472
        if (! preg_match("/^COMMIT/i", $query) && ! preg_match("/^ROLLBACK/i", $query))
473
        {
474
            // Si requete utilisateur, on la sauvegarde ainsi que son resultset
475
            if (! is_object($ret) || $this->error)
476
            {
477
                $this->lastqueryerror = $query;
478
                $this->lasterror = $this->error();
479
                $this->lasterrno = $this->errno();
480
481
                dol_syslog(get_class($this)."::query SQL Error query: ".$query, LOG_ERR);
482
483
                $errormsg = get_class($this)."::query SQL Error message: ".$this->lasterror;
484
485
                if (preg_match('/[0-9]/', $this->lasterrno)) {
486
                    $errormsg .= ' ('.$this->lasterrno.')';
487
                }
488
489
                if ($conf->global->SYSLOG_LEVEL < LOG_DEBUG) dol_syslog(get_class($this)."::query SQL Error query: ".$query, LOG_ERR);	// Log of request was not yet done previously
490
                dol_syslog(get_class($this)."::query SQL Error message: ".$errormsg, LOG_ERR);
491
            }
492
            $this->lastquery=$query;
493
            $this->_results = $ret;
494
        }
495
496
        return $ret;
497
    }
498
499
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
500
    /**
501
     *	Renvoie la ligne courante (comme un objet) pour le curseur resultset
502
     *
503
     *	@param	SQLite3Result	$resultset  Curseur de la requete voulue
504
     *	@return	false|object				Object result line or false if KO or end of cursor
505
     */
506
    public function fetch_object($resultset)
507
    {
508
        // phpcs:enable
509
        // Si le resultset n'est pas fourni, on prend le dernier utilise sur cette connexion
510
        if (! is_object($resultset)) { $resultset=$this->_results; }
511
        //return $resultset->fetch(PDO::FETCH_OBJ);
512
        $ret = $resultset->fetchArray(SQLITE3_ASSOC);
513
        if ($ret) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ret of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
514
            return (object) $ret;
515
        }
516
		return false;
517
    }
518
519
520
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
521
    /**
522
     *	Return datas as an array
523
     *
524
     *	@param	SQLite3Result	$resultset  Resultset of request
525
     *	@return	false|array					Array or false if KO or end of cursor
526
     */
527
    public function fetch_array($resultset)
528
    {
529
        // phpcs:enable
530
        // If resultset not provided, we take the last used by connexion
531
        if (! is_object($resultset)) { $resultset=$this->_results; }
532
        //return $resultset->fetch(PDO::FETCH_ASSOC);
533
        $ret = $resultset->fetchArray(SQLITE3_ASSOC);
534
	    return $ret;
535
    }
536
537
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
538
    /**
539
     *	Return datas as an array
540
     *
541
     *	@param	SQLite3Result	$resultset  Resultset of request
542
     *	@return	false|array					Array or false if KO or end of cursor
543
     */
544
    public function fetch_row($resultset)
545
    {
546
        // phpcs:enable
547
        // If resultset not provided, we take the last used by connexion
548
        if (! is_bool($resultset))
0 ignored issues
show
introduced by
The condition is_bool($resultset) is always false.
Loading history...
549
        {
550
            if (! is_object($resultset)) { $resultset=$this->_results; }
551
            return $resultset->fetchArray(SQLITE3_NUM);
552
        }
553
        else
554
        {
555
            // si le curseur est un booleen on retourne la valeur 0
556
            return false;
557
        }
558
    }
559
560
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
561
    /**
562
     *	Return number of lines for result of a SELECT
563
     *
564
     *	@param	SQLite3Result	$resultset  Resulset of requests
565
     *	@return int		    			Nb of lines
566
     *	@see    affected_rows()
567
     */
568
    public function num_rows($resultset)
569
    {
570
        // phpcs:enable
571
        // FIXME: SQLite3Result does not have a queryString member
572
573
        // If resultset not provided, we take the last used by connexion
574
        if (! is_object($resultset)) { $resultset=$this->_results; }
575
        if (preg_match("/^SELECT/i", $resultset->queryString)) {
0 ignored issues
show
Bug introduced by
The property queryString does not seem to exist on SQLite3Result.
Loading history...
576
            return $this->db->querySingle("SELECT count(*) FROM (" . $resultset->queryString . ") q");
577
        }
578
        return 0;
579
    }
580
581
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
582
    /**
583
     *	Return number of lines for result of a SELECT
584
     *
585
     *	@param	SQLite3Result	$resultset  Resulset of requests
586
     *	@return int		    			Nb of lines
587
     *	@see    affected_rows()
588
     */
589
    public function affected_rows($resultset)
590
    {
591
        // phpcs:enable
592
        // FIXME: SQLite3Result does not have a queryString member
593
594
        // If resultset not provided, we take the last used by connexion
595
        if (! is_object($resultset)) { $resultset=$this->_results; }
596
        if (preg_match("/^SELECT/i", $resultset->queryString)) {
0 ignored issues
show
Bug introduced by
The property queryString does not seem to exist on SQLite3Result.
Loading history...
597
            return $this->num_rows($resultset);
598
        }
599
        // mysql necessite un link de base pour cette fonction contrairement
600
        // a pqsql qui prend un resultset
601
        return $this->db->changes();
602
    }
603
604
605
    /**
606
	 *	Free last resultset used.
607
     *
608
	 *	@param  SQLite3Result	$resultset   Curseur de la requete voulue
609
	 *	@return	void
610
     */
611
    public function free($resultset = null)
612
    {
613
        // If resultset not provided, we take the last used by connexion
614
        if (! is_object($resultset)) { $resultset=$this->_results; }
615
        // Si resultset en est un, on libere la memoire
616
        if ($resultset && is_object($resultset)) $resultset->finalize();
617
    }
618
619
    /**
620
	 *	Escape a string to insert data
621
     *
622
	 *  @param	string	$stringtoencode		String to escape
623
	 *  @return	string						String escaped
624
     */
625
    public function escape($stringtoencode)
626
    {
627
        return Sqlite3::escapeString($stringtoencode);
628
    }
629
630
    /**
631
     *	Renvoie le code erreur generique de l'operation precedente.
632
     *
633
     *	@return	string		Error code (Exemples: DB_ERROR_TABLE_ALREADY_EXISTS, DB_ERROR_RECORD_ALREADY_EXISTS...)
634
     */
635
    public function errno()
636
    {
637
        if (! $this->connected) {
638
            // Si il y a eu echec de connexion, $this->db n'est pas valide.
639
            return 'DB_ERROR_FAILED_TO_CONNECT';
640
        }
641
        else {
642
            // Constants to convert error code to a generic Dolibarr error code
643
            /*$errorcode_map = array(
644
            1004 => 'DB_ERROR_CANNOT_CREATE',
645
            1005 => 'DB_ERROR_CANNOT_CREATE',
646
            1006 => 'DB_ERROR_CANNOT_CREATE',
647
            1007 => 'DB_ERROR_ALREADY_EXISTS',
648
            1008 => 'DB_ERROR_CANNOT_DROP',
649
            1025 => 'DB_ERROR_NO_FOREIGN_KEY_TO_DROP',
650
            1044 => 'DB_ERROR_ACCESSDENIED',
651
            1046 => 'DB_ERROR_NODBSELECTED',
652
            1048 => 'DB_ERROR_CONSTRAINT',
653
            'HY000' => 'DB_ERROR_TABLE_ALREADY_EXISTS',
654
            1051 => 'DB_ERROR_NOSUCHTABLE',
655
            1054 => 'DB_ERROR_NOSUCHFIELD',
656
            1060 => 'DB_ERROR_COLUMN_ALREADY_EXISTS',
657
            1061 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
658
            1062 => 'DB_ERROR_RECORD_ALREADY_EXISTS',
659
            1064 => 'DB_ERROR_SYNTAX',
660
            1068 => 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS',
661
            1075 => 'DB_ERROR_CANT_DROP_PRIMARY_KEY',
662
            1091 => 'DB_ERROR_NOSUCHFIELD',
663
            1100 => 'DB_ERROR_NOT_LOCKED',
664
            1136 => 'DB_ERROR_VALUE_COUNT_ON_ROW',
665
            1146 => 'DB_ERROR_NOSUCHTABLE',
666
            1216 => 'DB_ERROR_NO_PARENT',
667
            1217 => 'DB_ERROR_CHILD_EXISTS',
668
            1451 => 'DB_ERROR_CHILD_EXISTS'
669
            );
670
671
            if (isset($errorcode_map[$this->db->errorCode()]))
672
            {
673
                return $errorcode_map[$this->db->errorCode()];
674
            }*/
675
            $errno=$this->db->lastErrorCode();
676
			if ($errno=='HY000' || $errno == 0)
677
            {
678
                if (preg_match('/table.*already exists/i', $this->error))     return 'DB_ERROR_TABLE_ALREADY_EXISTS';
679
                elseif (preg_match('/index.*already exists/i', $this->error)) return 'DB_ERROR_KEY_NAME_ALREADY_EXISTS';
680
                elseif (preg_match('/syntax error/i', $this->error))          return 'DB_ERROR_SYNTAX';
681
            }
682
            if ($errno=='23000')
683
            {
684
                if (preg_match('/column.* not unique/i', $this->error))       return 'DB_ERROR_RECORD_ALREADY_EXISTS';
685
                elseif (preg_match('/PRIMARY KEY must be unique/i', $this->error)) return 'DB_ERROR_RECORD_ALREADY_EXISTS';
686
            }
687
            if ($errno > 1) {
688
                // TODO Voir la liste des messages d'erreur
689
            }
690
691
            return ($errno?'DB_ERROR_'.$errno:'0');
692
        }
693
    }
694
695
    /**
696
     *	Renvoie le texte de l'erreur mysql de l'operation precedente.
697
     *
698
     *	@return	string	Error text
699
     */
700
    public function error()
701
    {
702
        if (! $this->connected) {
703
            // Si il y a eu echec de connexion, $this->db n'est pas valide pour sqlite_error.
704
            return 'Not connected. Check setup parameters in conf/conf.php file and your sqlite version';
705
        }
706
        else {
707
            return $this->error;
708
        }
709
    }
710
711
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
712
    /**
713
     * Get last ID after an insert INSERT
714
     *
715
	 * @param   string	$tab    	Table name concerned by insert. Ne sert pas sous MySql mais requis pour compatibilite avec Postgresql
716
	 * @param	string	$fieldid	Field name
717
	 * @return  int     			Id of row
718
     */
719
    public function last_insert_id($tab, $fieldid = 'rowid')
720
    {
721
        // phpcs:enable
722
        return $this->db->lastInsertRowId();
723
    }
724
725
    /**
726
     *  Encrypt sensitive data in database
727
     *  Warning: This function includes the escape, so it must use direct value
728
     *
729
     *  @param  string  $fieldorvalue   Field name or value to encrypt
730
     *  @param	int		$withQuotes     Return string with quotes
731
     *  @return string          		XXX(field) or XXX('value') or field or 'value'
732
     */
733
    public function encrypt($fieldorvalue, $withQuotes = 0)
734
    {
735
        global $conf;
736
737
        // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
738
        $cryptType = ($conf->db->dolibarr_main_db_encryption?$conf->db->dolibarr_main_db_encryption:0);
739
740
        //Encryption key
741
        $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey)?$conf->db->dolibarr_main_db_cryptkey:'');
742
743
        $return = ($withQuotes?"'":"").$this->escape($fieldorvalue).($withQuotes?"'":"");
744
745
        if ($cryptType && !empty($cryptKey))
746
        {
747
            if ($cryptType == 2)
748
            {
749
                $return = 'AES_ENCRYPT('.$return.',\''.$cryptKey.'\')';
750
            }
751
            elseif ($cryptType == 1)
752
            {
753
                $return = 'DES_ENCRYPT('.$return.',\''.$cryptKey.'\')';
754
            }
755
        }
756
757
        return $return;
758
    }
759
760
    /**
761
     *	Decrypt sensitive data in database
762
     *
763
     *	@param	string	$value			Value to decrypt
764
     * 	@return	string					Decrypted value if used
765
     */
766
    public function decrypt($value)
767
    {
768
        global $conf;
769
770
        // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
771
        $cryptType = ($conf->db->dolibarr_main_db_encryption?$conf->db->dolibarr_main_db_encryption:0);
772
773
        //Encryption key
774
        $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey)?$conf->db->dolibarr_main_db_cryptkey:'');
775
776
        $return = $value;
777
778
        if ($cryptType && !empty($cryptKey))
779
        {
780
            if ($cryptType == 2)
781
            {
782
                $return = 'AES_DECRYPT('.$value.',\''.$cryptKey.'\')';
783
            }
784
            elseif ($cryptType == 1)
785
            {
786
                $return = 'DES_DECRYPT('.$value.',\''.$cryptKey.'\')';
787
            }
788
        }
789
790
        return $return;
791
    }
792
793
794
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
795
    /**
796
     * Return connexion ID
797
     *
798
	 * @return	        string      Id connexion
799
     */
800
    public function DDLGetConnectId()
801
    {
802
        // phpcs:enable
803
        return '?';
804
    }
805
806
807
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
808
    /**
809
	 *	Create a new database
810
	 *	Do not use function xxx_create_db (xxx=mysql, ...) as they are deprecated
811
	 *	We force to create database with charset this->forcecharset and collate this->forcecollate
812
     *
813
	 *	@param	string	$database		Database name to create
814
	 * 	@param	string	$charset		Charset used to store data
815
	 * 	@param	string	$collation		Charset used to sort data
816
	 * 	@param	string	$owner			Username of database owner
817
	 * 	@return	SQLite3Result   		resource defined if OK, null if KO
818
     */
819
    public function DDLCreateDb($database, $charset = '', $collation = '', $owner = '')
820
    {
821
        // phpcs:enable
822
        if (empty($charset))   $charset=$this->forcecharset;
823
        if (empty($collation)) $collation=$this->forcecollate;
824
825
        // ALTER DATABASE dolibarr_db DEFAULT CHARACTER SET latin DEFAULT COLLATE latin1_swedish_ci
826
        $sql = 'CREATE DATABASE '.$database;
827
        $sql.= ' DEFAULT CHARACTER SET '.$charset.' DEFAULT COLLATE '.$collation;
828
829
        dol_syslog($sql, LOG_DEBUG);
830
        $ret=$this->query($sql);
831
        if (! $ret)
832
        {
833
            // We try again for compatibility with Mysql < 4.1.1
834
            $sql = 'CREATE DATABASE '.$database;
835
            $ret=$this->query($sql);
836
            dol_syslog($sql, LOG_DEBUG);
837
        }
838
        return $ret;
839
    }
840
841
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
842
    /**
843
     *  List tables into a database
844
     *
845
	 *  @param	string		$database	Name of database
846
	 *  @param	string		$table		Name of table filter ('xxx%')
847
	 *  @return	array					List of tables in an array
848
     */
849
    public function DDLListTables($database, $table = '')
850
    {
851
        // phpcs:enable
852
        $listtables=array();
853
854
        $like = '';
855
        if ($table) $like = "LIKE '".$table."'";
856
        $sql="SHOW TABLES FROM ".$database." ".$like.";";
857
        //print $sql;
858
        $result = $this->query($sql);
859
        if ($result)
860
        {
861
            while($row = $this->fetch_row($result))
862
            {
863
                $listtables[] = $row[0];
864
            }
865
        }
866
        return $listtables;
867
    }
868
869
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
870
    /**
871
     *  List information of columns into a table.
872
     *
873
	 *	@param	string	$table		Name of table
874
	 *	@return	array				Tableau des informations des champs de la table
875
	 *	TODO modify for sqlite
876
     */
877
    public function DDLInfoTable($table)
878
    {
879
        // phpcs:enable
880
        $infotables=array();
881
882
        $sql="SHOW FULL COLUMNS FROM ".$table.";";
883
884
        dol_syslog($sql, LOG_DEBUG);
885
        $result = $this->query($sql);
886
        if ($result)
887
        {
888
            while($row = $this->fetch_row($result))
889
            {
890
                $infotables[] = $row;
891
            }
892
        }
893
        return $infotables;
894
    }
895
896
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
897
    /**
898
	 *	Create a table into database
899
     *
900
	 *	@param	    string	$table 			Nom de la table
901
	 *	@param	    array	$fields 		Tableau associatif [nom champ][tableau des descriptions]
902
	 *	@param	    string	$primary_key 	Nom du champ qui sera la clef primaire
903
	 *	@param	    string	$type 			Type de la table
904
	 *	@param	    array	$unique_keys 	Tableau associatifs Nom de champs qui seront clef unique => valeur
905
	 *	@param	    array	$fulltext_keys	Tableau des Nom de champs qui seront indexes en fulltext
906
	 *	@param	    array	$keys 			Tableau des champs cles noms => valeur
907
	 *	@return	    int						<0 if KO, >=0 if OK
908
     */
909
    public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null)
910
    {
911
        // phpcs:enable
912
        // FIXME: $fulltext_keys parameter is unused
913
914
        // cles recherchees dans le tableau des descriptions (fields) : type,value,attribute,null,default,extra
915
        // ex. : $fields['rowid'] = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
916
        $sql = "create table ".$table."(";
917
        $i=0;
918
        foreach($fields as $field_name => $field_desc)
919
        {
920
            $sqlfields[$i] = $field_name." ";
921
            $sqlfields[$i]  .= $field_desc['type'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sqlfields seems to be defined later in this foreach loop on line 920. Are you sure it is defined here?
Loading history...
922
            if( preg_match("/^[^\s]/i", $field_desc['value']))
923
                $sqlfields[$i]  .= "(".$field_desc['value'].")";
924
            elseif( preg_match("/^[^\s]/i", $field_desc['attribute']))
925
                $sqlfields[$i]  .= " ".$field_desc['attribute'];
926
            elseif( preg_match("/^[^\s]/i", $field_desc['default']))
927
            {
928
                if(preg_match("/null/i", $field_desc['default']))
929
                    $sqlfields[$i] .= " default ".$field_desc['default'];
930
                else
931
                    $sqlfields[$i] .= " default '".$field_desc['default']."'";
932
            }
933
            elseif( preg_match("/^[^\s]/i", $field_desc['null']))
934
                $sqlfields[$i] .= " ".$field_desc['null'];
935
936
            elseif( preg_match("/^[^\s]/i", $field_desc['extra']))
937
                $sqlfields[$i] .= " ".$field_desc['extra'];
938
            $i++;
939
        }
940
        if($primary_key != "")
941
        $pk = "primary key(".$primary_key.")";
942
943
        if(is_array($unique_keys))
944
        {
945
            $i = 0;
946
            foreach($unique_keys as $key => $value)
947
            {
948
                $sqluq[$i] = "UNIQUE KEY '".$key."' ('".$value."')";
949
                $i++;
950
            }
951
        }
952
        if(is_array($keys))
953
        {
954
            $i = 0;
955
            foreach($keys as $key => $value)
956
            {
957
                $sqlk[$i] = "KEY ".$key." (".$value.")";
958
                $i++;
959
            }
960
        }
961
        $sql .= implode(',', $sqlfields);
962
        if($primary_key != "")
963
        $sql .= ",".$pk;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $pk does not seem to be defined for all execution paths leading up to this point.
Loading history...
964
        if(is_array($unique_keys))
965
        $sql .= ",".implode(',', $sqluq);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sqluq does not seem to be defined for all execution paths leading up to this point.
Loading history...
966
        if(is_array($keys))
967
        $sql .= ",".implode(',', $sqlk);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sqlk does not seem to be defined for all execution paths leading up to this point.
Loading history...
968
        $sql .=") type=".$type;
969
970
        dol_syslog($sql, LOG_DEBUG);
971
        if(! $this -> query($sql))
972
            return -1;
973
        return 1;
974
    }
975
976
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
977
    /**
978
     *	Drop a table into database
979
     *
980
     *	@param	    string	$table 			Name of table
981
     *	@return	    int						<0 if KO, >=0 if OK
982
     */
983
    public function DDLDropTable($table)
984
    {
985
        // phpcs:enable
986
    	$sql = "DROP TABLE ".$table;
987
988
    	if (! $this->query($sql))
989
    		return -1;
990
    	else
991
    		return 1;
992
    }
993
994
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
995
    /**
996
	 *	Return a pointer of line with description of a table or field
997
     *
998
	 *	@param	string		$table	Name of table
999
	 *	@param	string		$field	Optionnel : Name of field if we want description of field
1000
	 *	@return	SQLite3Result		Resource
1001
     */
1002
    public function DDLDescTable($table, $field = "")
1003
    {
1004
        // phpcs:enable
1005
        $sql="DESC ".$table." ".$field;
1006
1007
        dol_syslog(get_class($this)."::DDLDescTable ".$sql, LOG_DEBUG);
1008
        $this->_results = $this->query($sql);
1009
        return $this->_results;
1010
    }
1011
1012
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1013
    /**
1014
	 *	Create a new field into table
1015
     *
1016
	 *	@param	string	$table 				Name of table
1017
	 *	@param	string	$field_name 		Name of field to add
1018
	 *	@param	string	$field_desc 		Tableau associatif de description du champ a inserer[nom du parametre][valeur du parametre]
1019
	 *	@param	string	$field_position 	Optionnel ex.: "after champtruc"
1020
	 *	@return	int							<0 if KO, >0 if OK
1021
     */
1022
    public function DDLAddField($table, $field_name, $field_desc, $field_position = "")
1023
    {
1024
        // phpcs:enable
1025
        // cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra
1026
        // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
1027
        $sql= "ALTER TABLE ".$table." ADD ".$field_name." ";
1028
        $sql.= $field_desc['type'];
1029
        if(preg_match("/^[^\s]/i", $field_desc['value']))
1030
        if (! in_array($field_desc['type'], array('date','datetime')))
1031
        {
1032
            $sql.= "(".$field_desc['value'].")";
1033
        }
1034
        if(preg_match("/^[^\s]/i", $field_desc['attribute']))
1035
        $sql.= " ".$field_desc['attribute'];
1036
        if(preg_match("/^[^\s]/i", $field_desc['null']))
1037
        $sql.= " ".$field_desc['null'];
1038
        if(preg_match("/^[^\s]/i", $field_desc['default']))
1039
        {
1040
            if(preg_match("/null/i", $field_desc['default']))
1041
            $sql.= " default ".$field_desc['default'];
1042
            else
1043
            $sql.= " default '".$field_desc['default']."'";
1044
        }
1045
        if(preg_match("/^[^\s]/i", $field_desc['extra']))
1046
        $sql.= " ".$field_desc['extra'];
1047
        $sql.= " ".$field_position;
1048
1049
        dol_syslog(get_class($this)."::DDLAddField ".$sql, LOG_DEBUG);
1050
        if(! $this->query($sql))
1051
        {
1052
            return -1;
1053
        }
1054
        return 1;
1055
    }
1056
1057
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1058
    /**
1059
	 *	Update format of a field into a table
1060
     *
1061
	 *	@param	string	$table 				Name of table
1062
	 *	@param	string	$field_name 		Name of field to modify
1063
	 *	@param	string	$field_desc 		Array with description of field format
1064
	 *	@return	int							<0 if KO, >0 if OK
1065
     */
1066
    public function DDLUpdateField($table, $field_name, $field_desc)
1067
    {
1068
        // phpcs:enable
1069
        $sql = "ALTER TABLE ".$table;
1070
        $sql .= " MODIFY COLUMN ".$field_name." ".$field_desc['type'];
1071
        if ($field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int' || $field_desc['type'] == 'varchar') {
1072
            $sql.="(".$field_desc['value'].")";
1073
        }
1074
1075
        dol_syslog(get_class($this)."::DDLUpdateField ".$sql, LOG_DEBUG);
1076
        if (! $this->query($sql))
1077
            return -1;
1078
        return 1;
1079
    }
1080
1081
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1082
    /**
1083
	 *	Drop a field from table
1084
     *
1085
	 *	@param	string	$table 			Name of table
1086
	 *	@param	string	$field_name 	Name of field to drop
1087
	 *	@return	int						<0 if KO, >0 if OK
1088
     */
1089
    public function DDLDropField($table, $field_name)
1090
    {
1091
        // phpcs:enable
1092
        $sql= "ALTER TABLE ".$table." DROP COLUMN `".$field_name."`";
1093
        dol_syslog(get_class($this)."::DDLDropField ".$sql, LOG_DEBUG);
1094
        if (! $this->query($sql))
1095
        {
1096
            $this->error=$this->lasterror();
1097
            return -1;
1098
        }
1099
        return 1;
1100
    }
1101
1102
1103
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1104
    /**
1105
     * 	Create a user and privileges to connect to database (even if database does not exists yet)
1106
     *
1107
	 *	@param	string	$dolibarr_main_db_host 		Ip serveur
1108
	 *	@param	string	$dolibarr_main_db_user 		Nom user a creer
1109
	 *	@param	string	$dolibarr_main_db_pass 		Mot de passe user a creer
1110
	 *	@param	string	$dolibarr_main_db_name		Database name where user must be granted
1111
	 *	@return	int									<0 if KO, >=0 if OK
1112
     */
1113
    public function DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name)
1114
    {
1115
        // phpcs:enable
1116
        $sql = "INSERT INTO user ";
1117
        $sql.= "(Host,User,password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Index_Priv,Alter_priv,Lock_tables_priv)";
1118
        $sql.= " VALUES ('".$this->escape($dolibarr_main_db_host)."','".$this->escape($dolibarr_main_db_user)."',password('".addslashes($dolibarr_main_db_pass)."')";
1119
        $sql.= ",'Y','Y','Y','Y','Y','Y','Y','Y','Y')";
1120
1121
        dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG);	// No sql to avoid password in log
1122
        $resql=$this->query($sql);
1123
        if (! $resql)
1124
        {
1125
            return -1;
1126
        }
1127
1128
        $sql = "INSERT INTO db ";
1129
        $sql.= "(Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Index_Priv,Alter_priv,Lock_tables_priv)";
1130
        $sql.= " VALUES ('".$this->escape($dolibarr_main_db_host)."','".$this->escape($dolibarr_main_db_name)."','".addslashes($dolibarr_main_db_user)."'";
1131
        $sql.= ",'Y','Y','Y','Y','Y','Y','Y','Y','Y')";
1132
1133
        dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG);
1134
        $resql=$this->query($sql);
1135
        if (! $resql)
1136
        {
1137
            return -1;
1138
        }
1139
1140
        $sql="FLUSH Privileges";
1141
1142
        dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG);
1143
        $resql=$this->query($sql);
1144
        if (! $resql)
1145
        {
1146
            return -1;
1147
        }
1148
        return 1;
1149
    }
1150
1151
    /**
1152
	 *	Return charset used to store data in database
1153
     *
1154
	 *	@return		string		Charset
1155
     */
1156
    public function getDefaultCharacterSetDatabase()
1157
    {
1158
        return 'UTF-8';
1159
    }
1160
1161
    /**
1162
	 *	Return list of available charset that can be used to store data in database
1163
     *
1164
	 *	@return		array		List of Charset
1165
     */
1166
    public function getListOfCharacterSet()
1167
    {
1168
        $liste = array();
1169
        $i=0;
1170
        $liste[$i]['charset'] = 'UTF-8';
1171
        $liste[$i]['description'] = 'UTF-8';
1172
        return $liste;
1173
    }
1174
1175
    /**
1176
	 *	Return collation used in database
1177
     *
1178
	 *	@return		string		Collation value
1179
     */
1180
    public function getDefaultCollationDatabase()
1181
    {
1182
        return 'UTF-8';
1183
    }
1184
1185
    /**
1186
	 *	Return list of available collation that can be used for database
1187
     *
1188
	 *	@return		array		List of Collation
1189
     */
1190
    public function getListOfCollation()
1191
    {
1192
        $liste = array();
1193
        $i=0;
1194
        $liste[$i]['charset'] = 'UTF-8';
1195
        $liste[$i]['description'] = 'UTF-8';
1196
        return $liste;
1197
    }
1198
1199
    /**
1200
	 *	Return full path of dump program
1201
     *
1202
	 *	@return		string		Full path of dump program
1203
     */
1204
    public function getPathOfDump()
1205
    {
1206
	    // FIXME: not for SQLite
1207
        $fullpathofdump='/pathtomysqldump/mysqldump';
1208
1209
        $resql=$this->query('SHOW VARIABLES LIKE \'basedir\'');
1210
        if ($resql)
1211
        {
1212
            $liste=$this->fetch_array($resql);
1213
            $basedir=$liste['Value'];
1214
            $fullpathofdump=$basedir.(preg_match('/\/$/', $basedir)?'':'/').'bin/mysqldump';
1215
        }
1216
        return $fullpathofdump;
1217
    }
1218
1219
    /**
1220
	 *	Return full path of restore program
1221
     *
1222
	 *	@return		string		Full path of restore program
1223
     */
1224
    public function getPathOfRestore()
1225
    {
1226
	    // FIXME: not for SQLite
1227
        $fullpathofimport='/pathtomysql/mysql';
1228
1229
        $resql=$this->query('SHOW VARIABLES LIKE \'basedir\'');
1230
        if ($resql)
1231
        {
1232
            $liste=$this->fetch_array($resql);
1233
            $basedir=$liste['Value'];
1234
            $fullpathofimport=$basedir.(preg_match('/\/$/', $basedir)?'':'/').'bin/mysql';
1235
        }
1236
        return $fullpathofimport;
1237
    }
1238
1239
    /**
1240
     * Return value of server parameters
1241
     *
1242
	 * @param	string	$filter		Filter list on a particular value
1243
	 * @return	array				Array of key-values (key=>value)
1244
     */
1245
    public function getServerParametersValues($filter = '')
1246
    {
1247
        $result=array();
1248
        static $pragmas;
1249
        if (! isset($pragmas)) {
1250
            // Définition de la liste des pragmas utilisés qui ne retournent qu'une seule valeur
1251
            // indépendante de la base de données.
1252
            // cf. http://www.sqlite.org/pragma.html
1253
            $pragmas = array(
1254
                'application_id', 'auto_vacuum', 'automatic_index', 'busy_timeout', 'cache_size',
1255
                'cache_spill', 'case_sensitive_like', 'checkpoint_fullsync', 'collation_list',
1256
                'compile_options', 'data_version',	/*'database_list',*/
1257
                'defer_foreign_keys', 'encoding', 'foreign_key_check', 'freelist_count',
1258
                'full_column_names', 'fullsync', 'ingore_check_constraints', 'integrity_check',
1259
                'journal_mode', 'journal_size_limit', 'legacy_file_format', 'locking_mode',
1260
                'max_page_count', 'page_count', 'page_size', 'parser_trace',
1261
                'query_only', 'quick_check', 'read_uncommitted', 'recursive_triggers',
1262
                'reverse_unordered_selects', 'schema_version', 'user_version',
1263
                'secure_delete', 'short_column_names', 'shrink_memory', 'soft_heap_limit',
1264
                'synchronous', 'temp_store', /*'temp_store_directory',*/ 'threads',
1265
                'vdbe_addoptrace', 'vdbe_debug', 'vdbe_listing', 'vdbe_trace',
1266
                'wal_autocheckpoint',
1267
            );
1268
        }
1269
1270
        // TODO prendre en compte le filtre
1271
        foreach($pragmas as $var) {
1272
            $sql = "PRAGMA $var";
1273
            $resql=$this->query($sql);
1274
            if ($resql)
1275
            {
1276
                $obj = $this->fetch_row($resql);
1277
                //dol_syslog(get_class($this)."::select_db getServerParametersValues $var=". print_r($obj, true), LOG_DEBUG);
1278
                $result[$var] = $obj[0];
1279
            }
1280
            else {
1281
                // TODO Récupérer le message
1282
                $result[$var] = 'FAIL';
1283
            }
1284
        }
1285
        return $result;
1286
    }
1287
1288
    /**
1289
     * Return value of server status
1290
     *
1291
	 * @param	string	$filter		Filter list on a particular value
1292
	 * @return  array				Array of key-values (key=>value)
1293
     */
1294
    public function getServerStatusValues($filter = '')
1295
    {
1296
        $result=array();
1297
        /*
1298
        $sql='SHOW STATUS';
1299
        if ($filter) $sql.=" LIKE '".$this->escape($filter)."'";
1300
        $resql=$this->query($sql);
1301
        if ($resql)
1302
        {
1303
            while ($obj=$this->fetch_object($resql)) $result[$obj->Variable_name]=$obj->Value;
1304
        }
1305
         */
1306
1307
        return $result;
1308
    }
1309
1310
    /**
1311
     * Permet le chargement d'une fonction personnalisee dans le moteur de base de donnees.
1312
     * Note: le nom de la fonction personnalisee est prefixee par 'db'. La fonction doit être
1313
     * statique et publique. Le nombre de parametres est determine automatiquement.
1314
     *
1315
     * @param 	string 	$name 			Le nom de la fonction a definir dans Sqlite
1316
     * @param	int		$arg_count		Arg count
1317
     * @return	void
1318
     */
1319
    private function addCustomFunction($name, $arg_count = -1)
1320
    {
1321
        if ($this->db)
1322
        {
1323
        	$newname=preg_replace('/_/', '', $name);
1324
            $localname = __CLASS__ . '::' . 'db' . $newname;
1325
            $reflectClass = new ReflectionClass(__CLASS__);
1326
            $reflectFunction = $reflectClass->getMethod('db' . $newname);
1327
            if ($arg_count < 0) {
1328
                $arg_count = $reflectFunction->getNumberOfParameters();
1329
            }
1330
            if (!$this->db->createFunction($name, $localname, $arg_count))
1331
            {
1332
                $this->error = "unable to create custom function '$name'";
1333
            }
1334
        }
1335
    }
1336
1337
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1338
    /**
1339
     * calc_daynr
1340
     *
1341
     * @param 	int 	$year		Year
1342
     * @param 	int 	$month		Month
1343
     * @param	int     $day 		Day
1344
     * @return int Formatted date
1345
     */
1346
    private static function calc_daynr($year, $month, $day)
1347
    {
1348
        // phpcs:enable
1349
        $y = $year;
1350
        if ($y == 0 && $month == 0) return 0;
1351
        $num = (365* $y + 31 * ($month - 1) + $day);
1352
        if ($month <= 2) {
1353
            $y--; }
1354
        else {
1355
            $num -= floor(($month * 4 + 23) / 10);
1356
        }
1357
        $temp = floor(($y / 100 + 1) * 3 / 4);
1358
        return $num + floor($y / 4) - $temp;
1359
    }
1360
1361
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1362
    /**
1363
     * calc_weekday
1364
     *
1365
     * @param int	$daynr							???
1366
     * @param bool	$sunday_first_day_of_week		???
1367
     * @return int
1368
     */
1369
    private static function calc_weekday($daynr, $sunday_first_day_of_week)
1370
    {
1371
        // phpcs:enable
1372
        $ret = floor(($daynr + 5 + ($sunday_first_day_of_week ? 1 : 0)) % 7);
1373
        return $ret;
1374
    }
1375
1376
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1377
    /**
1378
     * calc_days_in_year
1379
     *
1380
     * @param 	string	$year		Year
1381
     * @return	int					Nb of days in year
1382
     */
1383
    private static function calc_days_in_year($year)
1384
    {
1385
        // phpcs:enable
1386
        return (($year & 3) == 0 && ($year%100 || ($year%400 == 0 && $year)) ? 366 : 365);
1387
    }
1388
1389
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1390
	/**
1391
	 * calc_week
1392
	 *
1393
	 * @param 	string	$year				Year
1394
	 * @param 	string	$month				Month
1395
	 * @param 	string	$day				Day
1396
	 * @param 	string	$week_behaviour		Week behaviour
1397
	 * @param 	string	$calc_year			???
1398
	 * @return	string						???
1399
	 */
1400
    private static function calc_week($year, $month, $day, $week_behaviour, &$calc_year)
1401
    {
1402
        // phpcs:enable
1403
        $daynr=self::calc_daynr($year, $month, $day);
1404
        $first_daynr=self::calc_daynr($year, 1, 1);
1405
        $monday_first= ($week_behaviour & self::WEEK_MONDAY_FIRST) ? 1 : 0;
1406
        $week_year= ($week_behaviour & self::WEEK_YEAR) ? 1 : 0;
1407
        $first_weekday= ($week_behaviour & self::WEEK_FIRST_WEEKDAY) ? 1 : 0;
1408
1409
        $weekday=self::calc_weekday($first_daynr, !$monday_first);
1410
        $calc_year=$year;
1411
1412
        if ($month == 1 && $day <= 7-$weekday) {
1413
            if (!$week_year && (($first_weekday && $weekday != 0) || (!$first_weekday && $weekday >= 4)))
1414
                return 0;
1415
            $week_year= 1;
1416
            $calc_year--;
1417
            $first_daynr-= ($days=self::calc_days_in_year($calc_year));
1418
            $weekday= ($weekday + 53*7- $days) % 7;
1419
        }
1420
1421
        if (($first_weekday && $weekday != 0) || (!$first_weekday && $weekday >= 4)) {
1422
            $days= $daynr - ($first_daynr+ (7-$weekday));
1423
        } else {
1424
            $days= $daynr - ($first_daynr - $weekday);
1425
        }
1426
1427
        if ($week_year && $days >= 52*7) {
1428
            $weekday= ($weekday + self::calc_days_in_year($calc_year)) % 7;
1429
            if ((!$first_weekday && $weekday < 4) || ($first_weekday && $weekday == 0)) {
1430
                $calc_year++;
1431
                return 1;
1432
            }
1433
        }
1434
        return floor($days/7+1);
1435
    }
1436
}
1437