Passed
Push — master ( 5b283a...d58ab5 )
by Richard
01:35
created

SqliteAdapter::lastInsertId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Maphper\DataSource;
3
4
class SqliteAdapter implements DatabaseAdapter {
5
	private $pdo;
6
	private $stmtCache;
7
8
	public function __construct(\PDO $pdo) {
9
		$this->pdo = $pdo;
10
        $this->stmtCache = new StmtCache($pdo);
11
	}
12
13
	public function quote($str) {
14
		return '`' . str_replace('.', '`.`', trim($str, '`')) . '`';
15
	}
16
17
	public function query(\Maphper\Lib\Query $query) {
18
        $stmt = $this->stmtCache->getCachedStmt($query->getSql());
19
		$args = $query->getArgs();
20
21
        //Handle SQLite when PDO_ERRMODE is set to SILENT
22
        if ($stmt === false) throw new \Exception('Invalid query');
23
24
        $stmt->execute($args);
25
        if ($stmt->errorCode() !== '00000' && $stmt->errorInfo()[2] == 'database schema has changed') {
26
			$this->stmtCache->deleteQueryFromCache($query->getSql());
27
			return $this->query($query);
28
        }
29
30
        return $stmt;
31
	}
32
	
33
	public function lastInsertId() {
34
		return $this->pdo->lastInsertId();
35
	}
36
	
37
	private function getType($val) {
38
		if ($val instanceof \DateTime) return 'DATETIME';
39
		else if (is_int($val)) return  'INTEGER';
40
		else if (is_double($val)) return 'DECIMAL(9,' . strlen($val) - strrpos($val, '.') - 1 . ')';
41
		else if (is_string($val) && strlen($val) < 256) return 'VARCHAR(255)';
42
		else if (is_string($val) && strlen($val) > 256) return 'LONGBLOG';
43
		else return 'VARCHAR(255)';		
44
	}
45
46
	private function tableExists($name) {
47
		$result = $this->pdo->query('SELECT name FROM sqlite_master WHERE type="table" and name="'. $name.'"');
48
		return count($result->fetchAll()) == 1;
49
	}
50
51
	private function getColumns($table) {
52
		$result = $this->pdo->query('PRAGMA table_info(' . $table . ');')->fetchAll(\PDO::FETCH_OBJ);
53
		$return = [];
54
		foreach ($result as $row) {
55
			$return[] = $row->name;
56
		}
57
		return $return;
58
	}
59
60
	//Alter the database so that it can store $data
61
	public function alterDatabase($table, array $primaryKey, $data) {
62
		//Unset query cache, otherwise it causes:
63
		// SQLSTATE[HY000]: General error: 17 database schema has changed
64
		$this->queryCache = [];
0 ignored issues
show
Bug Best Practice introduced by
The property queryCache does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
65
66
		$affix = '_'.substr(md5($table), 0, 6);
67
		$this->createTable($table . $affix, $primaryKey, $data);
68
		$fields = [];
69
		foreach ($data as $key => $value) { $fields[] = $key; }
70
		try {
71
			if ($this->tableExists($table)) {
72
				$columns = implode(', ', $this->getColumns($table));			
73
74
				$this->pdo->query('INSERT INTO ' . $this->quote($table . $affix) . '(' . $columns . ') SELECT ' . $columns . ' FROM ' . $this->quote($table));
75
				$this->pdo->query('DROP TABLE IF EXISTS ' . $table );
76
			}
77
		}
78
		catch (\PDOException $e) {
79
			// No data to copy
80
			echo $e->getMessage();
81
		}
82
83
		$this->pdo->query('DROP TABLE IF EXISTS ' . $table );
84
		$this->pdo->query('ALTER TABLE ' . $table . $affix. ' RENAME TO '. $table );
85
86
	}
87
88
	public function createTable($table, array $primaryKey, $data) {
89
		$parts = [];
90
		foreach ($primaryKey as $key) {
91
			$pk = $data->$key;
92
			if ($pk == null) $parts[] = $key . ' INTEGER'; 
93
			else $parts[] = $key . ' ' . $this->getType($pk) . ' NOT NULL';					
94
		}
95
		
96
		$pkField = implode(', ', $parts) . ', PRIMARY KEY(' . implode(', ', $primaryKey) . ')';
97
				
98
		$this->pdo->query('DROP TABLE IF EXISTS ' . $table );
99
		$this->pdo->query('CREATE TABLE ' . $table . ' (' . $pkField . ')');
100
					
101
		foreach ($data as $key => $value) {
102
			if (is_array($value) || (is_object($value) && !($value instanceof \DateTime))) continue;
103
			if (in_array($key, $primaryKey)) continue;
104
105
			$type = $this->getType($value);
106
		
107
			$this->pdo->query('ALTER TABLE ' . $table . ' ADD ' . $this->quote($key) . ' ' . $type);
108
		}
109
	}
110
	
111
112
	public function addIndex($table, array $fields) {
113
		if (empty($fields)) return false;
114
		
115
		//SQLite doesn't support ASC/DESC indexes, remove the keywords
116
		foreach ($fields as &$field) $field = str_ireplace([' desc', ' asc'], '', $field);
117
		sort($fields);
118
		$fields = array_map('strtolower', $fields);
119
		$fields = array_map('trim', $fields);
120
		$keyName = implode('_', $fields);
121
	
122
		
123
		try {
124
			$this->pdo->query('CREATE INDEX IF NOT EXISTS  ' . $keyName . ' ON ' . $table . ' (' . implode(', ', $fields) . ')');
125
		}
126
		catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
127
			
128
		}
129
	}
130
	
131
	public function optimiseColumns($table) {
132
		//TODO
133
	}
134
}