Completed
Pull Request — release-2.1 (#5969)
by John
04:27
created

postgres_cache   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Importance

Changes 5
Bugs 1 Features 0
Metric Value
eloc 58
c 5
b 1
f 0
dl 0
loc 177
rs 10
wmc 20

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A housekeeping() 0 6 1
A createTempTable() 0 3 1
A retrieveData() 0 3 1
A getData() 0 10 2
A cleanCache() 0 5 1
A connect() 0 30 2
A deleteTempTable() 0 3 1
A putData() 0 10 3
A prepareQuery() 0 10 2
A isSupported() 0 14 4
A getVersion() 0 3 1
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines https://www.simplemachines.org
8
 * @copyright 2020 Simple Machines and individual contributors
9
 * @license https://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 RC2
12
 */
13
14
if (!defined('SMF'))
15
	die('Hacking attempt...');
16
17
/**
18
 * PostgreSQL Cache API class
19
 *
20
 * @package cacheAPI
21
 */
22
class postgres_cache extends cache_api
23
{
24
	/** @var string */
25
	private $db_prefix;
26
27
	/** @var resource result of pg_connect. */
28
	private $db_connection;
29
30
	public function __construct()
31
	{
32
		global $db_prefix, $db_connection;
33
34
		$this->db_prefix = $db_prefix;
35
		$this->db_connection = $db_connection;
36
37
		parent::__construct();
38
	}
39
40
	/**
41
	 * {@inheritDoc}
42
	 */
43
	public function connect()
44
	{
45
		$result = pg_query_params($this->db_connection, 'SELECT 1
46
			FROM   pg_tables
47
			WHERE  schemaname = $1
48
			AND    tablename = $2',
49
			array(
50
				'public',
51
				$this->db_prefix . 'cache',
52
			)
53
		);
54
55
		if (pg_affected_rows($result) === 0)
56
			pg_query($this->db_connection, 'CREATE UNLOGGED TABLE ' . $this->db_prefix . 'cache (key text, value text, ttl bigint, PRIMARY KEY (key))');
57
58
		$this->prepareQuery(
59
			'smf_cache_get_data',
60
			'SELECT value FROM ' . $this->db_prefix . 'cache WHERE key = $1 AND ttl >= $2 LIMIT 1'
61
		);
62
		$this->prepareQuery(
63
			'smf_cache_put_data',
64
			'INSERT INTO ' . $this->db_prefix . 'cache(key,value,ttl) VALUES($1,$2,$3)
65
			ON CONFLICT(key) DO UPDATE SET value = $2, ttl = $3'
66
		);
67
		$this->prepareQuery(
68
			'smf_cache_delete_data',
69
			'DELETE FROM ' . $this->db_prefix . 'cache WHERE key = $1'
70
		);
71
72
		return true;
73
	}
74
75
	/**
76
	 * Stores a prepared SQL statement, ensuring that it's not done twice.
77
	 *
78
	 * @param string $stmtname
79
	 * @param string $query
80
	 */
81
	private function prepareQuery($stmtname, $query)
82
	{
83
		$result = pg_query_params(
84
			$this->db_connection,
85
			'SELECT name FROM pg_prepared_statements WHERE name = $1',
86
			array($stmtname)
87
		);
88
89
		if (pg_num_rows($result) == 0)
90
			pg_prepare($this->db_connection, $stmtname, $query);
91
	}
92
93
	/**
94
	 * {@inheritDoc}
95
	 */
96
	public function isSupported($test = false)
97
	{
98
		global $smcFunc;
99
100
		if ($smcFunc['db_title'] !== POSTGRE_TITLE)
101
			return false;
102
103
		$result = pg_query($this->db_connection, 'SHOW server_version_num');
104
		$res = pg_fetch_assoc($result);
105
106
		if ($res['server_version_num'] < 90500)
107
			return false;
108
109
		return $test ? true : parent::isSupported();
110
	}
111
112
	/**
113
	 * {@inheritDoc}
114
	 */
115
	public function getData($key, $ttl = null)
116
	{
117
		$result = pg_execute($this->db_connection, 'smf_cache_get_data', array($key, time()));
118
119
		if (pg_affected_rows($result) === 0)
120
			return null;
121
122
		$res = pg_fetch_assoc($result);
123
124
		return $res['value'];
125
	}
126
127
	/**
128
	 * {@inheritDoc}
129
	 */
130
	public function putData($key, $value, $ttl = null)
131
	{
132
		$ttl = time() + (int) ($ttl !== null ? $ttl : $this->ttl);
133
134
		if ($value === null)
135
			$result = pg_execute($this->db_connection, 'smf_cache_delete_data', array($key));
136
		else
137
			$result = pg_execute($this->db_connection, 'smf_cache_put_data', array($key, $value, $ttl));
138
139
		return pg_affected_rows($result) > 0;
140
	}
141
142
	/**
143
	 * {@inheritDoc}
144
	 */
145
	public function cleanCache($type = '')
146
	{
147
		pg_query($this->db_connection, 'TRUNCATE ' . $this->db_prefix . 'cache');
148
149
		return true;
150
	}
151
152
	/**
153
	 * {@inheritDoc}
154
	 */
155
	public function getVersion()
156
	{
157
		return pg_version()['server'];
158
	}
159
160
	/**
161
	 * {@inheritDoc}
162
	 */
163
	public function housekeeping()
164
	{
165
		$this->createTempTable();
166
		$this->cleanCache();
167
		$this->retrieveData();
168
		$this->deleteTempTable();
169
	}
170
171
	/**
172
	 * Create the temp table of valid data.
173
	 *
174
	 * @return void
175
	 */
176
	private function createTempTable()
177
	{
178
		pg_query($this->db_connection, 'CREATE LOCAL TEMP TABLE IF NOT EXISTS ' . $this->db_prefix . 'cache_tmp AS SELECT * FROM ' . $this->db_prefix . 'cache WHERE ttl >= ' . time());
179
	}
180
181
	/**
182
	 * Delete the temp table.
183
	 *
184
	 * @return void
185
	 */
186
	private function deleteTempTable()
187
	{
188
		pg_query($this->db_connection, 'DROP TABLE IF EXISTS ' . $this->db_prefix . 'cache_tmp');
189
	}
190
191
	/**
192
	 * Retrieve the valid data from temp table.
193
	 *
194
	 * @return void
195
	 */
196
	private function retrieveData()
197
	{
198
		pg_query($this->db_connection, 'INSERT INTO ' . $this->db_prefix . 'cache SELECT * FROM ' . $this->db_prefix . 'cache_tmp ON CONFLICT DO NOTHING');
199
	}
200
}
201
202
?>