Completed
Push — master ( 58937a...81ac23 )
by El
07:13
created

Database   C

Complexity

Total Complexity 78

Size/Duplication

Total Lines 680
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 96.92%

Importance

Changes 0
Metric Value
wmc 78
lcom 1
cbo 1
dl 0
loc 680
ccs 283
cts 292
cp 0.9692
rs 5.0984
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
C getInstance() 0 78 13
B create() 0 53 8
C read() 0 60 13
A delete() 0 16 2
A exists() 0 9 2
A createComment() 0 21 3
B readComments() 0 28 6
A existsComment() 0 8 1
A _getExpiredPastes() 0 14 3
A _exec() 0 7 1
A _select() 0 10 2
C _getTableQuery() 0 45 8
A _getConfig() 0 8 1
A _getPrimaryKeyClauses() 0 10 2
A _createPasteTable() 0 17 3
A _createCommentTable() 0 19 2
A _createConfigTable() 0 13 1
A _sanitizeIdentifier() 0 4 1
B _upgradeDatabase() 0 55 6

How to fix   Complexity   

Complex Class

Complex classes like Database often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Database, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * PrivateBin
4
 *
5
 * a zero-knowledge paste bin
6
 *
7
 * @link      https://github.com/PrivateBin/PrivateBin
8
 * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
9
 * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
10
 * @version   1.1.1
11
 */
12
13
namespace PrivateBin\Data;
14
15
use Exception;
16
use PDO;
17
use PDOException;
18
use PrivateBin\PrivateBin;
19
use stdClass;
20
21
/**
22
 * Database
23
 *
24
 * Model for database access, implemented as a singleton.
25
 */
26
class Database extends AbstractData
27
{
28
    /**
29
     * cache for select queries
30
     *
31
     * @var array
32
     */
33
    private static $_cache = array();
34
35
    /**
36
     * instance of database connection
37
     *
38
     * @access private
39
     * @static
40
     * @var PDO
41
     */
42
    private static $_db;
43
44
    /**
45
     * table prefix
46
     *
47
     * @access private
48
     * @static
49
     * @var string
50
     */
51
    private static $_prefix = '';
52
53
    /**
54
     * database type
55
     *
56
     * @access private
57
     * @static
58
     * @var string
59
     */
60
    private static $_type = '';
61
62
    /**
63
     * get instance of singleton
64
     *
65
     * @access public
66
     * @static
67
     * @param  array $options
68
     * @throws Exception
69
     * @return Database
70
     */
71 75
    public static function getInstance($options = null)
72
    {
73
        // if needed initialize the singleton
74 75
        if (!(self::$_instance instanceof self)) {
75 46
            self::$_instance = new self;
76
        }
77
78 75
        if (is_array($options)) {
79
            // set table prefix if given
80 75
            if (array_key_exists('tbl', $options)) {
81 54
                self::$_prefix = $options['tbl'];
82
            }
83
84
            // initialize the db connection with new options
85
            if (
86 75
                array_key_exists('dsn', $options) &&
87 75
                array_key_exists('usr', $options) &&
88 75
                array_key_exists('pwd', $options) &&
89 75
                array_key_exists('opt', $options)
90
            ) {
91
                // set default options
92 75
                $options['opt'][PDO::ATTR_ERRMODE]          = PDO::ERRMODE_EXCEPTION;
93 75
                $options['opt'][PDO::ATTR_EMULATE_PREPARES] = false;
94 75
                $options['opt'][PDO::ATTR_PERSISTENT]       = true;
95 75
                $db_tables_exist                            = true;
96
97
                // setup type and dabase connection
98 75
                self::$_type = strtolower(
99 75
                    substr($options['dsn'], 0, strpos($options['dsn'], ':'))
100
                );
101 75
                $tableQuery = self::_getTableQuery(self::$_type);
102 75
                self::$_db  = new PDO(
103 75
                    $options['dsn'],
104 75
                    $options['usr'],
105 75
                    $options['pwd'],
106 75
                    $options['opt']
107
                );
108
109
                // check if the database contains the required tables
110 75
                $tables = self::$_db->query($tableQuery)->fetchAll(PDO::FETCH_COLUMN, 0);
111
112
                // create paste table if necessary
113 75
                if (!in_array(self::_sanitizeIdentifier('paste'), $tables)) {
114 48
                    self::_createPasteTable();
115 48
                    $db_tables_exist = false;
116
                }
117
118
                // create comment table if necessary
119 75
                if (!in_array(self::_sanitizeIdentifier('comment'), $tables)) {
120 48
                    self::_createCommentTable();
121 47
                    $db_tables_exist = false;
122
                }
123
124
                // create config table if necessary
125 75
                $db_version = PrivateBin::VERSION;
126 75
                if (!in_array(self::_sanitizeIdentifier('config'), $tables)) {
127 47
                    self::_createConfigTable();
128
                    // if we only needed to create the config table, the DB is older then 0.22
129 47
                    if ($db_tables_exist) {
130 47
                        $db_version = '0.21';
131
                    }
132
                } else {
133 66
                    $db_version = self::_getConfig('VERSION');
134
                }
135
136
                // update database structure if necessary
137 75
                if (version_compare($db_version, PrivateBin::VERSION, '<')) {
138 75
                    self::_upgradeDatabase($db_version);
139
                }
140
            } else {
141 4
                throw new Exception(
142 4
                    'Missing configuration for key dsn, usr, pwd or opt in the section model_options, please check your configuration file', 6
143
                );
144
            }
145
        }
146
147 75
        return self::$_instance;
148
    }
149
150
    /**
151
     * Create a paste.
152
     *
153
     * @access public
154
     * @param  string $pasteid
155
     * @param  array  $paste
156
     * @return bool
157
     */
158 45
    public function create($pasteid, $paste)
159
    {
160
        if (
161 45
            array_key_exists($pasteid, self::$_cache)
162
        ) {
163 45
            if (false !== self::$_cache[$pasteid]) {
164 2
                return false;
165
            } else {
166 45
                unset(self::$_cache[$pasteid]);
167
            }
168
        }
169
170 45
        $opendiscussion = $burnafterreading = false;
171 45
        $attachment     = $attachmentname     = '';
172 45
        $meta           = $paste['meta'];
173 45
        unset($meta['postdate']);
174 45
        $expire_date = 0;
175 45
        if (array_key_exists('expire_date', $paste['meta'])) {
176 10
            $expire_date = (int) $paste['meta']['expire_date'];
177 10
            unset($meta['expire_date']);
178
        }
179 45
        if (array_key_exists('opendiscussion', $paste['meta'])) {
180 32
            $opendiscussion = (bool) $paste['meta']['opendiscussion'];
181 32
            unset($meta['opendiscussion']);
182
        }
183 45
        if (array_key_exists('burnafterreading', $paste['meta'])) {
184 5
            $burnafterreading = (bool) $paste['meta']['burnafterreading'];
185 5
            unset($meta['burnafterreading']);
186
        }
187 45
        if (array_key_exists('attachment', $paste['meta'])) {
188 2
            $attachment = $paste['meta']['attachment'];
189 2
            unset($meta['attachment']);
190
        }
191 45
        if (array_key_exists('attachmentname', $paste['meta'])) {
192 2
            $attachmentname = $paste['meta']['attachmentname'];
193 2
            unset($meta['attachmentname']);
194
        }
195 45
        return self::_exec(
196 45
            'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
197 45
            ' VALUES(?,?,?,?,?,?,?,?,?)',
198
            array(
199 45
                $pasteid,
200 45
                $paste['data'],
201 45
                $paste['meta']['postdate'],
202 45
                $expire_date,
203 45
                (int) $opendiscussion,
204 45
                (int) $burnafterreading,
205 45
                json_encode($meta),
206 45
                $attachment,
207 45
                $attachmentname,
208
            )
209
        );
210
    }
211
212
    /**
213
     * Read a paste.
214
     *
215
     * @access public
216
     * @param  string $pasteid
217
     * @return stdClass|false
218
     */
219 61
    public function read($pasteid)
220
    {
221
        if (
222 61
            !array_key_exists($pasteid, self::$_cache)
223
        ) {
224 61
            self::$_cache[$pasteid] = false;
225 61
            $paste                  = self::_select(
226 61
                'SELECT * FROM ' . self::_sanitizeIdentifier('paste') .
227 61
                ' WHERE dataid = ?', array($pasteid), true
228
            );
229
230 61
            if (false !== $paste) {
231
                // create object
232 44
                self::$_cache[$pasteid]       = new stdClass;
233 44
                self::$_cache[$pasteid]->data = $paste['data'];
234
235 44
                $meta = json_decode($paste['meta']);
236 44
                if (!is_object($meta)) {
237
                    $meta = new stdClass;
238
                }
239
240
                // support older attachments
241 44
                if (property_exists($meta, 'attachment')) {
242 1
                    self::$_cache[$pasteid]->attachment = $meta->attachment;
243 1
                    unset($meta->attachment);
244 1
                    if (property_exists($meta, 'attachmentname')) {
245 1
                        self::$_cache[$pasteid]->attachmentname = $meta->attachmentname;
246 1
                        unset($meta->attachmentname);
247
                    }
248
                }
249
                // support current attachments
250 43
                elseif (array_key_exists('attachment', $paste) && strlen($paste['attachment'])) {
251 2
                    self::$_cache[$pasteid]->attachment = $paste['attachment'];
252 2
                    if (array_key_exists('attachmentname', $paste) && strlen($paste['attachmentname'])) {
253 2
                        self::$_cache[$pasteid]->attachmentname = $paste['attachmentname'];
254
                    }
255
                }
256 44
                self::$_cache[$pasteid]->meta           = $meta;
257 44
                self::$_cache[$pasteid]->meta->postdate = (int) $paste['postdate'];
258 44
                $expire_date                            = (int) $paste['expiredate'];
259
                if (
260 44
                    $expire_date > 0
261
                ) {
262 11
                    self::$_cache[$pasteid]->meta->expire_date = $expire_date;
263
                }
264
                if (
265 44
                    $paste['opendiscussion']
266
                ) {
267 31
                    self::$_cache[$pasteid]->meta->opendiscussion = true;
268
                }
269
                if (
270 44
                    $paste['burnafterreading']
271
                ) {
272 5
                    self::$_cache[$pasteid]->meta->burnafterreading = true;
273
                }
274
            }
275
        }
276
277 61
        return self::$_cache[$pasteid];
278
    }
279
280
    /**
281
     * Delete a paste and its discussion.
282
     *
283
     * @access public
284
     * @param  string $pasteid
285
     */
286 23
    public function delete($pasteid)
287
    {
288 23
        self::_exec(
289 23
            'DELETE FROM ' . self::_sanitizeIdentifier('paste') .
290 23
            ' WHERE dataid = ?', array($pasteid)
291
        );
292 23
        self::_exec(
293 23
            'DELETE FROM ' . self::_sanitizeIdentifier('comment') .
294 23
            ' WHERE pasteid = ?', array($pasteid)
295
        );
296
        if (
297 23
            array_key_exists($pasteid, self::$_cache)
298
        ) {
299 21
            unset(self::$_cache[$pasteid]);
300
        }
301 23
    }
302
303
    /**
304
     * Test if a paste exists.
305
     *
306
     * @access public
307
     * @param  string $pasteid
308
     * @return bool
309
     */
310 61
    public function exists($pasteid)
311
    {
312
        if (
313 61
            !array_key_exists($pasteid, self::$_cache)
314
        ) {
315 60
            self::$_cache[$pasteid] = $this->read($pasteid);
316
        }
317 61
        return (bool) self::$_cache[$pasteid];
318
    }
319
320
    /**
321
     * Create a comment in a paste.
322
     *
323
     * @access public
324
     * @param  string $pasteid
325
     * @param  string $parentid
326
     * @param  string $commentid
327
     * @param  array  $comment
328
     * @return bool
329
     */
330 9
    public function createComment($pasteid, $parentid, $commentid, $comment)
331
    {
332 9
        foreach (array('nickname', 'vizhash') as $key) {
333 9
            if (!array_key_exists($key, $comment['meta'])) {
334 9
                $comment['meta'][$key] = null;
335
            }
336
        }
337 9
        return self::_exec(
338 9
            'INSERT INTO ' . self::_sanitizeIdentifier('comment') .
339 9
            ' VALUES(?,?,?,?,?,?,?)',
340
            array(
341 9
                $commentid,
342 9
                $pasteid,
343 9
                $parentid,
344 9
                $comment['data'],
345 9
                $comment['meta']['nickname'],
346 9
                $comment['meta']['vizhash'],
347 9
                $comment['meta']['postdate'],
348
            )
349
        );
350
    }
351
352
    /**
353
     * Read all comments of paste.
354
     *
355
     * @access public
356
     * @param  string $pasteid
357
     * @return array
358
     */
359 21
    public function readComments($pasteid)
360
    {
361 21
        $rows = self::_select(
362 21
            'SELECT * FROM ' . self::_sanitizeIdentifier('comment') .
363 21
            ' WHERE pasteid = ?', array($pasteid)
364
        );
365
366
        // create comment list
367 21
        $comments = array();
368 21
        if (count($rows)) {
369 7
            foreach ($rows as $row) {
370 7
                $i                            = $this->getOpenSlot($comments, (int) $row['postdate']);
371 7
                $comments[$i]                 = new stdClass;
372 7
                $comments[$i]->id             = $row['dataid'];
373 7
                $comments[$i]->parentid       = $row['parentid'];
374 7
                $comments[$i]->data           = $row['data'];
375 7
                $comments[$i]->meta           = new stdClass;
376 7
                $comments[$i]->meta->postdate = (int) $row['postdate'];
377 7
                foreach (array('nickname', 'vizhash') as $key) {
378 7
                    if (array_key_exists($key, $row) && !empty($row[$key])) {
379 7
                        $comments[$i]->meta->$key = $row[$key];
380
                    }
381
                }
382
            }
383 7
            ksort($comments);
384
        }
385 21
        return $comments;
386
    }
387
388
    /**
389
     * Test if a comment exists.
390
     *
391
     * @access public
392
     * @param  string $pasteid
393
     * @param  string $parentid
394
     * @param  string $commentid
395
     * @return bool
396
     */
397 12
    public function existsComment($pasteid, $parentid, $commentid)
398
    {
399 12
        return (bool) self::_select(
400 12
            'SELECT dataid FROM ' . self::_sanitizeIdentifier('comment') .
401 12
            ' WHERE pasteid = ? AND parentid = ? AND dataid = ?',
402 12
            array($pasteid, $parentid, $commentid), true
403
        );
404
    }
405
406
    /**
407
     * Returns up to batch size number of paste ids that have expired
408
     *
409
     * @access private
410
     * @param  int $batchsize
411
     * @return array
412
     */
413 2
    protected function _getExpiredPastes($batchsize)
414
    {
415 2
        $pastes = array();
416 2
        $rows   = self::_select(
417 2
            'SELECT dataid FROM ' . self::_sanitizeIdentifier('paste') .
418 2
            ' WHERE expiredate < ? AND expiredate != ? LIMIT ?', array(time(), 0, $batchsize)
419
        );
420 2
        if (count($rows)) {
421 2
            foreach ($rows as $row) {
422 2
                $pastes[] = $row['dataid'];
423
            }
424
        }
425 2
        return $pastes;
426
    }
427
428
    /**
429
     * execute a statement
430
     *
431
     * @access private
432
     * @static
433
     * @param  string $sql
434
     * @param  array $params
435
     * @throws PDOException
436
     * @return bool
437
     */
438 62
    private static function _exec($sql, array $params)
439
    {
440 62
        $statement = self::$_db->prepare($sql);
441 62
        $result    = $statement->execute($params);
442 62
        $statement->closeCursor();
443 62
        return $result;
444
    }
445
446
    /**
447
     * run a select statement
448
     *
449
     * @access private
450
     * @static
451
     * @param  string $sql
452
     * @param  array $params
453
     * @param  bool $firstOnly if only the first row should be returned
454
     * @throws PDOException
455
     * @return array
456
     */
457 73
    private static function _select($sql, array $params, $firstOnly = false)
458
    {
459 73
        $statement = self::$_db->prepare($sql);
460 73
        $statement->execute($params);
461 73
        $result = $firstOnly ?
462 73
            $statement->fetch(PDO::FETCH_ASSOC) :
463 73
            $statement->fetchAll(PDO::FETCH_ASSOC);
464 73
        $statement->closeCursor();
465 73
        return $result;
466
    }
467
468
    /**
469
     * get table list query, depending on the database type
470
     *
471
     * @access private
472
     * @static
473
     * @param  string $type
474
     * @throws Exception
475
     * @return string
476
     */
477 75
    private static function _getTableQuery($type)
478
    {
479
        switch ($type) {
480 75
            case 'ibm':
481 1
                $sql = 'SELECT tabname FROM SYSCAT.TABLES ';
482 1
                break;
483 75
            case 'informix':
484 1
                $sql = 'SELECT tabname FROM systables ';
485 1
                break;
486 75
            case 'mssql':
487
                $sql = 'SELECT name FROM sysobjects '
488 1
                     . "WHERE type = 'U' ORDER BY name";
489 1
                break;
490 75
            case 'mysql':
491 1
                $sql = 'SHOW TABLES';
492 1
                break;
493 75
            case 'oci':
494 1
                $sql = 'SELECT table_name FROM all_tables';
495 1
                break;
496 75
            case 'pgsql':
497
                $sql = 'SELECT c.relname AS table_name '
498
                     . 'FROM pg_class c, pg_user u '
499
                     . "WHERE c.relowner = u.usesysid AND c.relkind = 'r' "
500
                     . 'AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) '
501
                     . "AND c.relname !~ '^(pg_|sql_)' "
502
                     . 'UNION '
503
                     . 'SELECT c.relname AS table_name '
504
                     . 'FROM pg_class c '
505
                     . "WHERE c.relkind = 'r' "
506
                     . 'AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) '
507
                     . 'AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) '
508 1
                     . "AND c.relname !~ '^pg_'";
509 1
                break;
510 75
            case 'sqlite':
511
                $sql = "SELECT name FROM sqlite_master WHERE type='table' "
512
                     . 'UNION ALL SELECT name FROM sqlite_temp_master '
513 75
                     . "WHERE type='table' ORDER BY name";
514 75
                break;
515
            default:
516 1
                throw new Exception(
517 1
                    "PDO type $type is currently not supported.", 5
518
                );
519
        }
520 75
        return $sql;
521
    }
522
523
    /**
524
     * get a value by key from the config table
525
     *
526
     * @access private
527
     * @static
528
     * @param  string $key
529
     * @throws PDOException
530
     * @return string
531
     */
532 66
    private static function _getConfig($key)
533
    {
534 66
        $row = self::_select(
535 66
            'SELECT value FROM ' . self::_sanitizeIdentifier('config') .
536 66
            ' WHERE id = ?', array($key), true
537
        );
538 66
        return $row['value'];
539
    }
540
541
    /**
542
     * get the primary key clauses, depending on the database driver
543
     *
544
     * @access private
545
     * @static
546
     * @param string $key
547
     * @return array
548
     */
549 48
    private static function _getPrimaryKeyClauses($key = 'dataid')
550
    {
551 48
        $main_key = $after_key = '';
552 48
        if (self::$_type === 'mysql') {
553
            $after_key = ", PRIMARY KEY ($key)";
554
        } else {
555 48
            $main_key = ' PRIMARY KEY';
556
        }
557 48
        return array($main_key, $after_key);
558
    }
559
560
    /**
561
     * create the paste table
562
     *
563
     * @access private
564
     * @static
565
     */
566 48
    private static function _createPasteTable()
567
    {
568 48
        list($main_key, $after_key) = self::_getPrimaryKeyClauses();
569 48
        $dataType                   = self::$_type === 'pgsql' ? 'TEXT' : 'BLOB';
570 48
        self::$_db->exec(
571 48
            'CREATE TABLE ' . self::_sanitizeIdentifier('paste') . ' ( ' .
572 48
            "dataid CHAR(16) NOT NULL$main_key, " .
573 48
            "data $dataType, " .
574 48
            'postdate INT, ' .
575 48
            'expiredate INT, ' .
576 48
            'opendiscussion INT, ' .
577 48
            'burnafterreading INT, ' .
578 48
            'meta TEXT, ' .
579 48
            'attachment ' . (self::$_type === 'pgsql' ? 'TEXT' : 'MEDIUMBLOB') . ', ' .
580 48
            "attachmentname $dataType$after_key );"
581
        );
582 48
    }
583
584
    /**
585
     * create the paste table
586
     *
587
     * @access private
588
     * @static
589
     */
590 48
    private static function _createCommentTable()
591
    {
592 48
        list($main_key, $after_key) = self::_getPrimaryKeyClauses();
593 48
        $dataType                   = self::$_type === 'pgsql' ? 'text' : 'BLOB';
594 48
        self::$_db->exec(
595 48
            'CREATE TABLE ' . self::_sanitizeIdentifier('comment') . ' ( ' .
596 48
            "dataid CHAR(16) NOT NULL$main_key, " .
597 48
            'pasteid CHAR(16), ' .
598 48
            'parentid CHAR(16), ' .
599 48
            "data $dataType, " .
600 48
            "nickname $dataType, " .
601 48
            "vizhash $dataType, " .
602 48
            "postdate INT$after_key );"
603
        );
604 48
        self::$_db->exec(
605
            'CREATE INDEX IF NOT EXISTS comment_parent ON ' .
606 48
            self::_sanitizeIdentifier('comment') . '(pasteid);'
607
        );
608 47
    }
609
610
    /**
611
     * create the paste table
612
     *
613
     * @access private
614
     * @static
615
     */
616 47
    private static function _createConfigTable()
617
    {
618 47
        list($main_key, $after_key) = self::_getPrimaryKeyClauses('id');
619 47
        self::$_db->exec(
620 47
            'CREATE TABLE ' . self::_sanitizeIdentifier('config') .
621 47
            " ( id CHAR(16) NOT NULL$main_key, value TEXT$after_key );"
622
        );
623 47
        self::_exec(
624 47
            'INSERT INTO ' . self::_sanitizeIdentifier('config') .
625 47
            ' VALUES(?,?)',
626 47
            array('VERSION', PrivateBin::VERSION)
627
        );
628 47
    }
629
630
    /**
631
     * sanitizes identifiers
632
     *
633
     * @access private
634
     * @static
635
     * @param  string $identifier
636
     * @return string
637
     */
638 75
    private static function _sanitizeIdentifier($identifier)
639
    {
640 75
        return preg_replace('/[^A-Za-z0-9_]+/', '', self::$_prefix . $identifier);
641
    }
642
643
    /**
644
     * upgrade the database schema from an old version
645
     *
646
     * @access private
647
     * @static
648
     * @param  string $oldversion
649
     */
650 1
    private static function _upgradeDatabase($oldversion)
651
    {
652 1
        $dataType = self::$_type === 'pgsql' ? 'TEXT' : 'BLOB';
653
        switch ($oldversion) {
654 1
            case '0.21':
655
                // create the meta column if necessary (pre 0.21 change)
656
                try {
657 1
                    self::$_db->exec('SELECT meta FROM ' . self::_sanitizeIdentifier('paste') . ' LIMIT 1;');
658 1
                } catch (PDOException $e) {
659 1
                    self::$_db->exec('ALTER TABLE ' . self::_sanitizeIdentifier('paste') . ' ADD COLUMN meta TEXT;');
660
                }
661
                // SQLite only allows one ALTER statement at a time...
662 1
                self::$_db->exec(
663 1
                    'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
664 1
                    ' ADD COLUMN attachment ' .
665 1
                    (self::$_type === 'pgsql' ? 'TEXT' : 'MEDIUMBLOB') . ';'
666
                );
667 1
                self::$_db->exec(
668 1
                    'ALTER TABLE ' . self::_sanitizeIdentifier('paste') . " ADD COLUMN attachmentname $dataType;"
669
                );
670
                // SQLite doesn't support MODIFY, but it allows TEXT of similar
671
                // size as BLOB, so there is no need to change it there
672 1
                if (self::$_type !== 'sqlite') {
673
                    self::$_db->exec(
674
                        'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
675
                        ' ADD PRIMARY KEY (dataid), MODIFY COLUMN data $dataType;'
676
                    );
677
                    self::$_db->exec(
678
                        'ALTER TABLE ' . self::_sanitizeIdentifier('comment') .
679
                        " ADD PRIMARY KEY (dataid), MODIFY COLUMN data $dataType, " .
680
                        "MODIFY COLUMN nickname $dataType, MODIFY COLUMN vizhash $dataType;"
681
                    );
682
                } else {
683 1
                    self::$_db->exec(
684
                        'CREATE UNIQUE INDEX IF NOT EXISTS paste_dataid ON ' .
685 1
                        self::_sanitizeIdentifier('paste') . '(dataid);'
686
                    );
687 1
                    self::$_db->exec(
688
                        'CREATE UNIQUE INDEX IF NOT EXISTS comment_dataid ON ' .
689 1
                        self::_sanitizeIdentifier('comment') . '(dataid);'
690
                    );
691
                }
692 1
                self::$_db->exec(
693
                    'CREATE INDEX IF NOT EXISTS comment_parent ON ' .
694 1
                    self::_sanitizeIdentifier('comment') . '(pasteid);'
695
                );
696
                // no break, continue with updates for 0.22 and later
697
            default:
698 1
                self::_exec(
699 1
                    'UPDATE ' . self::_sanitizeIdentifier('config') .
700 1
                    ' SET value = ? WHERE id = ?',
701 1
                    array(PrivateBin::VERSION, 'VERSION')
702
                );
703
        }
704 1
    }
705
}
706