Completed
Push — master ( 213254...a7e545 )
by Nazar
04:39
created

PostgreSQL::n()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs\DB;
9
class PostgreSQL extends _Abstract {
10
	/**
11
	 * @var resource DB connection handler
12
	 */
13
	protected $handler;
14
	/**
15
	 * @var resource
16
	 */
17
	protected $query_result;
18
	/**
19
	 * @inheritdoc
20
	 */
21
	function __construct ($database, $user = '', $password = '', $host = 'localhost', $prefix = '') {
22
		$start = microtime(true);
23
		/**
24
		 * Parsing of $host variable, detecting port and persistent connection
25
		 */
26
		list($host, $port, $persistent) = $this->get_host_port_and_persistent($host);
27
		$connection_string = "host=$host port=$port dbname=$database user=$user password=$password options='--client_encoding=UTF8'";
28
		$this->handler     = $persistent ? pg_connect($connection_string) : pg_pconnect($connection_string);
29
		if (!is_resource($this->handler)) {
30
			return;
31
		}
32
		$this->database        = $database;
33
		$this->connected       = true;
34
		$this->connecting_time = microtime(true) - $start;
35
		$this->db_type         = 'postgresql';
36
		$this->prefix          = $prefix;
37
	}
38
	/**
39
	 * Parse host string into host, port and persistent separately
40
	 *
41
	 * Understands `p:` prefix for persistent connections
42
	 *
43
	 * @param string $host_string
44
	 *
45
	 * @return array
46
	 */
47
	protected function get_host_port_and_persistent ($host_string) {
48
		$host       = explode(':', $host_string);
49
		$port       = 5432;
50
		$persistent = false;
51
		switch (count($host)) {
52
			case 1:
53
				$host = $host[0];
54
				break;
55
			case 2:
56
				if ($host[0] == 'p') {
57
					$persistent = true;
58
					$host       = $host[1];
59
				} else {
60
					list($host, $port) = $host;
61
				}
62
				break;
63
			case 3:
64
				$persistent = true;
65
				list(, $host, $port) = $host;
66
		}
67
		return [$host, $port, $persistent];
68
	}
69
	/**
70
	 * @inheritdoc
71
	 */
72
	function q ($query, $params = [], ...$param) {
73
		// Hack to convert small subset of MySQL queries into PostgreSQL-compatible syntax
74
		$query = str_replace('`', '"', $query);
75
		$query = preg_replace_callback(
76
			'/(INSERT IGNORE INTO|REPLACE INTO)(.+)(;|$)/Uis',
77
			function ($matches) {
78
				// Only support simplest cases
79
				if (stripos($matches[2], 'on duplicate')) {
80
					return $matches[0];
81
				}
82
				switch (strtoupper($matches[1])) {
83
					case 'INSERT IGNORE INTO':
84
						return "INSERT INTO $matches[2] ON CONFLICT DO NOTHING$matches[3]";
85
					case 'REPLACE INTO':
86
						$table_name = substr(
87
							$matches[2],
88
							strpos($matches[2], '"') + 1
89
						);
90
						$table_name = substr(
91
							$table_name,
92
							0,
93
							strpos($table_name, '"')
94
						);
95
						$update     = preg_replace_callback(
96
							'/"([^"]+)"/',
97
							function ($matches) {
98
								return "\"$matches[1]\" = EXCLUDED.\"$matches[1]\"";
99
							},
100
							substr(
101
								strstr($matches[2], ')', true),
102
								strpos($matches[2], '(') + 1
103
							)
104
						);
105
						// Only support constraint named as table with `_primary` prefix
106
						return "INSERT INTO $matches[2] ON CONFLICT ON CONSTRAINT \"{$table_name}_primary\" DO UPDATE SET $update$matches[3]";
107
				}
108
			},
109
			$query
110
		);
111
		return parent::q(...([$query] + func_get_args()));
112
	}
113
	/**
114
	 * @inheritdoc
115
	 *
116
	 * @return false|resource
117
	 */
118
	protected function q_internal ($query) {
119
		if (!$query) {
120
			return false;
121
		}
122
		return $this->query_result = pg_query($this->handler, $query);
123
	}
124
	/**
125
	 * @inheritdoc
126
	 */
127
	protected function q_multi_internal ($query) {
128
		return $this->q_internal(implode(';', $query));
129
	}
130
	/**
131
	 * @inheritdoc
132
	 *
133
	 * @param false|resource $query_result
134
	 */
135
	function f ($query_result, $single_column = false, $array = false, $indexed = false) {
136
		if (!is_resource($query_result)) {
137
			return false;
138
		}
139
		$result_type = $single_column || $indexed ? PGSQL_NUM : PGSQL_ASSOC;
140
		if ($array) {
141
			$result = [];
142
			while ($current = pg_fetch_array($query_result, null, $result_type)) {
143
				$result[] = $single_column ? $current[0] : $current;
144
			}
145
			$this->free($query_result);
146
			return $result;
147
		}
148
		$result = pg_fetch_array($query_result, null, $result_type);
149
		return $single_column && $result ? $result[0] : $result;
150
	}
151
	/**
152
	 * @inheritdoc
153
	 */
154
	function id () {
155
		return (int)$this->qfs('SELECT lastval()');
156
	}
157
	/**
158
	 * @inheritdoc
159
	 */
160
	function affected () {
161
		return is_resource($this->query_result) ? pg_affected_rows($this->query_result) : 0;
162
	}
163
	/**
164
	 * @inheritdoc
165
	 *
166
	 * @param false|resource $query_result
167
	 */
168
	function free ($query_result) {
169
		if (is_resource($query_result)) {
170
			return pg_free_result($query_result);
171
		}
172
		return true;
173
	}
174
	/**
175
	 * @inheritdoc
176
	 */
177
	function columns ($table, $like = false) {
178
		if (!$table) {
179
			return false;
180
		}
181
		if ($like) {
182
			$like    = $this->s($like);
183
			$columns = $this->qfas(
184
				"SELECT `column_name` 
185
				FROM `information_schema`.`columns`
186
				WHERE
187
					`table_name` = '$table' AND
188
					`column_name` LIKE $like"
189
			) ?: [];
190
		} else {
191
			$columns = $this->qfas(
192
				"SELECT `column_name`
193
				FROM `information_schema`.`columns`
194
				WHERE `table_name` = '$table'"
195
			) ?: [];
196
		}
197
		return $columns;
198
	}
199
	/**
200
	 * @inheritdoc
201
	 */
202
	function tables ($like = false) {
203
		if ($like) {
204
			$like = $this->s($like);
205
			return $this->qfas(
206
				"SELECT `table_name`
207
				FROM `information_schema`.`tables`
208
				WHERE
209
					`table_schema` = 'public' AND
210
					`table_name` LIKE $like
211
				ORDER BY `table_name` ASC"
212
			) ?: [];
213
		} else {
214
			return $this->qfas(
215
				"SELECT `table_name`
216
				FROM `information_schema`.`tables`
217
				WHERE `table_schema` = 'public'
218
				ORDER BY `table_name` ASC"
219
			) ?: [];
220
		}
221
	}
222
	/**
223
	 * @inheritdoc
224
	 */
225
	protected function s_internal ($string, $single_quotes_around) {
226
		return $single_quotes_around ? pg_escape_literal($this->handler, $string) : pg_escape_string($this->handler, $string);
227
	}
228
	/**
229
	 * @inheritdoc
230
	 */
231
	function server () {
232
		return $this->handler->server_info;
233
	}
234
	/**
235
	 * @inheritdoc
236
	 */
237
	function __destruct () {
238
		if ($this->connected && is_resource($this->handler)) {
239
			pg_close($this->handler);
240
			$this->connected = false;
241
		}
242
	}
243
}
244