Passed
Pull Request — release-2.1 (#5969)
by John
05:14
created

postgres_cache::putData()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 4
nop 3
dl 0
loc 10
rs 10
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
		{
57
			pg_query($this->db_connection, 'CREATE UNLOGGED TABLE ' . $this->db_prefix . 'cache (key text, value text, ttl bigint, PRIMARY KEY (key))');
58
59
			$this->prepareQuery(
60
				'smf_cache_get_data',
61
				'SELECT value FROM ' . $this->db_prefix . 'cache WHERE key = $1 AND ttl >= $2 LIMIT 1'
62
			);
63
			$this->prepareQuery(
64
				'smf_cache_put_data',
65
				'INSERT INTO ' . $this->db_prefix . 'cache(key,value,ttl) VALUES($1,$2,$3)
66
				ON CONFLICT(key) DO UPDATE SET value = $2, ttl = $3'
67
			);
68
			$this->prepareQuery(
69
				'smf_cache_delete_data',
70
				'DELETE FROM ' . $this->db_prefix . 'cache WHERE key = $1'
71
			);
72
		}
73
74
		return true;
75
	}
76
77
	/**
78
	 * Stores a prepared SQL statement, ensuring that it's not done twice.
79
	 *
80
	 * @param string $stmtname
81
	 * @param string $query
82
	 */
83
	private function prepareQuery($stmtname, $query)
84
	{
85
		$result = pg_query_params(
86
			$this->db_connection,
87
			'SELECT name FROM pg_prepared_statements WHERE name = $1',
88
			array($stmtname)
89
		);
90
91
		if (pg_num_rows($result) == 0)
92
			pg_prepare($this->db_connection, $stmtname, $query);
93
	}
94
95
	/**
96
	 * {@inheritDoc}
97
	 */
98
	public function isSupported($test = false)
99
	{
100
		global $smcFunc;
101
102
		if ($smcFunc['db_title'] !== POSTGRE_TITLE)
103
			return false;
104
105
		$result = pg_query($this->db_connection, 'SHOW server_version_num');
106
		$res = pg_fetch_assoc($result);
107
108
		if ($res['server_version_num'] < 90500)
109
			return false;
110
111
		return $test ? true : parent::isSupported();
112
	}
113
114
	/**
115
	 * {@inheritDoc}
116
	 */
117
	public function getData($key, $ttl = null)
118
	{
119
		$result = pg_execute($this->db_connection, 'smf_cache_get_data', array($key, time()));
120
121
		if (pg_affected_rows($result) === 0)
122
			return null;
123
124
		$res = pg_fetch_assoc($result);
125
126
		return $res['value'];
127
	}
128
129
	/**
130
	 * {@inheritDoc}
131
	 */
132
	public function putData($key, $value, $ttl = null)
133
	{
134
		$ttl = time() + (int) ($ttl !== null ? $ttl : $this->ttl);
135
136
		if ($value === null)
137
			$result = pg_execute($this->db_connection, 'smf_cache_delete_data', array($key));
138
		else
139
			$result = pg_execute($this->db_connection, 'smf_cache_put_data', array($key, $value, $ttl));
140
141
		return pg_affected_rows($result) > 0;
142
	}
143
144
	/**
145
	 * {@inheritDoc}
146
	 */
147
	public function cleanCache($type = '')
148
	{
149
		pg_query($this->db_connection, 'TRUNCATE ' . $this->db_prefix . 'cache');
150
151
		return true;
152
	}
153
154
	/**
155
	 * {@inheritDoc}
156
	 */
157
	public function getVersion()
158
	{
159
		return pg_version()['server'];
160
	}
161
162
	/**
163
	 * {@inheritDoc}
164
	 */
165
	public function housekeeping()
166
	{
167
		$this->createTempTable();
168
		$this->cleanCache();
169
		$this->retrieveData();
170
		$this->deleteTempTable();
171
	}
172
173
	/**
174
	 * Create the temp table of valid data.
175
	 *
176
	 * @return void
177
	 */
178
	private function createTempTable()
179
	{
180
		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());
181
	}
182
183
	/**
184
	 * Delete the temp table.
185
	 *
186
	 * @return void
187
	 */
188
	private function deleteTempTable()
189
	{
190
		pg_query($this->db_connection, 'DROP TABLE IF EXISTS ' . $this->db_prefix . 'cache_tmp');
191
	}
192
193
	/**
194
	 * Retrieve the valid data from temp table.
195
	 *
196
	 * @return void
197
	 */
198
	private function retrieveData()
199
	{
200
		pg_query($this->db_connection, 'INSERT INTO ' . $this->db_prefix . 'cache SELECT * FROM ' . $this->db_prefix . 'cache_tmp ON CONFLICT DO NOTHING');
201
	}
202
}
203
204
?>