1 |
6daefa8c
|
Petr Lukašík
|
<?php
|
2 |
|
|
|
3 |
|
|
/**
|
4 |
|
|
* A class that implements the DB interface for Postgres
|
5 |
|
|
* Note: This class uses ADODB and returns RecordSets.
|
6 |
|
|
*
|
7 |
|
|
* $Id: Postgres.php,v 1.279 2005/11/04 04:23:16 chriskl Exp $
|
8 |
|
|
*/
|
9 |
|
|
|
10 |
|
|
// @@@ THOUGHT: What about inherits? ie. use of ONLY???
|
11 |
|
|
|
12 |
|
|
include_once('./classes/database/ADODB_base.php');
|
13 |
|
|
|
14 |
|
|
class Postgres extends ADODB_base {
|
15 |
|
|
|
16 |
|
|
// Major version. MUST be numerically comparable to other versions.
|
17 |
|
|
var $major_version = 7.0;
|
18 |
|
|
// Array of allowed type alignments
|
19 |
|
|
var $typAligns = array('char', 'int2', 'int4', 'double');
|
20 |
|
|
// The default type alignment
|
21 |
|
|
var $typAlignDef = 'int4';
|
22 |
|
|
// Array of allowed type storage attributes
|
23 |
|
|
var $typStorages = array('plain', 'external', 'extended', 'main');
|
24 |
|
|
// The default type storage
|
25 |
|
|
var $typStorageDef = 'plain';
|
26 |
|
|
// Extra "magic" types
|
27 |
|
|
var $extraTypes = array('SERIAL');
|
28 |
|
|
// Array of allowed index types
|
29 |
|
|
var $typIndexes = array('BTREE', 'RTREE', 'GIST', 'HASH');
|
30 |
|
|
// Default index type
|
31 |
|
|
var $typIndexDef = 'BTREE';
|
32 |
|
|
// Array of allowed trigger events
|
33 |
|
|
var $triggerEvents= array('INSERT', 'UPDATE', 'DELETE', 'INSERT OR UPDATE', 'INSERT OR DELETE',
|
34 |
|
|
'DELETE OR UPDATE', 'INSERT OR DELETE OR UPDATE');
|
35 |
|
|
// When to execute the trigger
|
36 |
|
|
var $triggerExecTimes = array('BEFORE', 'AFTER');
|
37 |
|
|
// How often to execute the trigger
|
38 |
|
|
var $triggerFrequency = array('ROW');
|
39 |
|
|
// Foreign key stuff. First element MUST be the default.
|
40 |
|
|
var $fkactions = array('NO ACTION', 'RESTRICT', 'CASCADE', 'SET NULL', 'SET DEFAULT');
|
41 |
|
|
var $fkmatches = array('MATCH SIMPLE', 'MATCH FULL');
|
42 |
|
|
var $fkdeferrable = array('NOT DEFERRABLE', 'DEFERRABLE');
|
43 |
|
|
var $fkinitial = array('INITIALLY IMMEDIATE', 'INITIALLY DEFERRED');
|
44 |
|
|
// Function properties
|
45 |
|
|
var $funcprops = array(array('', 'ISCACHABLE'));
|
46 |
|
|
var $defaultprops = array('');
|
47 |
|
|
|
48 |
|
|
// Last oid assigned to a system object
|
49 |
|
|
var $_lastSystemOID = 18539;
|
50 |
|
|
var $_maxNameLen = 31;
|
51 |
|
|
|
52 |
|
|
// Name of id column
|
53 |
|
|
var $id = 'oid';
|
54 |
|
|
|
55 |
|
|
// Map of database encoding names to HTTP encoding names. If a
|
56 |
|
|
// database encoding does not appear in this list, then its HTTP
|
57 |
|
|
// encoding name is the same as its database encoding name.
|
58 |
|
|
var $codemap = array(
|
59 |
|
|
'ALT' => 'CP866',
|
60 |
|
|
'EUC_CN' => 'GB2312',
|
61 |
|
|
'EUC_JP' => 'EUC-JP',
|
62 |
|
|
'EUC_KR' => 'EUC-KR',
|
63 |
|
|
'EUC_TW' => 'EUC-TW',
|
64 |
|
|
'ISO_8859_5' => 'ISO-8859-5',
|
65 |
|
|
'ISO_8859_6' => 'ISO-8859-6',
|
66 |
|
|
'ISO_8859_7' => 'ISO-8859-7',
|
67 |
|
|
'ISO_8859_8' => 'ISO-8859-8',
|
68 |
|
|
'JOHAB' => 'CP1361',
|
69 |
|
|
'KOI8' => 'KOI8-R',
|
70 |
|
|
'LATIN1' => 'ISO-8859-1',
|
71 |
|
|
'LATIN2' => 'ISO-8859-2',
|
72 |
|
|
'LATIN3' => 'ISO-8859-3',
|
73 |
|
|
'LATIN4' => 'ISO-8859-4',
|
74 |
|
|
// The following encoding map is a known error in PostgreSQL < 7.2
|
75 |
|
|
// See the constructor for Postgres72.
|
76 |
|
|
'LATIN5' => 'ISO-8859-5',
|
77 |
|
|
'LATIN6' => 'ISO-8859-10',
|
78 |
|
|
'LATIN7' => 'ISO-8859-13',
|
79 |
|
|
'LATIN8' => 'ISO-8859-14',
|
80 |
|
|
'LATIN9' => 'ISO-8859-15',
|
81 |
|
|
'LATIN10' => 'ISO-8859-16',
|
82 |
|
|
'SQL_ASCII' => 'US-ASCII',
|
83 |
|
|
'TCVN' => 'CP1258',
|
84 |
|
|
'UNICODE' => 'UTF-8',
|
85 |
|
|
'WIN' => 'CP1251',
|
86 |
|
|
'WIN874' => 'CP874',
|
87 |
|
|
'WIN1256' => 'CP1256'
|
88 |
|
|
);
|
89 |
|
|
|
90 |
|
|
// Map of internal language name to syntax highlighting name
|
91 |
|
|
var $langmap = array(
|
92 |
|
|
'sql' => 'SQL',
|
93 |
|
|
'plpgsql' => 'SQL',
|
94 |
|
|
'php' => 'PHP',
|
95 |
|
|
'phpu' => 'PHP',
|
96 |
|
|
'plphp' => 'PHP',
|
97 |
|
|
'plphpu' => 'PHP',
|
98 |
|
|
'perl' => 'Perl',
|
99 |
|
|
'perlu' => 'Perl',
|
100 |
|
|
'plperl' => 'Perl',
|
101 |
|
|
'plperlu' => 'Perl',
|
102 |
|
|
'java' => 'Java',
|
103 |
|
|
'javau' => 'Java',
|
104 |
|
|
'pljava' => 'Java',
|
105 |
|
|
'pljavau' => 'Java',
|
106 |
|
|
'plj' => 'Java',
|
107 |
|
|
'plju' => 'Java',
|
108 |
|
|
'python' => 'Python',
|
109 |
|
|
'pythonu' => 'Python',
|
110 |
|
|
'plpython' => 'Python',
|
111 |
|
|
'plpythonu' => 'Python',
|
112 |
|
|
'ruby' => 'Ruby',
|
113 |
|
|
'rubyu' => 'Ruby',
|
114 |
|
|
'plruby' => 'Ruby',
|
115 |
|
|
'plrubyu' => 'Ruby'
|
116 |
|
|
);
|
117 |
|
|
|
118 |
|
|
// List of all legal privileges that can be applied to different types
|
119 |
|
|
// of objects.
|
120 |
|
|
var $privlist = array(
|
121 |
|
|
'table' => array('SELECT', 'INSERT', 'UPDATE', 'RULE', 'ALL'),
|
122 |
|
|
'view' => array('SELECT', 'INSERT', 'UPDATE', 'RULE', 'ALL'),
|
123 |
|
|
'sequence' => array('SELECT', 'UPDATE', 'ALL')
|
124 |
|
|
);
|
125 |
|
|
|
126 |
|
|
// List of characters in acl lists and the privileges they
|
127 |
|
|
// refer to.
|
128 |
|
|
var $privmap = array(
|
129 |
|
|
'r' => 'SELECT',
|
130 |
|
|
'w' => 'UPDATE',
|
131 |
|
|
'a' => 'INSERT',
|
132 |
|
|
'R' => 'RULE'
|
133 |
|
|
);
|
134 |
|
|
|
135 |
|
|
// Rule action types
|
136 |
|
|
var $rule_events = array('SELECT', 'INSERT', 'UPDATE', 'DELETE');
|
137 |
|
|
|
138 |
|
|
// Select operators
|
139 |
|
|
// Operators of type 'i' are 'infix', eg. a = '1'. Type 'p' means postfix unary, eg. a IS TRUE.
|
140 |
|
|
// 'x' is a bracketed subquery form. eg. IN (1,2,3)
|
141 |
|
|
var $selectOps = array('=' => 'i', '!=' => 'i', '<' => 'i', '>' => 'i', '<=' => 'i', '>=' => 'i', 'LIKE' => 'i', 'NOT LIKE' => 'i',
|
142 |
|
|
'~' => 'i', '!~' => 'i', '~*' => 'i', '!~*' => 'i', 'IS NULL' => 'p', 'IS NOT NULL' => 'p',
|
143 |
|
|
'IN' => 'x', 'NOT IN' => 'x');
|
144 |
|
|
|
145 |
|
|
// Supported join operations for use with view wizard
|
146 |
|
|
var $joinOps = array('INNER JOIN' => 'INNER JOIN');
|
147 |
|
|
|
148 |
|
|
// Default help URL
|
149 |
|
|
var $help_base;
|
150 |
|
|
|
151 |
|
|
// Help sub pages
|
152 |
|
|
var $help_page;
|
153 |
|
|
|
154 |
|
|
/**
|
155 |
|
|
* Constructor
|
156 |
|
|
* @param $conn The database connection
|
157 |
|
|
*/
|
158 |
|
|
function Postgres($conn) {
|
159 |
|
|
$this->ADODB_base($conn);
|
160 |
|
|
}
|
161 |
|
|
|
162 |
|
|
// Help functions
|
163 |
|
|
|
164 |
|
|
/**
|
165 |
|
|
* Fetch a URL (or array of URLs) for a given help page.
|
166 |
|
|
*/
|
167 |
|
|
function getHelp($help) {
|
168 |
|
|
$this->getHelpPages();
|
169 |
|
|
|
170 |
|
|
if (isset($this->help_page[$help])) {
|
171 |
|
|
if (is_array($this->help_page[$help])) {
|
172 |
|
|
$urls = array();
|
173 |
|
|
foreach ($this->help_page[$help] as $link) {
|
174 |
|
|
$urls[] = $this->help_base . $link;
|
175 |
|
|
}
|
176 |
|
|
return $urls;
|
177 |
|
|
} else
|
178 |
|
|
return $this->help_base . $this->help_page[$help];
|
179 |
|
|
} else
|
180 |
|
|
return null;
|
181 |
|
|
}
|
182 |
|
|
|
183 |
|
|
/**
|
184 |
|
|
* Initialize help pages and return the full list
|
185 |
|
|
*/
|
186 |
|
|
function getHelpPages() {
|
187 |
|
|
include_once('./help/PostgresDoc70.php');
|
188 |
|
|
return $this->help_page;
|
189 |
|
|
}
|
190 |
|
|
|
191 |
|
|
// Formatting functions
|
192 |
|
|
|
193 |
|
|
/**
|
194 |
|
|
* Cleans (escapes) a string
|
195 |
|
|
* @param $str The string to clean, by reference
|
196 |
|
|
* @return The cleaned string
|
197 |
|
|
*/
|
198 |
|
|
function clean(&$str) {
|
199 |
|
|
if ($str === null) return null;
|
200 |
|
|
$str = str_replace("\r\n","\n",$str);
|
201 |
|
|
if (function_exists('pg_escape_string'))
|
202 |
|
|
$str = pg_escape_string($str);
|
203 |
|
|
else
|
204 |
|
|
$str = addslashes($str);
|
205 |
|
|
return $str;
|
206 |
|
|
}
|
207 |
|
|
|
208 |
|
|
/**
|
209 |
|
|
* Cleans (escapes) an object name (eg. table, field)
|
210 |
|
|
* @param $str The string to clean, by reference
|
211 |
|
|
* @return The cleaned string
|
212 |
|
|
*/
|
213 |
|
|
function fieldClean(&$str) {
|
214 |
|
|
if ($str === null) return null;
|
215 |
|
|
$str = str_replace('"', '""', $str);
|
216 |
|
|
return $str;
|
217 |
|
|
}
|
218 |
|
|
|
219 |
|
|
/**
|
220 |
|
|
* Cleans (escapes) an array
|
221 |
|
|
* @param $arr The array to clean, by reference
|
222 |
|
|
* @return The cleaned array
|
223 |
|
|
*/
|
224 |
|
|
function arrayClean(&$arr) {
|
225 |
|
|
foreach ($arr as $k => $v) {
|
226 |
|
|
if ($v === null) continue;
|
227 |
|
|
if (function_exists('pg_escape_string'))
|
228 |
|
|
$arr[$k] = pg_escape_string($v);
|
229 |
|
|
else
|
230 |
|
|
$arr[$k] = addslashes($v);
|
231 |
|
|
}
|
232 |
|
|
return $arr;
|
233 |
|
|
}
|
234 |
|
|
|
235 |
|
|
/**
|
236 |
|
|
* Cleans (escapes) an array of field names
|
237 |
|
|
* @param $arr The array to clean, by reference
|
238 |
|
|
* @return The cleaned array
|
239 |
|
|
*/
|
240 |
|
|
function fieldArrayClean(&$arr) {
|
241 |
|
|
foreach ($arr as $k => $v) {
|
242 |
|
|
if ($v === null) continue;
|
243 |
|
|
$arr[$k] = str_replace('"', '""', $v);
|
244 |
|
|
}
|
245 |
|
|
return $arr;
|
246 |
|
|
}
|
247 |
|
|
|
248 |
|
|
/**
|
249 |
|
|
* Escapes bytea data for display on the screen
|
250 |
|
|
* @param $data The bytea data
|
251 |
|
|
* @return Data formatted for on-screen display
|
252 |
|
|
*/
|
253 |
|
|
function escapeBytea($data) {
|
254 |
|
|
if (function_exists('pg_escape_bytea'))
|
255 |
|
|
return stripslashes(pg_escape_bytea($data));
|
256 |
|
|
else {
|
257 |
|
|
$translations = array('\\a' => '\\007', '\\b' => '\\010', '\\t' => '\\011', '\\n' => '\\012', '\\v' => '\\013', '\\f' => '\\014', '\\r' => '\\015');
|
258 |
|
|
return strtr(addCSlashes($data, "\0..\37\177..\377"), $translations);
|
259 |
|
|
}
|
260 |
|
|
}
|
261 |
|
|
|
262 |
|
|
/**
|
263 |
|
|
* Outputs the HTML code for a particular field
|
264 |
|
|
* @param $name The name to give the field
|
265 |
|
|
* @param $value The value of the field. Note this could be 'numeric(7,2)' sort of thing...
|
266 |
|
|
* @param $type The database type of the field
|
267 |
|
|
* @param $actions An array of javascript action name to the code to execute on that action
|
268 |
|
|
*/
|
269 |
|
|
function printField($name, $value, $type, $actions = array()) {
|
270 |
|
|
global $lang;
|
271 |
|
|
|
272 |
|
|
// Determine actions string
|
273 |
|
|
$action_str = '';
|
274 |
|
|
foreach ($actions as $k => $v) {
|
275 |
|
|
$action_str .= " {$k}=\"" . htmlspecialchars($v) . "\"";
|
276 |
|
|
}
|
277 |
|
|
|
278 |
|
|
switch (substr($type,0,9)) {
|
279 |
|
|
case 'bool':
|
280 |
|
|
case 'boolean':
|
281 |
|
|
if ($value !== null && $value == '') $value = null;
|
282 |
|
|
elseif ($value == 'true') $value = 't';
|
283 |
|
|
elseif ($value == 'false') $value = 'f';
|
284 |
|
|
|
285 |
|
|
// If value is null, 't' or 'f'...
|
286 |
|
|
if ($value === null || $value == 't' || $value == 'f') {
|
287 |
|
|
echo "<select name=\"", htmlspecialchars($name), "\"{$action_str}>\n";
|
288 |
|
|
echo "<option value=\"\"", ($value === null) ? ' selected="selected"' : '', "></option>\n";
|
289 |
|
|
echo "<option value=\"t\"", ($value == 't') ? ' selected="selected"' : '', ">{$lang['strtrue']}</option>\n";
|
290 |
|
|
echo "<option value=\"f\"", ($value == 'f') ? ' selected="selected"' : '', ">{$lang['strfalse']}</option>\n";
|
291 |
|
|
echo "</select>\n";
|
292 |
|
|
}
|
293 |
|
|
else {
|
294 |
|
|
echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} />\n";
|
295 |
|
|
}
|
296 |
|
|
break;
|
297 |
|
|
case 'bytea':
|
298 |
|
|
$value = $this->escapeBytea($value);
|
299 |
|
|
case 'text':
|
300 |
|
|
$n = substr_count($value, "\n");
|
301 |
|
|
$n = $n < 5 ? 5 : $n;
|
302 |
|
|
$n = $n > 20 ? 20 : $n;
|
303 |
|
|
echo "<textarea name=\"", htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"75\" wrap=\"virtual\"{$action_str}>\n";
|
304 |
|
|
echo htmlspecialchars($value);
|
305 |
|
|
echo "</textarea>\n";
|
306 |
|
|
break;
|
307 |
|
|
case 'character':
|
308 |
|
|
$n = substr_count($value, "\n");
|
309 |
|
|
$n = $n < 5 ? 5 : $n;
|
310 |
|
|
$n = $n > 20 ? 20 : $n;
|
311 |
|
|
echo "<textarea name=\"", htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"35\" wrap=\"virtual\"{$action_str}>\n";
|
312 |
|
|
echo htmlspecialchars($value);
|
313 |
|
|
echo "</textarea>\n";
|
314 |
|
|
break;
|
315 |
|
|
default:
|
316 |
|
|
echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} />\n";
|
317 |
|
|
break;
|
318 |
|
|
}
|
319 |
|
|
}
|
320 |
|
|
|
321 |
|
|
/**
|
322 |
|
|
* Formats a value or expression for sql purposes
|
323 |
|
|
* @param $type The type of the field
|
324 |
|
|
* @param $format VALUE or EXPRESSION
|
325 |
|
|
* @param $value The actual value entered in the field. Can be NULL
|
326 |
|
|
* @return The suitably quoted and escaped value.
|
327 |
|
|
*/
|
328 |
|
|
function formatValue($type, $format, $value) {
|
329 |
|
|
switch ($type) {
|
330 |
|
|
case 'bool':
|
331 |
|
|
case 'boolean':
|
332 |
|
|
if ($value == 't')
|
333 |
|
|
return 'TRUE';
|
334 |
|
|
elseif ($value == 'f')
|
335 |
|
|
return 'FALSE';
|
336 |
|
|
elseif ($value == '')
|
337 |
|
|
return 'NULL';
|
338 |
|
|
else
|
339 |
|
|
return $value;
|
340 |
|
|
break;
|
341 |
|
|
default:
|
342 |
|
|
// Checking variable fields is difficult as there might be a size
|
343 |
|
|
// attribute...
|
344 |
|
|
if (strpos($type, 'time') === 0) {
|
345 |
|
|
// Assume it's one of the time types...
|
346 |
|
|
if ($value == '') return "''";
|
347 |
|
|
elseif (strcasecmp($value, 'CURRENT_TIMESTAMP') == 0
|
348 |
|
|
|| strcasecmp($value, 'CURRENT_TIME') == 0
|
349 |
|
|
|| strcasecmp($value, 'CURRENT_DATE') == 0
|
350 |
|
|
|| strcasecmp($value, 'LOCALTIME') == 0
|
351 |
|
|
|| strcasecmp($value, 'LOCALTIMESTAMP') == 0) {
|
352 |
|
|
return $value;
|
353 |
|
|
}
|
354 |
|
|
elseif ($format == 'EXPRESSION')
|
355 |
|
|
return $value;
|
356 |
|
|
else {
|
357 |
|
|
$this->clean($value);
|
358 |
|
|
return "'{$value}'";
|
359 |
|
|
}
|
360 |
|
|
}
|
361 |
|
|
else {
|
362 |
|
|
if ($format == 'VALUE') {
|
363 |
|
|
$this->clean($value);
|
364 |
|
|
return "'{$value}'";
|
365 |
|
|
}
|
366 |
|
|
return $value;
|
367 |
|
|
}
|
368 |
|
|
}
|
369 |
|
|
}
|
370 |
|
|
|
371 |
|
|
/**
|
372 |
|
|
* Formats a type correctly for display. Postgres 7.0 had no 'format_type'
|
373 |
|
|
* built-in function, and hence we need to do it manually.
|
374 |
|
|
* @param $typname The name of the type
|
375 |
|
|
* @param $typmod The contents of the typmod field
|
376 |
|
|
*/
|
377 |
|
|
function formatType($typname, $typmod) {
|
378 |
|
|
// This is a specific constant in the 7.0 source
|
379 |
|
|
$varhdrsz = 4;
|
380 |
|
|
|
381 |
|
|
// If the first character is an underscore, it's an array type
|
382 |
|
|
$is_array = false;
|
383 |
|
|
if (substr($typname, 0, 1) == '_') {
|
384 |
|
|
$is_array = true;
|
385 |
|
|
$typname = substr($typname, 1);
|
386 |
|
|
}
|
387 |
|
|
|
388 |
|
|
// Show lengths on bpchar and varchar
|
389 |
|
|
if ($typname == 'bpchar') {
|
390 |
|
|
$len = $typmod - $varhdrsz;
|
391 |
|
|
$temp = 'character';
|
392 |
|
|
if ($len > 1)
|
393 |
|
|
$temp .= "({$len})";
|
394 |
|
|
}
|
395 |
|
|
elseif ($typname == 'varchar') {
|
396 |
|
|
$temp = 'character varying';
|
397 |
|
|
if ($typmod != -1)
|
398 |
|
|
$temp .= "(" . ($typmod - $varhdrsz) . ")";
|
399 |
|
|
}
|
400 |
|
|
elseif ($typname == 'numeric') {
|
401 |
|
|
$temp = 'numeric';
|
402 |
|
|
if ($typmod != -1) {
|
403 |
|
|
$tmp_typmod = $typmod - $varhdrsz;
|
404 |
|
|
$precision = ($tmp_typmod >> 16) & 0xffff;
|
405 |
|
|
$scale = $tmp_typmod & 0xffff;
|
406 |
|
|
$temp .= "({$precision}, {$scale})";
|
407 |
|
|
}
|
408 |
|
|
}
|
409 |
|
|
else $temp = $typname;
|
410 |
|
|
|
411 |
|
|
// Add array qualifier if it's an array
|
412 |
|
|
if ($is_array) $temp .= '[]';
|
413 |
|
|
|
414 |
|
|
return $temp;
|
415 |
|
|
}
|
416 |
|
|
|
417 |
|
|
// Database functions
|
418 |
|
|
|
419 |
|
|
/**
|
420 |
|
|
* Return all database available on the server
|
421 |
|
|
* @return A list of databases, sorted alphabetically
|
422 |
|
|
*/
|
423 |
|
|
function getDatabases($currentdatabase = NULL) {
|
424 |
|
|
global $conf, $misc;
|
425 |
|
|
|
426 |
|
|
$server_info = $misc->getServerInfo();
|
427 |
|
|
|
428 |
|
|
if (isset($conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser($server_info['username'])) {
|
429 |
|
|
$username = $server_info['username'];
|
430 |
|
|
$this->clean($username);
|
431 |
|
|
$clause = " AND pu.usename='{$username}'";
|
432 |
|
|
}
|
433 |
|
|
else $clause = '';
|
434 |
|
|
|
435 |
|
|
if ($currentdatabase != NULL)
|
436 |
|
|
$orderby = "ORDER BY pdb.datname = '{$currentdatabase}' DESC, pdb.datname";
|
437 |
|
|
else
|
438 |
|
|
$orderby = "ORDER BY pdb.datname";
|
439 |
|
|
|
440 |
|
|
if (!$conf['show_system'])
|
441 |
|
|
$where = "AND pdb.datname NOT IN ('template1')";
|
442 |
|
|
else
|
443 |
|
|
$where = '';
|
444 |
|
|
|
445 |
|
|
$sql = "SELECT pdb.datname, pu.usename AS datowner, pg_encoding_to_char(encoding) AS datencoding,
|
446 |
|
|
(SELECT description FROM pg_description pd WHERE pdb.oid=pd.objoid) AS datcomment
|
447 |
|
|
FROM pg_database pdb, pg_user pu
|
448 |
|
|
WHERE pdb.datdba = pu.usesysid
|
449 |
|
|
{$where}
|
450 |
|
|
{$clause}
|
451 |
|
|
{$orderby}";
|
452 |
|
|
|
453 |
|
|
return $this->selectSet($sql);
|
454 |
|
|
}
|
455 |
|
|
|
456 |
|
|
/**
|
457 |
|
|
* Return the database owner of a db
|
458 |
|
|
* @param string $database the name of the database to get the owner for
|
459 |
|
|
* @return recordset of the db owner info
|
460 |
|
|
*/
|
461 |
|
|
function getDatabaseOwner($database) {
|
462 |
|
|
$this->clean($database);
|
463 |
|
|
$sql = "SELECT usename FROM pg_user, pg_database WHERE pg_user.usesysid = pg_database.datdba AND pg_database.datname = '{$database}' ";
|
464 |
|
|
return $this->selectSet($sql);
|
465 |
|
|
}
|
466 |
|
|
|
467 |
|
|
/**
|
468 |
|
|
* Return all information about a particular database
|
469 |
|
|
* @param $database The name of the database to retrieve
|
470 |
|
|
* @return The database info
|
471 |
|
|
*/
|
472 |
|
|
function getDatabase($database) {
|
473 |
|
|
$this->clean($database);
|
474 |
|
|
$sql = "SELECT * FROM pg_database WHERE datname='{$database}'";
|
475 |
|
|
return $this->selectSet($sql);
|
476 |
|
|
}
|
477 |
|
|
|
478 |
|
|
/**
|
479 |
|
|
* Returns the current database encoding
|
480 |
|
|
* @return The encoding. eg. SQL_ASCII, UTF-8, etc.
|
481 |
|
|
*/
|
482 |
|
|
function getDatabaseEncoding() {
|
483 |
|
|
// Try to avoid a query if at all possible (5)
|
484 |
|
|
if (function_exists('pg_parameter_status')) {
|
485 |
|
|
$encoding = pg_parameter_status($this->conn->_connectionID, 'server_encoding');
|
486 |
|
|
if ($encoding !== false) return $encoding;
|
487 |
|
|
}
|
488 |
|
|
|
489 |
|
|
$sql = "SELECT getdatabaseencoding() AS encoding";
|
490 |
|
|
|
491 |
|
|
return $this->selectField($sql, 'encoding');
|
492 |
|
|
}
|
493 |
|
|
|
494 |
|
|
/**
|
495 |
|
|
* Sets the client encoding
|
496 |
|
|
* @param $encoding The encoding to for the client
|
497 |
|
|
* @return 0 success
|
498 |
|
|
*/
|
499 |
|
|
function setClientEncoding($encoding) {
|
500 |
|
|
return -99;
|
501 |
|
|
}
|
502 |
|
|
|
503 |
|
|
/**
|
504 |
|
|
* Creates a database
|
505 |
|
|
* @param $database The name of the database to create
|
506 |
|
|
* @param $encoding Encoding of the database
|
507 |
|
|
* @param $tablespace (optional) The tablespace name
|
508 |
|
|
* @return 0 success
|
509 |
|
|
*/
|
510 |
|
|
function createDatabase($database, $encoding, $tablespace = '') {
|
511 |
|
|
$this->fieldClean($database);
|
512 |
|
|
$this->clean($encoding);
|
513 |
|
|
$this->fieldClean($tablespace);
|
514 |
|
|
|
515 |
|
|
if ($encoding == '') {
|
516 |
|
|
$sql = "CREATE DATABASE \"{$database}\"";
|
517 |
|
|
} else {
|
518 |
|
|
$sql = "CREATE DATABASE \"{$database}\" WITH ENCODING='{$encoding}'";
|
519 |
|
|
}
|
520 |
|
|
|
521 |
|
|
if ($tablespace != '' && $this->hasTablespaces()) $sql .= " TABLESPACE \"{$tablespace}\"";
|
522 |
|
|
|
523 |
|
|
return $this->execute($sql);
|
524 |
|
|
}
|
525 |
|
|
|
526 |
|
|
/**
|
527 |
|
|
* Drops a database
|
528 |
|
|
* @param $database The name of the database to drop
|
529 |
|
|
* @return 0 success
|
530 |
|
|
*/
|
531 |
|
|
function dropDatabase($database) {
|
532 |
|
|
$this->fieldClean($database);
|
533 |
|
|
$sql = "DROP DATABASE \"{$database}\"";
|
534 |
|
|
return $this->execute($sql);
|
535 |
|
|
}
|
536 |
|
|
|
537 |
|
|
// Schema functions
|
538 |
|
|
|
539 |
|
|
/**
|
540 |
|
|
* Sets the current working schema. This is a do nothing method for
|
541 |
|
|
* < 7.3 and is just here for polymorphism's sake.
|
542 |
|
|
* @param $schema The the name of the schema to work in
|
543 |
|
|
* @return 0 success
|
544 |
|
|
*/
|
545 |
|
|
function setSchema($schema) {
|
546 |
|
|
return 0;
|
547 |
|
|
}
|
548 |
|
|
|
549 |
|
|
// Inheritance functions
|
550 |
|
|
|
551 |
|
|
/**
|
552 |
|
|
* Finds the names and schemas of parent tables (in order)
|
553 |
|
|
* @param $table The table to find the parents for
|
554 |
|
|
* @return A recordset
|
555 |
|
|
*/
|
556 |
|
|
function getTableParents($table) {
|
557 |
|
|
$this->clean($table);
|
558 |
|
|
|
559 |
|
|
$sql = "
|
560 |
|
|
SELECT
|
561 |
|
|
NULL AS nspname, relname
|
562 |
|
|
FROM
|
563 |
|
|
pg_class pc, pg_inherits pi
|
564 |
|
|
WHERE
|
565 |
|
|
pc.oid=pi.inhparent
|
566 |
|
|
AND pi.inhrelid = (SELECT oid from pg_class WHERE relname='{$table}')
|
567 |
|
|
ORDER BY
|
568 |
|
|
pi.inhseqno
|
569 |
|
|
";
|
570 |
|
|
|
571 |
|
|
return $this->selectSet($sql);
|
572 |
|
|
}
|
573 |
|
|
|
574 |
|
|
|
575 |
|
|
/**
|
576 |
|
|
* Finds the names and schemas of child tables
|
577 |
|
|
* @param $table The table to find the children for
|
578 |
|
|
* @return A recordset
|
579 |
|
|
*/
|
580 |
|
|
function getTableChildren($table) {
|
581 |
|
|
$this->clean($table);
|
582 |
|
|
|
583 |
|
|
$sql = "
|
584 |
|
|
SELECT
|
585 |
|
|
NULL AS nspname, relname
|
586 |
|
|
FROM
|
587 |
|
|
pg_class pc, pg_inherits pi
|
588 |
|
|
WHERE
|
589 |
|
|
pc.oid=pi.inhrelid
|
590 |
|
|
AND pi.inhparent = (SELECT oid from pg_class WHERE relname='{$table}')
|
591 |
|
|
";
|
592 |
|
|
|
593 |
|
|
return $this->selectSet($sql);
|
594 |
|
|
}
|
595 |
|
|
|
596 |
|
|
// Table functions
|
597 |
|
|
|
598 |
|
|
/**
|
599 |
|
|
* Returns table information
|
600 |
|
|
* @param $table The name of the table
|
601 |
|
|
* @return A recordset
|
602 |
|
|
*/
|
603 |
|
|
function getTable($table) {
|
604 |
|
|
$this->clean($table);
|
605 |
|
|
|
606 |
|
|
$sql = "SELECT pc.relname,
|
607 |
|
|
pg_get_userbyid(pc.relowner) AS relowner,
|
608 |
|
|
(SELECT description FROM pg_description pd
|
609 |
|
|
WHERE pc.oid=pd.objoid) AS relcomment
|
610 |
|
|
FROM pg_class pc
|
611 |
|
|
WHERE pc.relname='{$table}'";
|
612 |
|
|
|
613 |
|
|
return $this->selectSet($sql);
|
614 |
|
|
}
|
615 |
|
|
|
616 |
|
|
/**
|
617 |
|
|
* Return all tables in current database
|
618 |
|
|
* @param $all True to fetch all tables, false for just in current schema
|
619 |
|
|
* @return All tables, sorted alphabetically
|
620 |
|
|
*/
|
621 |
|
|
function getTables($all = false) {
|
622 |
|
|
global $conf;
|
623 |
|
|
if (!$conf['show_system'] || $all) $where = "AND c.relname NOT LIKE 'pg\\\\_%' ";
|
624 |
|
|
else $where = '';
|
625 |
|
|
|
626 |
|
|
$sql = "SELECT NULL AS nspname, c.relname,
|
627 |
|
|
(SELECT usename FROM pg_user u WHERE u.usesysid=c.relowner) AS relowner,
|
628 |
|
|
(SELECT description FROM pg_description pd WHERE c.oid=pd.objoid) AS relcomment,
|
629 |
|
|
reltuples::integer AS reltuples
|
630 |
|
|
FROM pg_class c
|
631 |
|
|
WHERE c.relkind='r'
|
632 |
|
|
AND NOT EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = c.oid AND r.ev_type = '1')
|
633 |
|
|
{$where}
|
634 |
|
|
ORDER BY relname";
|
635 |
|
|
return $this->selectSet($sql);
|
636 |
|
|
}
|
637 |
|
|
|
638 |
|
|
/**
|
639 |
|
|
* Retrieve the attribute definition of a table
|
640 |
|
|
* @param $table The name of the table
|
641 |
|
|
* @param $field (optional) The name of a field to return
|
642 |
|
|
* @return All attributes in order
|
643 |
|
|
*/
|
644 |
|
|
function getTableAttributes($table, $field = '') {
|
645 |
|
|
$this->clean($table);
|
646 |
|
|
$this->clean($field);
|
647 |
|
|
|
648 |
|
|
if ($field == '') {
|
649 |
|
|
$sql = "SELECT
|
650 |
|
|
a.attname, t.typname as type, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, -1 AS attstattarget, a.attstorage,
|
651 |
|
|
(SELECT adsrc FROM pg_attrdef adef WHERE a.attrelid=adef.adrelid AND a.attnum=adef.adnum) AS adsrc,
|
652 |
|
|
a.attstorage AS typstorage, false AS attisserial,
|
653 |
|
|
(SELECT description FROM pg_description d WHERE d.objoid = a.oid) as comment
|
654 |
|
|
FROM
|
655 |
|
|
pg_attribute a,
|
656 |
|
|
pg_class c,
|
657 |
|
|
pg_type t
|
658 |
|
|
WHERE
|
659 |
|
|
c.relname = '{$table}' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid
|
660 |
|
|
ORDER BY a.attnum";
|
661 |
|
|
}
|
662 |
|
|
else {
|
663 |
|
|
$sql = "SELECT
|
664 |
|
|
a.attname, t.typname as type, t.typname as base_type,
|
665 |
|
|
a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, -1 AS attstattarget, a.attstorage,
|
666 |
|
|
(SELECT adsrc FROM pg_attrdef adef WHERE a.attrelid=adef.adrelid AND a.attnum=adef.adnum) AS adsrc,
|
667 |
|
|
a.attstorage AS typstorage,
|
668 |
|
|
(SELECT description FROM pg_description d WHERE d.objoid = a.oid) as comment
|
669 |
|
|
FROM
|
670 |
|
|
pg_attribute a ,
|
671 |
|
|
pg_class c,
|
672 |
|
|
pg_type t
|
673 |
|
|
WHERE
|
674 |
|
|
c.relname = '{$table}' AND a.attname='{$field}' AND a.attrelid = c.oid AND a.atttypid = t.oid";
|
675 |
|
|
}
|
676 |
|
|
|
677 |
|
|
return $this->selectSet($sql);
|
678 |
|
|
}
|
679 |
|
|
|
680 |
|
|
/**
|
681 |
|
|
* Checks to see whether or not a table has a unique id column
|
682 |
|
|
* @param $table The table name
|
683 |
|
|
* @return True if it has a unique id, false otherwise
|
684 |
|
|
* @return -99 error
|
685 |
|
|
*/
|
686 |
|
|
function hasObjectID($table) {
|
687 |
|
|
// 7.0 and 7.1 always had an oid column
|
688 |
|
|
return true;
|
689 |
|
|
}
|
690 |
|
|
|
691 |
|
|
/**
|
692 |
|
|
* Creates a new table in the database
|
693 |
|
|
* @param $name The name of the table
|
694 |
|
|
* @param $fields The number of fields
|
695 |
|
|
* @param $field An array of field names
|
696 |
|
|
* @param $type An array of field types
|
697 |
|
|
* @param $array An array of '' or '[]' for each type if it's an array or not
|
698 |
|
|
* @param $length An array of field lengths
|
699 |
|
|
* @param $notnull An array of not null
|
700 |
|
|
* @param $default An array of default values
|
701 |
|
|
* @param $withoutoids True if WITHOUT OIDS, false otherwise
|
702 |
|
|
* @param $colcomment An array of comments
|
703 |
|
|
* @param $comment Table comment
|
704 |
|
|
* @param $tablespace The tablespace name ('' means none/default)
|
705 |
|
|
* @param $uniquekey An Array indicating the fields that are unique (those indexes that are set)
|
706 |
|
|
* @param $primarykey An Array indicating the field used for the primarykey (those indexes that are set)
|
707 |
|
|
* @return 0 success
|
708 |
|
|
* @return -1 no fields supplied
|
709 |
|
|
*/
|
710 |
|
|
function createTable($name, $fields, $field, $type, $array, $length, $notnull,
|
711 |
|
|
$default, $withoutoids, $colcomment, $tblcomment, $tablespace,
|
712 |
|
|
$uniquekey, $primarykey) {
|
713 |
|
|
$this->fieldClean($name);
|
714 |
|
|
$this->clean($tblcomment);
|
715 |
|
|
|
716 |
|
|
$status = $this->beginTransaction();
|
717 |
|
|
if ($status != 0) return -1;
|
718 |
|
|
|
719 |
|
|
$found = false;
|
720 |
|
|
$first = true;
|
721 |
|
|
$comment_sql = ''; //Accumulate comments for the columns
|
722 |
|
|
$sql = "CREATE TABLE \"{$name}\" (";
|
723 |
|
|
for ($i = 0; $i < $fields; $i++) {
|
724 |
|
|
$this->fieldClean($field[$i]);
|
725 |
|
|
$this->clean($type[$i]);
|
726 |
|
|
$this->clean($length[$i]);
|
727 |
|
|
$this->clean($colcomment[$i]);
|
728 |
|
|
|
729 |
|
|
// Skip blank columns - for user convenience
|
730 |
|
|
if ($field[$i] == '' || $type[$i] == '') continue;
|
731 |
|
|
// If not the first column, add a comma
|
732 |
|
|
if (!$first) $sql .= ", ";
|
733 |
|
|
else $first = false;
|
734 |
|
|
|
735 |
|
|
switch ($type[$i]) {
|
736 |
|
|
// Have to account for weird placing of length for with/without
|
737 |
|
|
// time zone types
|
738 |
|
|
case 'timestamp with time zone':
|
739 |
|
|
case 'timestamp without time zone':
|
740 |
|
|
$qual = substr($type[$i], 9);
|
741 |
|
|
$sql .= "\"{$field[$i]}\" timestamp";
|
742 |
|
|
if ($length[$i] != '') $sql .= "({$length[$i]})";
|
743 |
|
|
$sql .= $qual;
|
744 |
|
|
break;
|
745 |
|
|
case 'time with time zone':
|
746 |
|
|
case 'time without time zone':
|
747 |
|
|
$qual = substr($type[$i], 4);
|
748 |
|
|
$sql .= "\"{$field[$i]}\" time";
|
749 |
|
|
if ($length[$i] != '') $sql .= "({$length[$i]})";
|
750 |
|
|
$sql .= $qual;
|
751 |
|
|
break;
|
752 |
|
|
default:
|
753 |
|
|
$sql .= "\"{$field[$i]}\" {$type[$i]}";
|
754 |
|
|
if ($length[$i] != '') $sql .= "({$length[$i]})";
|
755 |
|
|
}
|
756 |
|
|
// Add array qualifier if necessary
|
757 |
|
|
if ($array[$i] == '[]') $sql .= '[]';
|
758 |
|
|
// Add other qualifiers
|
759 |
|
|
if (!isset($primarykey[$i])) {
|
760 |
|
|
if (isset($uniquekey[$i])) $sql .= " UNIQUE";
|
761 |
|
|
if (isset($notnull[$i])) $sql .= " NOT NULL";
|
762 |
|
|
}
|
763 |
|
|
if ($default[$i] != '') $sql .= " DEFAULT {$default[$i]}";
|
764 |
|
|
|
765 |
|
|
if ($colcomment[$i] != '') $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
|
766 |
|
|
|
767 |
|
|
$found = true;
|
768 |
|
|
}
|
769 |
|
|
|
770 |
|
|
if (!$found) return -1;
|
771 |
|
|
|
772 |
|
|
// PRIMARY KEY
|
773 |
|
|
$primarykeycolumns = array();
|
774 |
|
|
for ($i = 0; $i < $fields; $i++) {
|
775 |
|
|
if (isset($primarykey[$i])) {
|
776 |
|
|
$primarykeycolumns[] = "\"{$field[$i]}\"";
|
777 |
|
|
}
|
778 |
|
|
}
|
779 |
|
|
if (count($primarykeycolumns) > 0) {
|
780 |
|
|
$sql .= ", PRIMARY KEY (" . implode(", ", $primarykeycolumns) . ")";
|
781 |
|
|
}
|
782 |
|
|
|
783 |
|
|
$sql .= ")";
|
784 |
|
|
|
785 |
|
|
// WITHOUT OIDS
|
786 |
|
|
if ($this->hasWithoutOIDs() && $withoutoids)
|
787 |
|
|
$sql .= ' WITHOUT OIDS';
|
788 |
|
|
|
789 |
|
|
// Tablespace
|
790 |
|
|
if ($this->hasTablespaces() && $tablespace != '') {
|
791 |
|
|
$this->fieldClean($tablespace);
|
792 |
|
|
$sql .= " TABLESPACE \"{$tablespace}\"";
|
793 |
|
|
}
|
794 |
|
|
|
795 |
|
|
$status = $this->execute($sql);
|
796 |
|
|
if ($status) {
|
797 |
|
|
$this->rollbackTransaction();
|
798 |
|
|
return -1;
|
799 |
|
|
}
|
800 |
|
|
|
801 |
|
|
if ($tblcomment != '') {
|
802 |
|
|
$status = $this->setComment('TABLE', '', $name, $tblcomment, true);
|
803 |
|
|
if ($status) {
|
804 |
|
|
$this->rollbackTransaction();
|
805 |
|
|
return -1;
|
806 |
|
|
}
|
807 |
|
|
}
|
808 |
|
|
|
809 |
|
|
if ($comment_sql != '') {
|
810 |
|
|
$status = $this->execute($comment_sql);
|
811 |
|
|
if ($status) {
|
812 |
|
|
$this->rollbackTransaction();
|
813 |
|
|
return -1;
|
814 |
|
|
}
|
815 |
|
|
}
|
816 |
|
|
return $this->endTransaction();
|
817 |
|
|
|
818 |
|
|
}
|
819 |
|
|
|
820 |
|
|
/**
|
821 |
|
|
* Alters a table
|
822 |
|
|
* @param $table The name of the table
|
823 |
|
|
* @param $name The new name for the table
|
824 |
|
|
* @param $owner The new owner for the table
|
825 |
|
|
* @param $comment The comment on the table
|
826 |
|
|
* @param $tablespace The new tablespace for the table ('' means leave as is)
|
827 |
|
|
* @return 0 success
|
828 |
|
|
* @return -1 transaction error
|
829 |
|
|
* @return -2 owner error
|
830 |
|
|
* @return -3 rename error
|
831 |
|
|
* @return -4 comment error
|
832 |
|
|
* @return -5 get existing table error
|
833 |
|
|
* @return -6 tablespace error
|
834 |
|
|
*/
|
835 |
|
|
function alterTable($table, $name, $owner, $comment, $tablespace) {
|
836 |
|
|
$this->fieldClean($table);
|
837 |
|
|
$this->fieldClean($name);
|
838 |
|
|
$this->fieldClean($owner);
|
839 |
|
|
$this->clean($comment);
|
840 |
|
|
$this->fieldClean($tablespace);
|
841 |
|
|
|
842 |
|
|
$status = $this->beginTransaction();
|
843 |
|
|
if ($status != 0) {
|
844 |
|
|
$this->rollbackTransaction();
|
845 |
|
|
return -1;
|
846 |
|
|
}
|
847 |
|
|
|
848 |
|
|
// Comment
|
849 |
|
|
$status = $this->setComment('TABLE', '', $table, $comment);
|
850 |
|
|
if ($status != 0) {
|
851 |
|
|
$this->rollbackTransaction();
|
852 |
|
|
return -4;
|
853 |
|
|
}
|
854 |
|
|
|
855 |
|
|
// Owner
|
856 |
|
|
if ($this->hasAlterTableOwner() && $owner != '') {
|
857 |
|
|
// Fetch existing owner
|
858 |
|
|
$data = $this->getTable($table);
|
859 |
|
|
if ($data->recordCount() != 1) {
|
860 |
|
|
$this->rollbackTransaction();
|
861 |
|
|
return -5;
|
862 |
|
|
}
|
863 |
|
|
|
864 |
|
|
// If owner has been changed, then do the alteration. We are
|
865 |
|
|
// careful to avoid this generally as changing owner is a
|
866 |
|
|
// superuser only function.
|
867 |
|
|
if ($data->f['relowner'] != $owner) {
|
868 |
|
|
$sql = "ALTER TABLE \"{$table}\" OWNER TO \"{$owner}\"";
|
869 |
|
|
|
870 |
|
|
$status = $this->execute($sql);
|
871 |
|
|
if ($status != 0) {
|
872 |
|
|
$this->rollbackTransaction();
|
873 |
|
|
return -2;
|
874 |
|
|
}
|
875 |
|
|
}
|
876 |
|
|
}
|
877 |
|
|
|
878 |
|
|
// Tablespace
|
879 |
|
|
if ($this->hasTablespaces() && $tablespace != '') {
|
880 |
|
|
// Fetch existing tablespace
|
881 |
|
|
$data = $this->getTable($table);
|
882 |
|
|
if ($data->recordCount() != 1) {
|
883 |
|
|
$this->rollbackTransaction();
|
884 |
|
|
return -5;
|
885 |
|
|
}
|
886 |
|
|
|
887 |
|
|
// If tablespace has been changed, then do the alteration. We
|
888 |
|
|
// don't want to do this unnecessarily.
|
889 |
|
|
if ($data->f['tablespace'] != $tablespace) {
|
890 |
|
|
$sql = "ALTER TABLE \"{$table}\" SET TABLESPACE \"{$tablespace}\"";
|
891 |
|
|
|
892 |
|
|
$status = $this->execute($sql);
|
893 |
|
|
if ($status != 0) {
|
894 |
|
|
$this->rollbackTransaction();
|
895 |
|
|
return -6;
|
896 |
|
|
}
|
897 |
|
|
}
|
898 |
|
|
}
|
899 |
|
|
|
900 |
|
|
// Rename (only if name has changed)
|
901 |
|
|
if ($name != $table) {
|
902 |
|
|
$sql = "ALTER TABLE \"{$table}\" RENAME TO \"{$name}\"";
|
903 |
|
|
$status = $this->execute($sql);
|
904 |
|
|
if ($status != 0) {
|
905 |
|
|
$this->rollbackTransaction();
|
906 |
|
|
return -3;
|
907 |
|
|
}
|
908 |
|
|
}
|
909 |
|
|
|
910 |
|
|
return $this->endTransaction();
|
911 |
|
|
}
|
912 |
|
|
|
913 |
|
|
/**
|
914 |
|
|
* Removes a table from the database
|
915 |
|
|
* @param $table The table to drop
|
916 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
917 |
|
|
* @return 0 success
|
918 |
|
|
*/
|
919 |
|
|
function dropTable($table, $cascade) {
|
920 |
|
|
$this->fieldClean($table);
|
921 |
|
|
|
922 |
|
|
$sql = "DROP TABLE \"{$table}\"";
|
923 |
|
|
if ($cascade) $sql .= " CASCADE";
|
924 |
|
|
|
925 |
|
|
return $this->execute($sql);
|
926 |
|
|
}
|
927 |
|
|
|
928 |
|
|
/**
|
929 |
|
|
* Empties a table in the database
|
930 |
|
|
* @param $table The table to be emptied
|
931 |
|
|
* @return 0 success
|
932 |
|
|
*/
|
933 |
|
|
function emptyTable($table) {
|
934 |
|
|
$this->fieldClean($table);
|
935 |
|
|
|
936 |
|
|
$sql = "DELETE FROM \"{$table}\"";
|
937 |
|
|
|
938 |
|
|
return $this->execute($sql);
|
939 |
|
|
}
|
940 |
|
|
|
941 |
|
|
/**
|
942 |
|
|
* Renames a table
|
943 |
|
|
* @param $table The table to be renamed
|
944 |
|
|
* @param $newName The new name for the table
|
945 |
|
|
* @return 0 success
|
946 |
|
|
*/
|
947 |
|
|
function renameTable($table, $newName) {
|
948 |
|
|
$this->fieldClean($table);
|
949 |
|
|
$this->fieldClean($newName);
|
950 |
|
|
|
951 |
|
|
$sql = "ALTER TABLE \"{$table}\" RENAME TO \"{$newName}\"";
|
952 |
|
|
|
953 |
|
|
return $this->execute($sql);
|
954 |
|
|
}
|
955 |
|
|
|
956 |
|
|
/**
|
957 |
|
|
* Returns the SQL definition for the table.
|
958 |
|
|
* @pre MUST be run within a transaction
|
959 |
|
|
* @param $table The table to define
|
960 |
|
|
* @param $clean True to issue drop command, false otherwise
|
961 |
|
|
* @return A string containing the formatted SQL code
|
962 |
|
|
* @return null On error
|
963 |
|
|
*/
|
964 |
|
|
function getTableDefPrefix($table, $clean = false) {
|
965 |
|
|
// Fetch table
|
966 |
|
|
$t = $this->getTable($table);
|
967 |
|
|
if (!is_object($t) || $t->recordCount() != 1) {
|
968 |
|
|
$this->rollbackTransaction();
|
969 |
|
|
return null;
|
970 |
|
|
}
|
971 |
|
|
$this->fieldClean($t->f['relname']);
|
972 |
|
|
|
973 |
|
|
// Fetch attributes
|
974 |
|
|
$atts = $this->getTableAttributes($table);
|
975 |
|
|
if (!is_object($atts)) {
|
976 |
|
|
$this->rollbackTransaction();
|
977 |
|
|
return null;
|
978 |
|
|
}
|
979 |
|
|
|
980 |
|
|
// Fetch constraints
|
981 |
|
|
$cons = $this->getConstraints($table);
|
982 |
|
|
if (!is_object($cons)) {
|
983 |
|
|
$this->rollbackTransaction();
|
984 |
|
|
return null;
|
985 |
|
|
}
|
986 |
|
|
|
987 |
|
|
// Output a reconnect command to create the table as the correct user
|
988 |
|
|
$sql = $this->getChangeUserSQL($t->f['relowner']) . "\n\n";
|
989 |
|
|
|
990 |
|
|
// Set schema search path if we support schemas
|
991 |
|
|
if ($this->hasSchemas()) {
|
992 |
|
|
$sql .= "SET search_path = \"{$this->_schema}\", pg_catalog;\n\n";
|
993 |
|
|
}
|
994 |
|
|
|
995 |
|
|
// Begin CREATE TABLE definition
|
996 |
|
|
$sql .= "-- Definition\n\n";
|
997 |
|
|
// DROP TABLE must be fully qualified in case a table with the same name exists
|
998 |
|
|
// in pg_catalog.
|
999 |
|
|
if (!$clean) $sql .= "-- ";
|
1000 |
|
|
$sql .= "DROP TABLE ";
|
1001 |
|
|
if ($this->hasSchemas()) {
|
1002 |
|
|
$sql .= "\"{$this->_schema}\".";
|
1003 |
|
|
}
|
1004 |
|
|
$sql .= "\"{$t->f['relname']}\";\n";
|
1005 |
|
|
$sql .= "CREATE TABLE \"{$t->f['relname']}\" (\n";
|
1006 |
|
|
|
1007 |
|
|
// Output all table columns
|
1008 |
|
|
$col_comments_sql = ''; // Accumulate comments on columns
|
1009 |
|
|
$num = $atts->recordCount() + $cons->recordCount();
|
1010 |
|
|
$i = 1;
|
1011 |
|
|
while (!$atts->EOF) {
|
1012 |
|
|
$this->fieldClean($atts->f['attname']);
|
1013 |
|
|
$sql .= " \"{$atts->f['attname']}\"";
|
1014 |
|
|
// Dump SERIAL and BIGSERIAL columns correctly
|
1015 |
|
|
if ($this->phpBool($atts->f['attisserial']) &&
|
1016 |
|
|
($atts->f['type'] == 'integer' || $atts->f['type'] == 'bigint')) {
|
1017 |
|
|
if ($atts->f['type'] == 'integer')
|
1018 |
|
|
$sql .= " SERIAL";
|
1019 |
|
|
else
|
1020 |
|
|
$sql .= " BIGSERIAL";
|
1021 |
|
|
}
|
1022 |
|
|
else {
|
1023 |
|
|
$sql .= " " . $this->formatType($atts->f['type'], $atts->f['atttypmod']);
|
1024 |
|
|
|
1025 |
|
|
// Add NOT NULL if necessary
|
1026 |
|
|
if ($this->phpBool($atts->f['attnotnull']))
|
1027 |
|
|
$sql .= " NOT NULL";
|
1028 |
|
|
// Add default if necessary
|
1029 |
|
|
if ($atts->f['adsrc'] !== null)
|
1030 |
|
|
$sql .= " DEFAULT {$atts->f['adsrc']}";
|
1031 |
|
|
}
|
1032 |
|
|
|
1033 |
|
|
// Output comma or not
|
1034 |
|
|
if ($i < $num) $sql .= ",\n";
|
1035 |
|
|
else $sql .= "\n";
|
1036 |
|
|
|
1037 |
|
|
// Does this column have a comment?
|
1038 |
|
|
if ($atts->f['comment'] !== null) {
|
1039 |
|
|
$this->clean($atts->f['comment']);
|
1040 |
|
|
$col_comments_sql .= "COMMENT ON COLUMN \"{$t->f['relname']}\".\"{$atts->f['attname']}\" IS '{$atts->f['comment']}';\n";
|
1041 |
|
|
}
|
1042 |
|
|
|
1043 |
|
|
$atts->moveNext();
|
1044 |
|
|
$i++;
|
1045 |
|
|
}
|
1046 |
|
|
// Output all table constraints
|
1047 |
|
|
while (!$cons->EOF) {
|
1048 |
|
|
$this->fieldClean($cons->f['conname']);
|
1049 |
|
|
$sql .= " CONSTRAINT \"{$cons->f['conname']}\" ";
|
1050 |
|
|
// Nasty hack to support pre-7.4 PostgreSQL
|
1051 |
|
|
if ($cons->f['consrc'] !== null)
|
1052 |
|
|
$sql .= $cons->f['consrc'];
|
1053 |
|
|
else {
|
1054 |
|
|
switch ($cons->f['contype']) {
|
1055 |
|
|
case 'p':
|
1056 |
|
|
$keys = $this->getAttributeNames($table, explode(' ', $cons->f['indkey']));
|
1057 |
|
|
$sql .= "PRIMARY KEY (" . join(',', $keys) . ")";
|
1058 |
|
|
break;
|
1059 |
|
|
case 'u':
|
1060 |
|
|
$keys = $this->getAttributeNames($table, explode(' ', $cons->f['indkey']));
|
1061 |
|
|
$sql .= "UNIQUE (" . join(',', $keys) . ")";
|
1062 |
|
|
break;
|
1063 |
|
|
default:
|
1064 |
|
|
// Unrecognised constraint
|
1065 |
|
|
$this->rollbackTransaction();
|
1066 |
|
|
return null;
|
1067 |
|
|
}
|
1068 |
|
|
}
|
1069 |
|
|
|
1070 |
|
|
// Output comma or not
|
1071 |
|
|
if ($i < $num) $sql .= ",\n";
|
1072 |
|
|
else $sql .= "\n";
|
1073 |
|
|
|
1074 |
|
|
$cons->moveNext();
|
1075 |
|
|
$i++;
|
1076 |
|
|
}
|
1077 |
|
|
|
1078 |
|
|
$sql .= ")";
|
1079 |
|
|
|
1080 |
|
|
// @@@@ DUMP CLUSTERING INFORMATION
|
1081 |
|
|
|
1082 |
|
|
// Inherits
|
1083 |
|
|
/*
|
1084 |
|
|
* XXX: This is currently commented out as handling inheritance isn't this simple.
|
1085 |
|
|
* You also need to make sure you don't dump inherited columns and defaults, as well
|
1086 |
|
|
* as inherited NOT NULL and CHECK constraints. So for the time being, we just do
|
1087 |
|
|
* not claim to support inheritance.
|
1088 |
|
|
$parents = $this->getTableParents($table);
|
1089 |
|
|
if ($parents->recordCount() > 0) {
|
1090 |
|
|
$sql .= " INHERITS (";
|
1091 |
|
|
while (!$parents->EOF) {
|
1092 |
|
|
$this->fieldClean($parents->f['relname']);
|
1093 |
|
|
// Qualify the parent table if it's in another schema
|
1094 |
|
|
if ($this->hasSchemas() && $parents->f['schemaname'] != $this->_schema) {
|
1095 |
|
|
$this->fieldClean($parents->f['schemaname']);
|
1096 |
|
|
$sql .= "\"{$parents->f['schemaname']}\".";
|
1097 |
|
|
}
|
1098 |
|
|
$sql .= "\"{$parents->f['relname']}\"";
|
1099 |
|
|
|
1100 |
|
|
$parents->moveNext();
|
1101 |
|
|
if (!$parents->EOF) $sql .= ', ';
|
1102 |
|
|
}
|
1103 |
|
|
$sql .= ")";
|
1104 |
|
|
}
|
1105 |
|
|
*/
|
1106 |
|
|
|
1107 |
|
|
// Handle WITHOUT OIDS
|
1108 |
|
|
if ($this->hasWithoutOIDs()) {
|
1109 |
|
|
if ($this->hasObjectID($table))
|
1110 |
|
|
$sql .= " WITH OIDS";
|
1111 |
|
|
else
|
1112 |
|
|
$sql .= " WITHOUT OIDS";
|
1113 |
|
|
}
|
1114 |
|
|
|
1115 |
|
|
$sql .= ";\n";
|
1116 |
|
|
|
1117 |
|
|
// Column storage and statistics
|
1118 |
|
|
$atts->moveFirst();
|
1119 |
|
|
$first = true;
|
1120 |
|
|
while (!$atts->EOF) {
|
1121 |
|
|
$this->fieldClean($atts->f['attname']);
|
1122 |
|
|
// Statistics first
|
1123 |
|
|
if ($atts->f['attstattarget'] >= 0) {
|
1124 |
|
|
if ($first) {
|
1125 |
|
|
$sql .= "\n";
|
1126 |
|
|
$first = false;
|
1127 |
|
|
}
|
1128 |
|
|
$sql .= "ALTER TABLE ONLY \"{$t->f['relname']}\" ALTER COLUMN \"{$atts->f['attname']}\" SET STATISTICS {$atts->f['attstattarget']};\n";
|
1129 |
|
|
}
|
1130 |
|
|
// Then storage
|
1131 |
|
|
if ($atts->f['attstorage'] != $atts->f['typstorage']) {
|
1132 |
|
|
switch ($atts->f['attstorage']) {
|
1133 |
|
|
case 'p':
|
1134 |
|
|
$storage = 'PLAIN';
|
1135 |
|
|
break;
|
1136 |
|
|
case 'e':
|
1137 |
|
|
$storage = 'EXTERNAL';
|
1138 |
|
|
break;
|
1139 |
|
|
case 'm':
|
1140 |
|
|
$storage = 'MAIN';
|
1141 |
|
|
break;
|
1142 |
|
|
case 'x':
|
1143 |
|
|
$storage = 'EXTENDED';
|
1144 |
|
|
break;
|
1145 |
|
|
default:
|
1146 |
|
|
// Unknown storage type
|
1147 |
|
|
$this->rollbackTransaction();
|
1148 |
|
|
return null;
|
1149 |
|
|
}
|
1150 |
|
|
$sql .= "ALTER TABLE ONLY \"{$t->f['relname']}\" ALTER COLUMN \"{$atts->f['attname']}\" SET STORAGE {$storage};\n";
|
1151 |
|
|
}
|
1152 |
|
|
|
1153 |
|
|
$atts->moveNext();
|
1154 |
|
|
}
|
1155 |
|
|
|
1156 |
|
|
// Comment
|
1157 |
|
|
if ($t->f['relcomment'] !== null) {
|
1158 |
|
|
$this->clean($t->f['relcomment']);
|
1159 |
|
|
$sql .= "\n-- Comment\n\n";
|
1160 |
|
|
$sql .= "COMMENT ON TABLE \"{$t->f['relname']}\" IS '{$t->f['relcomment']}';\n";
|
1161 |
|
|
}
|
1162 |
|
|
|
1163 |
|
|
// Add comments on columns, if any
|
1164 |
|
|
if ($col_comments_sql != '') $sql .= $col_comments_sql;
|
1165 |
|
|
|
1166 |
|
|
// Privileges
|
1167 |
|
|
$privs = $this->getPrivileges($table, 'table');
|
1168 |
|
|
if (!is_array($privs)) {
|
1169 |
|
|
$this->rollbackTransaction();
|
1170 |
|
|
return null;
|
1171 |
|
|
}
|
1172 |
|
|
|
1173 |
|
|
if (sizeof($privs) > 0) {
|
1174 |
|
|
$sql .= "\n-- Privileges\n\n";
|
1175 |
|
|
/*
|
1176 |
|
|
* Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
|
1177 |
|
|
* wire-in knowledge about the default public privileges for different
|
1178 |
|
|
* kinds of objects.
|
1179 |
|
|
*/
|
1180 |
|
|
$sql .= "REVOKE ALL ON TABLE \"{$t->f['relname']}\" FROM PUBLIC;\n";
|
1181 |
|
|
foreach ($privs as $v) {
|
1182 |
|
|
// Get non-GRANT OPTION privs
|
1183 |
|
|
$nongrant = array_diff($v[2], $v[4]);
|
1184 |
|
|
|
1185 |
|
|
// Skip empty or owner ACEs
|
1186 |
|
|
if (sizeof($v[2]) == 0 || ($v[0] == 'user' && $v[1] == $t->f['relowner'])) continue;
|
1187 |
|
|
|
1188 |
|
|
// Change user if necessary
|
1189 |
|
|
if ($this->hasGrantOption() && $v[3] != $t->f['relowner']) {
|
1190 |
|
|
$grantor = $v[3];
|
1191 |
|
|
$this->clean($grantor);
|
1192 |
|
|
$sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
|
1193 |
|
|
}
|
1194 |
|
|
|
1195 |
|
|
// Output privileges with no GRANT OPTION
|
1196 |
|
|
$sql .= "GRANT " . join(', ', $nongrant) . " ON TABLE \"{$t->f['relname']}\" TO ";
|
1197 |
|
|
switch ($v[0]) {
|
1198 |
|
|
case 'public':
|
1199 |
|
|
$sql .= "PUBLIC;\n";
|
1200 |
|
|
break;
|
1201 |
|
|
case 'user':
|
1202 |
|
|
$this->fieldClean($v[1]);
|
1203 |
|
|
$sql .= "\"{$v[1]}\";\n";
|
1204 |
|
|
break;
|
1205 |
|
|
case 'group':
|
1206 |
|
|
$this->fieldClean($v[1]);
|
1207 |
|
|
$sql .= "GROUP \"{$v[1]}\";\n";
|
1208 |
|
|
break;
|
1209 |
|
|
default:
|
1210 |
|
|
// Unknown privilege type - fail
|
1211 |
|
|
$this->rollbackTransaction();
|
1212 |
|
|
return null;
|
1213 |
|
|
}
|
1214 |
|
|
|
1215 |
|
|
// Reset user if necessary
|
1216 |
|
|
if ($this->hasGrantOption() && $v[3] != $t->f['relowner']) {
|
1217 |
|
|
$sql .= "RESET SESSION AUTHORIZATION;\n";
|
1218 |
|
|
}
|
1219 |
|
|
|
1220 |
|
|
// Output privileges with GRANT OPTION
|
1221 |
|
|
|
1222 |
|
|
// Skip empty or owner ACEs
|
1223 |
|
|
if (!$this->hasGrantOption() || sizeof($v[4]) == 0) continue;
|
1224 |
|
|
|
1225 |
|
|
// Change user if necessary
|
1226 |
|
|
if ($this->hasGrantOption() && $v[3] != $t->f['relowner']) {
|
1227 |
|
|
$grantor = $v[3];
|
1228 |
|
|
$this->clean($grantor);
|
1229 |
|
|
$sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
|
1230 |
|
|
}
|
1231 |
|
|
|
1232 |
|
|
$sql .= "GRANT " . join(', ', $v[4]) . " ON \"{$t->f['relname']}\" TO ";
|
1233 |
|
|
switch ($v[0]) {
|
1234 |
|
|
case 'public':
|
1235 |
|
|
$sql .= "PUBLIC";
|
1236 |
|
|
break;
|
1237 |
|
|
case 'user':
|
1238 |
|
|
$this->fieldClean($v[1]);
|
1239 |
|
|
$sql .= "\"{$v[1]}\"";
|
1240 |
|
|
break;
|
1241 |
|
|
case 'group':
|
1242 |
|
|
$this->fieldClean($v[1]);
|
1243 |
|
|
$sql .= "GROUP \"{$v[1]}\"";
|
1244 |
|
|
break;
|
1245 |
|
|
default:
|
1246 |
|
|
// Unknown privilege type - fail
|
1247 |
|
|
return null;
|
1248 |
|
|
}
|
1249 |
|
|
$sql .= " WITH GRANT OPTION;\n";
|
1250 |
|
|
|
1251 |
|
|
// Reset user if necessary
|
1252 |
|
|
if ($this->hasGrantOption() && $v[3] != $t->f['relowner']) {
|
1253 |
|
|
$sql .= "RESET SESSION AUTHORIZATION;\n";
|
1254 |
|
|
}
|
1255 |
|
|
|
1256 |
|
|
}
|
1257 |
|
|
}
|
1258 |
|
|
|
1259 |
|
|
// Add a newline to separate data that follows (if any)
|
1260 |
|
|
$sql .= "\n";
|
1261 |
|
|
|
1262 |
|
|
return $sql;
|
1263 |
|
|
}
|
1264 |
|
|
|
1265 |
|
|
/**
|
1266 |
|
|
* Returns extra table definition information that is most usefully
|
1267 |
|
|
* dumped after the table contents for speed and efficiency reasons
|
1268 |
|
|
* @param $table The table to define
|
1269 |
|
|
* @return A string containing the formatted SQL code
|
1270 |
|
|
* @return null On error
|
1271 |
|
|
*/
|
1272 |
|
|
function getTableDefSuffix($table) {
|
1273 |
|
|
$sql = '';
|
1274 |
|
|
|
1275 |
|
|
// Indexes
|
1276 |
|
|
$indexes = $this->getIndexes($table);
|
1277 |
|
|
if (!is_object($indexes)) {
|
1278 |
|
|
$this->rollbackTransaction();
|
1279 |
|
|
return null;
|
1280 |
|
|
}
|
1281 |
|
|
|
1282 |
|
|
if ($indexes->recordCount() > 0) {
|
1283 |
|
|
$sql .= "\n-- Indexes\n\n";
|
1284 |
|
|
while (!$indexes->EOF) {
|
1285 |
|
|
$sql .= $indexes->f['inddef'] . ";\n";
|
1286 |
|
|
|
1287 |
|
|
$indexes->moveNext();
|
1288 |
|
|
}
|
1289 |
|
|
}
|
1290 |
|
|
|
1291 |
|
|
// Triggers
|
1292 |
|
|
$triggers = $this->getTriggers($table);
|
1293 |
|
|
if (!is_object($triggers)) {
|
1294 |
|
|
$this->rollbackTransaction();
|
1295 |
|
|
return null;
|
1296 |
|
|
}
|
1297 |
|
|
|
1298 |
|
|
if ($triggers->recordCount() > 0) {
|
1299 |
|
|
$sql .= "\n-- Triggers\n\n";
|
1300 |
|
|
while (!$triggers->EOF) {
|
1301 |
|
|
// Nasty hack to support pre-7.4 PostgreSQL
|
1302 |
|
|
if ($triggers->f['tgdef'] !== null)
|
1303 |
|
|
$sql .= $triggers->f['tgdef'];
|
1304 |
|
|
else
|
1305 |
|
|
$sql .= $this->getTriggerDef($triggers->f);
|
1306 |
|
|
|
1307 |
|
|
$sql .= ";\n";
|
1308 |
|
|
|
1309 |
|
|
$triggers->moveNext();
|
1310 |
|
|
}
|
1311 |
|
|
}
|
1312 |
|
|
|
1313 |
|
|
// Rules
|
1314 |
|
|
$rules = $this->getRules($table);
|
1315 |
|
|
if (!is_object($rules)) {
|
1316 |
|
|
$this->rollbackTransaction();
|
1317 |
|
|
return null;
|
1318 |
|
|
}
|
1319 |
|
|
|
1320 |
|
|
if ($rules->recordCount() > 0) {
|
1321 |
|
|
$sql .= "\n-- Rules\n\n";
|
1322 |
|
|
while (!$rules->EOF) {
|
1323 |
|
|
$sql .= $rules->f['definition'] . "\n";
|
1324 |
|
|
|
1325 |
|
|
$rules->moveNext();
|
1326 |
|
|
}
|
1327 |
|
|
}
|
1328 |
|
|
|
1329 |
|
|
return $sql;
|
1330 |
|
|
}
|
1331 |
|
|
|
1332 |
|
|
/**
|
1333 |
|
|
* Given an array of attnums and a relation, returns an array mapping
|
1334 |
|
|
* atttribute number to attribute name. Relation could be a table OR
|
1335 |
|
|
* a view.
|
1336 |
|
|
* @param $table The table to get attributes for
|
1337 |
|
|
* @param $atts An array of attribute numbers
|
1338 |
|
|
* @return An array mapping attnum to attname
|
1339 |
|
|
* @return -1 $atts must be an array
|
1340 |
|
|
* @return -2 wrong number of attributes found
|
1341 |
|
|
*/
|
1342 |
|
|
function getAttributeNames($table, $atts) {
|
1343 |
|
|
$this->clean($table);
|
1344 |
|
|
$this->arrayClean($atts);
|
1345 |
|
|
|
1346 |
|
|
if (!is_array($atts)) return -1;
|
1347 |
|
|
|
1348 |
|
|
if (sizeof($atts) == 0) return array();
|
1349 |
|
|
|
1350 |
|
|
$sql = "SELECT attnum, attname FROM pg_attribute WHERE attrelid=(SELECT oid FROM pg_class WHERE relname='{$table}') AND attnum IN ('" .
|
1351 |
|
|
join("','", $atts) . "')";
|
1352 |
|
|
|
1353 |
|
|
$rs = $this->selectSet($sql);
|
1354 |
|
|
if ($rs->recordCount() != sizeof($atts)) {
|
1355 |
|
|
return -2;
|
1356 |
|
|
}
|
1357 |
|
|
else {
|
1358 |
|
|
$temp = array();
|
1359 |
|
|
while (!$rs->EOF) {
|
1360 |
|
|
$temp[$rs->f['attnum']] = $rs->f['attname'];
|
1361 |
|
|
$rs->moveNext();
|
1362 |
|
|
}
|
1363 |
|
|
return $temp;
|
1364 |
|
|
}
|
1365 |
|
|
}
|
1366 |
|
|
|
1367 |
|
|
/**
|
1368 |
|
|
* Add a new column to a table
|
1369 |
|
|
* @param $table The table to add to
|
1370 |
|
|
* @param $column The name of the new column
|
1371 |
|
|
* @param $type The type of the column
|
1372 |
|
|
* @param $array True if array type, false otherwise
|
1373 |
|
|
* @param $notnull True if NOT NULL, false otherwise
|
1374 |
|
|
* @param $default The default for the column. '' for none.
|
1375 |
|
|
* @param $length The optional size of the column (ie. 30 for varchar(30))
|
1376 |
|
|
* @return 0 success
|
1377 |
|
|
*/
|
1378 |
|
|
function addColumn($table, $column, $type, $array, $length, $notnull, $default, $comment) {
|
1379 |
|
|
$this->fieldClean($table);
|
1380 |
|
|
$this->fieldClean($column);
|
1381 |
|
|
$this->clean($type);
|
1382 |
|
|
$this->clean($length);
|
1383 |
|
|
$this->clean($comment);
|
1384 |
|
|
|
1385 |
|
|
if ($length == '')
|
1386 |
|
|
$sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" {$type}";
|
1387 |
|
|
else {
|
1388 |
|
|
switch ($type) {
|
1389 |
|
|
// Have to account for weird placing of length for with/without
|
1390 |
|
|
// time zone types
|
1391 |
|
|
case 'timestamp with time zone':
|
1392 |
|
|
case 'timestamp without time zone':
|
1393 |
|
|
$qual = substr($type, 9);
|
1394 |
|
|
$sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" timestamp({$length}){$qual}";
|
1395 |
|
|
break;
|
1396 |
|
|
case 'time with time zone':
|
1397 |
|
|
case 'time without time zone':
|
1398 |
|
|
$qual = substr($type, 4);
|
1399 |
|
|
$sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" time({$length}){$qual}";
|
1400 |
|
|
break;
|
1401 |
|
|
default:
|
1402 |
|
|
$sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" {$type}({$length})";
|
1403 |
|
|
}
|
1404 |
|
|
}
|
1405 |
|
|
|
1406 |
|
|
// Add array qualifier, if requested
|
1407 |
|
|
if ($array) $sql .= '[]';
|
1408 |
|
|
|
1409 |
|
|
// If we have advanced column adding, add the extra qualifiers
|
1410 |
|
|
if ($this->hasAlterColumnType()) {
|
1411 |
|
|
// NOT NULL clause
|
1412 |
|
|
if ($notnull) $sql .= ' NOT NULL';
|
1413 |
|
|
|
1414 |
|
|
// DEFAULT clause
|
1415 |
|
|
if ($default != '') $sql .= ' DEFAULT ' . $default;
|
1416 |
|
|
}
|
1417 |
|
|
|
1418 |
|
|
$status = $this->beginTransaction();
|
1419 |
|
|
if ($status != 0) return -1;
|
1420 |
|
|
|
1421 |
|
|
$status = $this->execute($sql);
|
1422 |
|
|
if ($status != 0) {
|
1423 |
|
|
$this->rollbackTransaction();
|
1424 |
|
|
return -1;
|
1425 |
|
|
}
|
1426 |
|
|
|
1427 |
|
|
$status = $this->setComment('COLUMN', $column, $table, $comment);
|
1428 |
|
|
if ($status != 0) {
|
1429 |
|
|
$this->rollbackTransaction();
|
1430 |
|
|
return -1;
|
1431 |
|
|
}
|
1432 |
|
|
|
1433 |
|
|
return $this->endTransaction();
|
1434 |
|
|
}
|
1435 |
|
|
|
1436 |
|
|
/**
|
1437 |
|
|
* Sets default value of a column
|
1438 |
|
|
* @param $table The table from which to drop
|
1439 |
|
|
* @param $column The column name to set
|
1440 |
|
|
* @param $default The new default value
|
1441 |
|
|
* @return 0 success
|
1442 |
|
|
*/
|
1443 |
|
|
function setColumnDefault($table, $column, $default) {
|
1444 |
|
|
$this->fieldClean($table);
|
1445 |
|
|
$this->fieldClean($column);
|
1446 |
|
|
|
1447 |
|
|
$sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";
|
1448 |
|
|
|
1449 |
|
|
return $this->execute($sql);
|
1450 |
|
|
}
|
1451 |
|
|
|
1452 |
|
|
/**
|
1453 |
|
|
* Drops default value of a column
|
1454 |
|
|
* @param $table The table from which to drop
|
1455 |
|
|
* @param $column The column name to drop default
|
1456 |
|
|
* @return 0 success
|
1457 |
|
|
*/
|
1458 |
|
|
function dropColumnDefault($table, $column) {
|
1459 |
|
|
$this->fieldClean($table);
|
1460 |
|
|
$this->fieldClean($column);
|
1461 |
|
|
|
1462 |
|
|
$sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT";
|
1463 |
|
|
|
1464 |
|
|
return $this->execute($sql);
|
1465 |
|
|
}
|
1466 |
|
|
|
1467 |
|
|
/**
|
1468 |
|
|
* Sets whether or not a column can contain NULLs
|
1469 |
|
|
* @param $table The table that contains the column
|
1470 |
|
|
* @param $column The column to alter
|
1471 |
|
|
* @param $state True to set null, false to set not null
|
1472 |
|
|
* @return 0 success
|
1473 |
|
|
* @return -1 attempt to set not null, but column contains nulls
|
1474 |
|
|
* @return -2 transaction error
|
1475 |
|
|
* @return -3 lock error
|
1476 |
|
|
* @return -4 update error
|
1477 |
|
|
*/
|
1478 |
|
|
function setColumnNull($table, $column, $state) {
|
1479 |
|
|
$this->fieldClean($table);
|
1480 |
|
|
$this->fieldClean($column);
|
1481 |
|
|
|
1482 |
|
|
// Begin transaction
|
1483 |
|
|
$status = $this->beginTransaction();
|
1484 |
|
|
if ($status != 0) return -2;
|
1485 |
|
|
|
1486 |
|
|
// Properly lock the table
|
1487 |
|
|
$sql = "LOCK TABLE \"{$table}\" IN ACCESS EXCLUSIVE MODE";
|
1488 |
|
|
$status = $this->execute($sql);
|
1489 |
|
|
if ($status != 0) {
|
1490 |
|
|
$this->rollbackTransaction();
|
1491 |
|
|
return -3;
|
1492 |
|
|
}
|
1493 |
|
|
|
1494 |
|
|
// Check for existing nulls
|
1495 |
|
|
if (!$state) {
|
1496 |
|
|
$sql = "SELECT COUNT(*) AS total FROM \"{$table}\" WHERE \"{$column}\" IS NULL";
|
1497 |
|
|
$result = $this->selectField($sql, 'total');
|
1498 |
|
|
if ($result > 0) {
|
1499 |
|
|
$this->rollbackTransaction();
|
1500 |
|
|
return -1;
|
1501 |
|
|
}
|
1502 |
|
|
}
|
1503 |
|
|
|
1504 |
|
|
// Otherwise update the table. Note the reverse-sensed $state variable
|
1505 |
|
|
$sql = "UPDATE pg_attribute SET attnotnull = " . (($state) ? 'false' : 'true') . "
|
1506 |
|
|
WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '{$table}')
|
1507 |
|
|
AND attname = '{$column}'";
|
1508 |
|
|
|
1509 |
|
|
$status = $this->execute($sql);
|
1510 |
|
|
if ($status != 0) {
|
1511 |
|
|
$this->rollbackTransaction();
|
1512 |
|
|
return -4;
|
1513 |
|
|
}
|
1514 |
|
|
|
1515 |
|
|
// Otherwise, close the transaction
|
1516 |
|
|
return $this->endTransaction();
|
1517 |
|
|
}
|
1518 |
|
|
|
1519 |
|
|
/**
|
1520 |
|
|
* Renames a column in a table
|
1521 |
|
|
* @param $table The table containing the column to be renamed
|
1522 |
|
|
* @param $column The column to be renamed
|
1523 |
|
|
* @param $newName The new name for the column
|
1524 |
|
|
* @return 0 success
|
1525 |
|
|
*/
|
1526 |
|
|
function renameColumn($table, $column, $newName) {
|
1527 |
|
|
$this->fieldClean($table);
|
1528 |
|
|
$this->fieldClean($column);
|
1529 |
|
|
$this->fieldClean($newName);
|
1530 |
|
|
|
1531 |
|
|
$sql = "ALTER TABLE \"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\"";
|
1532 |
|
|
|
1533 |
|
|
return $this->execute($sql);
|
1534 |
|
|
}
|
1535 |
|
|
|
1536 |
|
|
/**
|
1537 |
|
|
* Drops a column from a table
|
1538 |
|
|
* @param $table The table from which to drop a column
|
1539 |
|
|
* @param $column The column to be dropped
|
1540 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
1541 |
|
|
* @return 0 success
|
1542 |
|
|
* @return -99 not implemented
|
1543 |
|
|
*/
|
1544 |
|
|
function dropColumn($table, $column, $cascade) {
|
1545 |
|
|
return -99;
|
1546 |
|
|
}
|
1547 |
|
|
|
1548 |
|
|
/**
|
1549 |
|
|
* Alters a column in a table OR view
|
1550 |
|
|
* @param $table The table in which the column resides
|
1551 |
|
|
* @param $column The column to alter
|
1552 |
|
|
* @param $name The new name for the column
|
1553 |
|
|
* @param $notnull (boolean) True if not null, false otherwise
|
1554 |
|
|
* @param $oldnotnull (boolean) True if column is already not null, false otherwise
|
1555 |
|
|
* @param $default The new default for the column
|
1556 |
|
|
* @param $olddefault The old default for the column
|
1557 |
|
|
* @param $type The new type for the column
|
1558 |
|
|
* @param $array True if array type, false otherwise
|
1559 |
|
|
* @param $length The optional size of the column (ie. 30 for varchar(30))
|
1560 |
|
|
* @param $oldtype The old type for the column
|
1561 |
|
|
* @param $comment Comment for the column
|
1562 |
|
|
* @return 0 success
|
1563 |
|
|
* @return -1 set not null error
|
1564 |
|
|
* @return -2 set default error
|
1565 |
|
|
* @return -3 rename column error
|
1566 |
|
|
* @return -4 comment error
|
1567 |
|
|
*/
|
1568 |
|
|
function alterColumn($table, $column, $name, $notnull, $oldnotnull, $default, $olddefault,
|
1569 |
|
|
$type, $length, $array, $oldtype, $comment) {
|
1570 |
|
|
$this->beginTransaction();
|
1571 |
|
|
|
1572 |
|
|
// @@ NEED TO HANDLE "NESTED" TRANSACTION HERE
|
1573 |
|
|
if ($notnull != $oldnotnull) {
|
1574 |
|
|
$status = $this->setColumnNull($table, $column, !$notnull);
|
1575 |
|
|
if ($status != 0) {
|
1576 |
|
|
$this->rollbackTransaction();
|
1577 |
|
|
return -1;
|
1578 |
|
|
}
|
1579 |
|
|
}
|
1580 |
|
|
|
1581 |
|
|
// Set default, if it has changed
|
1582 |
|
|
if ($default != $olddefault) {
|
1583 |
|
|
if ($default == '')
|
1584 |
|
|
$status = $this->dropColumnDefault($table, $column);
|
1585 |
|
|
else
|
1586 |
|
|
$status = $this->setColumnDefault($table, $column, $default);
|
1587 |
|
|
|
1588 |
|
|
if ($status != 0) {
|
1589 |
|
|
$this->rollbackTransaction();
|
1590 |
|
|
return -2;
|
1591 |
|
|
}
|
1592 |
|
|
}
|
1593 |
|
|
|
1594 |
|
|
// Rename the column, if it has been changed
|
1595 |
|
|
if ($column != $name) {
|
1596 |
|
|
$status = $this->renameColumn($table, $column, $name);
|
1597 |
|
|
if ($status != 0) {
|
1598 |
|
|
$this->rollbackTransaction();
|
1599 |
|
|
return -3;
|
1600 |
|
|
}
|
1601 |
|
|
}
|
1602 |
|
|
|
1603 |
|
|
// Parameters must be cleaned for the setComment function. It's ok to do
|
1604 |
|
|
// that here since this is the last time these variables are used.
|
1605 |
|
|
$this->fieldClean($name);
|
1606 |
|
|
$this->fieldClean($table);
|
1607 |
|
|
$this->clean($comment);
|
1608 |
|
|
$status = $this->setComment('COLUMN', $name, $table, $comment);
|
1609 |
|
|
if ($status != 0) {
|
1610 |
|
|
$this->rollbackTransaction();
|
1611 |
|
|
return -4;
|
1612 |
|
|
}
|
1613 |
|
|
|
1614 |
|
|
return $this->endTransaction();
|
1615 |
|
|
}
|
1616 |
|
|
|
1617 |
|
|
// Row functions
|
1618 |
|
|
|
1619 |
|
|
/**
|
1620 |
|
|
* Delete a row from a table
|
1621 |
|
|
* @param $table The table from which to delete
|
1622 |
|
|
* @param $key An array mapping column => value to delete
|
1623 |
|
|
* @return 0 success
|
1624 |
|
|
*/
|
1625 |
|
|
function deleteRow($table, $key) {
|
1626 |
|
|
if (!is_array($key)) return -1;
|
1627 |
|
|
else {
|
1628 |
|
|
// Begin transaction. We do this so that we can ensure only one row is
|
1629 |
|
|
// deleted
|
1630 |
|
|
$status = $this->beginTransaction();
|
1631 |
|
|
if ($status != 0) {
|
1632 |
|
|
$this->rollbackTransaction();
|
1633 |
|
|
return -1;
|
1634 |
|
|
}
|
1635 |
|
|
|
1636 |
|
|
$status = $this->delete($table, $key);
|
1637 |
|
|
if ($status != 0 || $this->conn->Affected_Rows() != 1) {
|
1638 |
|
|
$this->rollbackTransaction();
|
1639 |
|
|
return -2;
|
1640 |
|
|
}
|
1641 |
|
|
|
1642 |
|
|
// End transaction
|
1643 |
|
|
return $this->endTransaction();
|
1644 |
|
|
}
|
1645 |
|
|
}
|
1646 |
|
|
|
1647 |
|
|
/**
|
1648 |
|
|
* Updates a row in a table
|
1649 |
|
|
* @param $table The table in which to update
|
1650 |
|
|
* @param $vars An array mapping new values for the row
|
1651 |
|
|
* @param $nulls An array mapping column => something if it is to be null
|
1652 |
|
|
* @param $format An array of the data type (VALUE or EXPRESSION)
|
1653 |
|
|
* @param $types An array of field types
|
1654 |
|
|
* @param $keyarr An array mapping column => value to update
|
1655 |
|
|
* @return 0 success
|
1656 |
|
|
* @return -1 invalid parameters
|
1657 |
|
|
*/
|
1658 |
|
|
function editRow($table, $vars, $nulls, $format, $types, $keyarr) {
|
1659 |
|
|
if (!is_array($vars) || !is_array($nulls) || !is_array($format)
|
1660 |
|
|
|| !is_array($types)) return -1;
|
1661 |
|
|
else {
|
1662 |
|
|
$this->fieldClean($table);
|
1663 |
|
|
|
1664 |
|
|
// Build clause
|
1665 |
|
|
if (sizeof($vars) > 0) {
|
1666 |
|
|
foreach($vars as $key => $value) {
|
1667 |
|
|
$this->fieldClean($key);
|
1668 |
|
|
|
1669 |
|
|
// Handle NULL values
|
1670 |
|
|
if (isset($nulls[$key])) $tmp = 'NULL';
|
1671 |
|
|
else $tmp = $this->formatValue($types[$key], $format[$key], $value);
|
1672 |
|
|
|
1673 |
|
|
if (isset($sql)) $sql .= ", \"{$key}\"={$tmp}";
|
1674 |
|
|
else $sql = "UPDATE \"{$table}\" SET \"{$key}\"={$tmp}";
|
1675 |
|
|
}
|
1676 |
|
|
$first = true;
|
1677 |
|
|
foreach ($keyarr as $k => $v) {
|
1678 |
|
|
$this->fieldClean($k);
|
1679 |
|
|
$this->clean($v);
|
1680 |
|
|
if ($first) {
|
1681 |
|
|
$sql .= " WHERE \"{$k}\"='{$v}'";
|
1682 |
|
|
$first = false;
|
1683 |
|
|
}
|
1684 |
|
|
else $sql .= " AND \"{$k}\"='{$v}'";
|
1685 |
|
|
}
|
1686 |
|
|
}
|
1687 |
|
|
|
1688 |
|
|
// Begin transaction. We do this so that we can ensure only one row is
|
1689 |
|
|
// edited
|
1690 |
|
|
$status = $this->beginTransaction();
|
1691 |
|
|
if ($status != 0) {
|
1692 |
|
|
$this->rollbackTransaction();
|
1693 |
|
|
return -1;
|
1694 |
|
|
}
|
1695 |
|
|
|
1696 |
|
|
$status = $this->execute($sql);
|
1697 |
|
|
if ($status != 0 || $this->conn->Affected_Rows() != 1) {
|
1698 |
|
|
$this->rollbackTransaction();
|
1699 |
|
|
return -2;
|
1700 |
|
|
}
|
1701 |
|
|
|
1702 |
|
|
// End transaction
|
1703 |
|
|
return $this->endTransaction();
|
1704 |
|
|
}
|
1705 |
|
|
}
|
1706 |
|
|
|
1707 |
|
|
/**
|
1708 |
|
|
* Adds a new row to a table
|
1709 |
|
|
* @param $table The table in which to insert
|
1710 |
|
|
* @param $var An array mapping new values for the row
|
1711 |
|
|
* @param $nulls An array mapping column => something if it is to be null
|
1712 |
|
|
* @param $format An array of the data type (VALUE or EXPRESSION)
|
1713 |
|
|
* @param $types An array of field types
|
1714 |
|
|
* @return 0 success
|
1715 |
|
|
* @return -1 invalid parameters
|
1716 |
|
|
*/
|
1717 |
|
|
function insertRow($table, $vars, $nulls, $format, $types) {
|
1718 |
|
|
if (!is_array($vars) || !is_array($nulls) || !is_array($format)
|
1719 |
|
|
|| !is_array($types)) return -1;
|
1720 |
|
|
else {
|
1721 |
|
|
$this->fieldClean($table);
|
1722 |
|
|
|
1723 |
|
|
// Build clause
|
1724 |
|
|
if (sizeof($vars) > 0) {
|
1725 |
|
|
$fields = '';
|
1726 |
|
|
$values = '';
|
1727 |
|
|
foreach($vars as $key => $value) {
|
1728 |
|
|
$this->fieldClean($key);
|
1729 |
|
|
|
1730 |
|
|
// Handle NULL values
|
1731 |
|
|
if (isset($nulls[$key])) $tmp = 'NULL';
|
1732 |
|
|
else $tmp = $this->formatValue($types[$key], $format[$key], $value);
|
1733 |
|
|
|
1734 |
|
|
if ($fields) $fields .= ", \"{$key}\"";
|
1735 |
|
|
else $fields = "INSERT INTO \"{$table}\" (\"{$key}\"";
|
1736 |
|
|
|
1737 |
|
|
if ($values) $values .= ", {$tmp}";
|
1738 |
|
|
else $values = ") VALUES ({$tmp}";
|
1739 |
|
|
}
|
1740 |
|
|
$sql = $fields . $values . ')';
|
1741 |
|
|
}
|
1742 |
|
|
return $this->execute($sql);
|
1743 |
|
|
}
|
1744 |
|
|
}
|
1745 |
|
|
|
1746 |
|
|
/**
|
1747 |
|
|
* Returns a recordset of all columns in a table
|
1748 |
|
|
* @param $table The name of a table
|
1749 |
|
|
* @param $key The associative array holding the key to retrieve
|
1750 |
|
|
* @return A recordset
|
1751 |
|
|
*/
|
1752 |
|
|
function browseRow($table, $key) {
|
1753 |
|
|
$this->fieldClean($table);
|
1754 |
|
|
|
1755 |
|
|
$sql = "SELECT * FROM \"{$table}\"";
|
1756 |
|
|
if (is_array($key) && sizeof($key) > 0) {
|
1757 |
|
|
$sql .= " WHERE true";
|
1758 |
|
|
foreach ($key as $k => $v) {
|
1759 |
|
|
$this->fieldClean($k);
|
1760 |
|
|
$this->clean($v);
|
1761 |
|
|
$sql .= " AND \"{$k}\"='{$v}'";
|
1762 |
|
|
}
|
1763 |
|
|
}
|
1764 |
|
|
|
1765 |
|
|
return $this->selectSet($sql);
|
1766 |
|
|
}
|
1767 |
|
|
|
1768 |
|
|
/**
|
1769 |
|
|
* Get the fields for uniquely identifying a row in a table
|
1770 |
|
|
* @param $table The table for which to retrieve the identifier
|
1771 |
|
|
* @return An array mapping attribute number to attribute name, empty for no identifiers
|
1772 |
|
|
* @return -1 error
|
1773 |
|
|
*/
|
1774 |
|
|
function getRowIdentifier($table) {
|
1775 |
|
|
$oldtable = $table;
|
1776 |
|
|
$this->clean($table);
|
1777 |
|
|
|
1778 |
|
|
$status = $this->beginTransaction();
|
1779 |
|
|
if ($status != 0) return -1;
|
1780 |
|
|
|
1781 |
|
|
// Get the first primary or unique index (sorting primary keys first) that
|
1782 |
|
|
// is NOT a partial index.
|
1783 |
|
|
$sql = "SELECT indrelid, indkey FROM pg_index WHERE indisunique AND indrelid=(SELECT oid FROM pg_class
|
1784 |
|
|
WHERE relname='{$table}') AND indpred='' AND indproc=0 ORDER BY indisprimary DESC LIMIT 1";
|
1785 |
|
|
$rs = $this->selectSet($sql);
|
1786 |
|
|
|
1787 |
|
|
// If none, check for an OID column. Even though OIDs can be duplicated, the edit and delete row
|
1788 |
|
|
// functions check that they're only modiying a single row. Otherwise, return empty array.
|
1789 |
|
|
if ($rs->recordCount() == 0) {
|
1790 |
|
|
// Check for OID column
|
1791 |
|
|
$temp = array();
|
1792 |
|
|
if ($this->hasObjectID($table)) {
|
1793 |
|
|
$temp = array('oid');
|
1794 |
|
|
}
|
1795 |
|
|
$this->endTransaction();
|
1796 |
|
|
return $temp;
|
1797 |
|
|
}
|
1798 |
|
|
// Otherwise find the names of the keys
|
1799 |
|
|
else {
|
1800 |
|
|
$attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->f['indkey']));
|
1801 |
|
|
if (!is_array($attnames)) {
|
1802 |
|
|
$this->rollbackTransaction();
|
1803 |
|
|
return -1;
|
1804 |
|
|
}
|
1805 |
|
|
else {
|
1806 |
|
|
$this->endTransaction();
|
1807 |
|
|
return $attnames;
|
1808 |
|
|
}
|
1809 |
|
|
}
|
1810 |
|
|
}
|
1811 |
|
|
|
1812 |
|
|
// Sequence functions
|
1813 |
|
|
|
1814 |
|
|
/**
|
1815 |
|
|
* Returns all sequences in the current database
|
1816 |
|
|
* @return A recordset
|
1817 |
|
|
*/
|
1818 |
|
|
function getSequences($all = false) {
|
1819 |
|
|
// $all argument is ignored as it makes no difference
|
1820 |
|
|
$sql = "SELECT
|
1821 |
|
|
c.relname AS seqname,
|
1822 |
|
|
u.usename AS seqowner,
|
1823 |
|
|
(SELECT description FROM pg_description pd WHERE c.oid=pd.objoid) AS seqcomment
|
1824 |
|
|
FROM
|
1825 |
|
|
pg_class c, pg_user u WHERE c.relowner=u.usesysid AND c.relkind = 'S' ORDER BY seqname";
|
1826 |
|
|
|
1827 |
|
|
return $this->selectSet( $sql );
|
1828 |
|
|
}
|
1829 |
|
|
|
1830 |
|
|
/**
|
1831 |
|
|
* Returns properties of a single sequence
|
1832 |
|
|
* @param $sequence Sequence name
|
1833 |
|
|
* @return A recordset
|
1834 |
|
|
*/
|
1835 |
|
|
function getSequence($sequence) {
|
1836 |
|
|
$temp = $sequence;
|
1837 |
|
|
// Need both field cleaned and literal cleaned versions
|
1838 |
|
|
$this->fieldClean($sequence);
|
1839 |
|
|
$this->clean($temp);
|
1840 |
|
|
|
1841 |
|
|
$sql = "SELECT sequence_name AS seqname, *,
|
1842 |
|
|
(SELECT description FROM pg_description pd WHERE pd.objoid=(SELECT oid FROM pg_class WHERE relname='{$temp}')) AS seqcomment
|
1843 |
|
|
FROM \"{$sequence}\" AS s";
|
1844 |
|
|
|
1845 |
|
|
return $this->selectSet( $sql );
|
1846 |
|
|
}
|
1847 |
|
|
|
1848 |
|
|
/**
|
1849 |
|
|
* Drops a given sequence
|
1850 |
|
|
* @param $sequence Sequence name
|
1851 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
1852 |
|
|
* @return 0 success
|
1853 |
|
|
*/
|
1854 |
|
|
function dropSequence($sequence, $cascade) {
|
1855 |
|
|
$this->fieldClean($sequence);
|
1856 |
|
|
|
1857 |
|
|
$sql = "DROP SEQUENCE \"{$sequence}\"";
|
1858 |
|
|
if ($cascade) $sql .= " CASCADE";
|
1859 |
|
|
|
1860 |
|
|
return $this->execute($sql);
|
1861 |
|
|
}
|
1862 |
|
|
|
1863 |
|
|
/**
|
1864 |
|
|
* Resets a given sequence to min value of sequence
|
1865 |
|
|
* @param $sequence Sequence name
|
1866 |
|
|
* @return 0 success
|
1867 |
|
|
* @return -1 sequence not found
|
1868 |
|
|
*/
|
1869 |
|
|
function resetSequence($sequence) {
|
1870 |
|
|
// Get the minimum value of the sequence
|
1871 |
|
|
$seq = $this->getSequence($sequence);
|
1872 |
|
|
if ($seq->recordCount() != 1) return -1;
|
1873 |
|
|
$minvalue = $seq->f[$this->sqFields['minvalue']];
|
1874 |
|
|
|
1875 |
|
|
/* This double-cleaning is deliberate */
|
1876 |
|
|
$this->fieldClean($sequence);
|
1877 |
|
|
$this->clean($sequence);
|
1878 |
|
|
|
1879 |
|
|
$sql = "SELECT SETVAL('\"{$sequence}\"', {$minvalue})";
|
1880 |
|
|
|
1881 |
|
|
return $this->execute($sql);
|
1882 |
|
|
}
|
1883 |
|
|
|
1884 |
|
|
/**
|
1885 |
|
|
* Creates a new sequence
|
1886 |
|
|
* @param $sequence Sequence name
|
1887 |
|
|
* @param $increment The increment
|
1888 |
|
|
* @param $minvalue The min value
|
1889 |
|
|
* @param $maxvalue The max value
|
1890 |
|
|
* @param $startvalue The starting value
|
1891 |
|
|
* @param $cachevalue The cache value
|
1892 |
|
|
* @param $cycledvalue True if cycled, false otherwise
|
1893 |
|
|
* @return 0 success
|
1894 |
|
|
*/
|
1895 |
|
|
function createSequence($sequence, $increment, $minvalue, $maxvalue,
|
1896 |
|
|
$startvalue, $cachevalue, $cycledvalue) {
|
1897 |
|
|
$this->fieldClean($sequence);
|
1898 |
|
|
$this->clean($increment);
|
1899 |
|
|
$this->clean($minvalue);
|
1900 |
|
|
$this->clean($maxvalue);
|
1901 |
|
|
$this->clean($startvalue);
|
1902 |
|
|
$this->clean($cachevalue);
|
1903 |
|
|
|
1904 |
|
|
$sql = "CREATE SEQUENCE \"{$sequence}\"";
|
1905 |
|
|
if ($increment != '') $sql .= " INCREMENT {$increment}";
|
1906 |
|
|
if ($minvalue != '') $sql .= " MINVALUE {$minvalue}";
|
1907 |
|
|
if ($maxvalue != '') $sql .= " MAXVALUE {$maxvalue}";
|
1908 |
|
|
if ($startvalue != '') $sql .= " START {$startvalue}";
|
1909 |
|
|
if ($cachevalue != '') $sql .= " CACHE {$cachevalue}";
|
1910 |
|
|
if ($cycledvalue) $sql .= " CYCLE";
|
1911 |
|
|
|
1912 |
|
|
return $this->execute($sql);
|
1913 |
|
|
}
|
1914 |
|
|
|
1915 |
|
|
// Constraint functions
|
1916 |
|
|
|
1917 |
|
|
/**
|
1918 |
|
|
* Returns a list of all constraints on a table
|
1919 |
|
|
* @param $table The table to find rules for
|
1920 |
|
|
* @return A recordset
|
1921 |
|
|
*/
|
1922 |
|
|
function getConstraints($table) {
|
1923 |
|
|
$this->clean($table);
|
1924 |
|
|
|
1925 |
|
|
$status = $this->beginTransaction();
|
1926 |
|
|
if ($status != 0) return -1;
|
1927 |
|
|
|
1928 |
|
|
$sql = "
|
1929 |
|
|
SELECT
|
1930 |
|
|
rcname AS conname,
|
1931 |
|
|
'CHECK (' || rcsrc || ')' AS consrc,
|
1932 |
|
|
'c' AS contype,
|
1933 |
|
|
NULL::int2vector AS indkey
|
1934 |
|
|
FROM
|
1935 |
|
|
pg_relcheck
|
1936 |
|
|
WHERE
|
1937 |
|
|
rcrelid = (SELECT oid FROM pg_class WHERE relname='{$table}')
|
1938 |
|
|
UNION ALL
|
1939 |
|
|
SELECT
|
1940 |
|
|
pc.relname,
|
1941 |
|
|
NULL,
|
1942 |
|
|
CASE WHEN indisprimary THEN
|
1943 |
|
|
'p'
|
1944 |
|
|
ELSE
|
1945 |
|
|
'u'
|
1946 |
|
|
END,
|
1947 |
|
|
indkey
|
1948 |
|
|
FROM
|
1949 |
|
|
pg_class pc,
|
1950 |
|
|
pg_index pi
|
1951 |
|
|
WHERE
|
1952 |
|
|
pc.oid=pi.indexrelid
|
1953 |
|
|
AND (pi.indisunique OR pi.indisprimary)
|
1954 |
|
|
AND pi.indrelid = (SELECT oid FROM pg_class WHERE relname='{$table}')
|
1955 |
|
|
ORDER BY
|
1956 |
|
|
1
|
1957 |
|
|
";
|
1958 |
|
|
|
1959 |
|
|
return $this->selectSet($sql);
|
1960 |
|
|
}
|
1961 |
|
|
|
1962 |
|
|
/**
|
1963 |
|
|
* Adds a check constraint to a table
|
1964 |
|
|
* @param $table The table to which to add the check
|
1965 |
|
|
* @param $definition The definition of the check
|
1966 |
|
|
* @param $name (optional) The name to give the check, otherwise default name is assigned
|
1967 |
|
|
* @return 0 success
|
1968 |
|
|
*/
|
1969 |
|
|
function addCheckConstraint($table, $definition, $name = '') {
|
1970 |
|
|
$this->fieldClean($table);
|
1971 |
|
|
$this->fieldClean($name);
|
1972 |
|
|
// @@ How the heck do you clean a definition???
|
1973 |
|
|
|
1974 |
|
|
$sql = "ALTER TABLE \"{$table}\" ADD ";
|
1975 |
|
|
if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
|
1976 |
|
|
$sql .= "CHECK ({$definition})";
|
1977 |
|
|
|
1978 |
|
|
return $this->execute($sql);
|
1979 |
|
|
}
|
1980 |
|
|
|
1981 |
|
|
/**
|
1982 |
|
|
* Drops a check constraint from a table
|
1983 |
|
|
* @param $table The table from which to drop the check
|
1984 |
|
|
* @param $name The name of the check to be dropped
|
1985 |
|
|
* @return 0 success
|
1986 |
|
|
* @return -2 transaction error
|
1987 |
|
|
* @return -3 lock error
|
1988 |
|
|
* @return -4 check drop error
|
1989 |
|
|
*/
|
1990 |
|
|
function dropCheckConstraint($table, $name) {
|
1991 |
|
|
$this->clean($table);
|
1992 |
|
|
$this->clean($name);
|
1993 |
|
|
|
1994 |
|
|
// Begin transaction
|
1995 |
|
|
$status = $this->beginTransaction();
|
1996 |
|
|
if ($status != 0) return -2;
|
1997 |
|
|
|
1998 |
|
|
// Properly lock the table
|
1999 |
|
|
$sql = "LOCK TABLE \"{$table}\" IN ACCESS EXCLUSIVE MODE";
|
2000 |
|
|
$status = $this->execute($sql);
|
2001 |
|
|
if ($status != 0) {
|
2002 |
|
|
$this->rollbackTransaction();
|
2003 |
|
|
return -3;
|
2004 |
|
|
}
|
2005 |
|
|
|
2006 |
|
|
// Delete the check constraint
|
2007 |
|
|
$sql = "DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_class WHERE relname='{$table}') AND rcname='{$name}'";
|
2008 |
|
|
$status = $this->execute($sql);
|
2009 |
|
|
if ($status != 0) {
|
2010 |
|
|
$this->rollbackTransaction();
|
2011 |
|
|
return -4;
|
2012 |
|
|
}
|
2013 |
|
|
|
2014 |
|
|
// Update the pg_class catalog to reflect the new number of checks
|
2015 |
|
|
$sql = "UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE
|
2016 |
|
|
rcrelid=(SELECT oid FROM pg_class WHERE relname='{$table}'))
|
2017 |
|
|
WHERE relname='{$table}'";
|
2018 |
|
|
$status = $this->execute($sql);
|
2019 |
|
|
if ($status != 0) {
|
2020 |
|
|
$this->rollbackTransaction();
|
2021 |
|
|
return -4;
|
2022 |
|
|
}
|
2023 |
|
|
|
2024 |
|
|
// Otherwise, close the transaction
|
2025 |
|
|
return $this->endTransaction();
|
2026 |
|
|
}
|
2027 |
|
|
|
2028 |
|
|
// Constraint functions
|
2029 |
|
|
|
2030 |
|
|
/**
|
2031 |
|
|
* Removes a constraint from a relation
|
2032 |
|
|
* @param $constraint The constraint to drop
|
2033 |
|
|
* @param $relation The relation from which to drop
|
2034 |
|
|
* @param $type The type of constraint (c, f, u or p)
|
2035 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
2036 |
|
|
* @return 0 success
|
2037 |
|
|
* @return -99 dropping foreign keys not supported
|
2038 |
|
|
*/
|
2039 |
|
|
function dropConstraint($constraint, $relation, $type, $cascade) {
|
2040 |
|
|
$this->fieldClean($constraint);
|
2041 |
|
|
$this->fieldClean($relation);
|
2042 |
|
|
|
2043 |
|
|
switch ($type) {
|
2044 |
|
|
case 'c':
|
2045 |
|
|
// CHECK constraint
|
2046 |
|
|
return $this->dropCheckConstraint($relation, $constraint);
|
2047 |
|
|
break;
|
2048 |
|
|
case 'p':
|
2049 |
|
|
case 'u':
|
2050 |
|
|
// PRIMARY KEY or UNIQUE constraint
|
2051 |
|
|
return $this->dropIndex($constraint, $cascade);
|
2052 |
|
|
break;
|
2053 |
|
|
case 'f':
|
2054 |
|
|
// FOREIGN KEY constraint
|
2055 |
|
|
return -99;
|
2056 |
|
|
}
|
2057 |
|
|
}
|
2058 |
|
|
|
2059 |
|
|
/**
|
2060 |
|
|
* Adds a unique constraint to a table
|
2061 |
|
|
* @param $table The table to which to add the unique
|
2062 |
|
|
* @param $fields (array) An array of fields over which to add the unique
|
2063 |
|
|
* @param $name (optional) The name to give the unique, otherwise default name is assigned
|
2064 |
|
|
* @return 0 success
|
2065 |
|
|
* @return -1 invalid fields
|
2066 |
|
|
*/
|
2067 |
|
|
function addUniqueKey($table, $fields, $name = '') {
|
2068 |
|
|
if (!is_array($fields) || sizeof($fields) == 0) return -1;
|
2069 |
|
|
$this->fieldClean($table);
|
2070 |
|
|
$this->fieldArrayClean($fields);
|
2071 |
|
|
$this->fieldClean($name);
|
2072 |
|
|
|
2073 |
|
|
if ($name != '')
|
2074 |
|
|
$sql = "CREATE UNIQUE INDEX \"{$name}\" ON \"{$table}\"(\"" . join('","', $fields) . "\")";
|
2075 |
|
|
else return -99; // Not supported
|
2076 |
|
|
|
2077 |
|
|
return $this->execute($sql);
|
2078 |
|
|
}
|
2079 |
|
|
|
2080 |
|
|
/**
|
2081 |
|
|
* Adds a foreign key constraint to a table
|
2082 |
|
|
* @param $targschema The schema that houses the target table to which to add the foreign key
|
2083 |
|
|
* @param $targtable The table to which to add the foreign key
|
2084 |
|
|
* @param $target The table that contains the target columns
|
2085 |
|
|
* @param $sfields (array) An array of source fields over which to add the foreign key
|
2086 |
|
|
* @param $tfields (array) An array of target fields over which to add the foreign key
|
2087 |
|
|
* @param $upd_action The action for updates (eg. RESTRICT)
|
2088 |
|
|
* @param $del_action The action for deletes (eg. RESTRICT)
|
2089 |
|
|
* @param $match The match type (eg. MATCH FULL)
|
2090 |
|
|
* @param $deferrable The deferrability (eg. NOT DEFERRABLE)
|
2091 |
|
|
* @param $intially The initial deferrability (eg. INITIALLY IMMEDIATE)
|
2092 |
|
|
* @param $name (optional) The name to give the key, otherwise default name is assigned
|
2093 |
|
|
* @return 0 success
|
2094 |
|
|
* @return -1 no fields given
|
2095 |
|
|
*/
|
2096 |
|
|
function addForeignKey($table, $targschema, $targtable, $sfields, $tfields, $upd_action, $del_action,
|
2097 |
|
|
$match, $deferrable, $initially, $name = '') {
|
2098 |
|
|
if (!is_array($sfields) || sizeof($sfields) == 0 ||
|
2099 |
|
|
!is_array($tfields) || sizeof($tfields) == 0) return -1;
|
2100 |
|
|
$this->fieldClean($table);
|
2101 |
|
|
$this->fieldClean($targschema);
|
2102 |
|
|
$this->fieldClean($targtable);
|
2103 |
|
|
$this->fieldArrayClean($sfields);
|
2104 |
|
|
$this->fieldArrayClean($tfields);
|
2105 |
|
|
$this->fieldClean($name);
|
2106 |
|
|
|
2107 |
|
|
$sql = "ALTER TABLE \"{$table}\" ADD ";
|
2108 |
|
|
if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
|
2109 |
|
|
$sql .= "FOREIGN KEY (\"" . join('","', $sfields) . "\") ";
|
2110 |
|
|
$sql .= "REFERENCES ";
|
2111 |
|
|
// Target table needs to be fully qualified
|
2112 |
|
|
if ($this->hasSchemas()) {
|
2113 |
|
|
$sql .= "\"{$targschema}\".";
|
2114 |
|
|
}
|
2115 |
|
|
$sql .= "\"{$targtable}\"(\"" . join('","', $tfields) . "\") ";
|
2116 |
|
|
if ($match != $this->fkmatches[0]) $sql .= " {$match}";
|
2117 |
|
|
if ($upd_action != $this->fkactions[0]) $sql .= " ON UPDATE {$upd_action}";
|
2118 |
|
|
if ($del_action != $this->fkactions[0]) $sql .= " ON DELETE {$del_action}";
|
2119 |
|
|
if ($deferrable != $this->fkdeferrable[0]) $sql .= " {$deferrable}";
|
2120 |
|
|
if ($initially != $this->fkinitial[0]) $sql .= " {$initially}";
|
2121 |
|
|
|
2122 |
|
|
return $this->execute($sql);
|
2123 |
|
|
}
|
2124 |
|
|
|
2125 |
|
|
/**
|
2126 |
|
|
* Adds a primary key constraint to a table
|
2127 |
|
|
* @param $table The table to which to add the primery key
|
2128 |
|
|
* @param $fields (array) An array of fields over which to add the primary key
|
2129 |
|
|
* @param $name (optional) The name to give the key, otherwise default name is assigned
|
2130 |
|
|
* @return 0 success
|
2131 |
|
|
*/
|
2132 |
|
|
function addPrimaryKey($table, $fields, $name = '') {
|
2133 |
|
|
// This function can be faked with a unique index and a catalog twiddle, however
|
2134 |
|
|
// how do we ensure that it's only used on NOT NULL fields?
|
2135 |
|
|
return -99; // Not supported.
|
2136 |
|
|
}
|
2137 |
|
|
|
2138 |
|
|
/**
|
2139 |
|
|
* Finds the foreign keys that refer to the specified table
|
2140 |
|
|
* @param $table The table to find referrers for
|
2141 |
|
|
* @return A recordset
|
2142 |
|
|
*/
|
2143 |
|
|
function getReferrers($table) {
|
2144 |
|
|
// In PostgreSQL < 7.3, there is no way to discover foreign keys
|
2145 |
|
|
return -99;
|
2146 |
|
|
}
|
2147 |
|
|
|
2148 |
|
|
// Index functions
|
2149 |
|
|
|
2150 |
|
|
/**
|
2151 |
|
|
* Grabs a list of indexes for a table
|
2152 |
|
|
* @param $table The name of a table whose indexes to retrieve
|
2153 |
|
|
* @param $unique Only get unique/pk indexes
|
2154 |
|
|
* @return A recordset
|
2155 |
|
|
*/
|
2156 |
|
|
function getIndexes($table = '', $unique = false) {
|
2157 |
|
|
$this->clean($table);
|
2158 |
|
|
$sql = "SELECT c2.relname AS indname, i.indisprimary, i.indisunique, pg_get_indexdef(i.indexrelid) AS inddef
|
2159 |
|
|
FROM pg_class c, pg_class c2, pg_index i
|
2160 |
|
|
WHERE c.relname = '{$table}' AND c.oid = i.indrelid AND i.indexrelid = c2.oid
|
2161 |
|
|
";
|
2162 |
|
|
if ($unique) $sql .= " AND i.indisunique ";
|
2163 |
|
|
$sql .= " ORDER BY c2.relname";
|
2164 |
|
|
|
2165 |
|
|
return $this->selectSet($sql);
|
2166 |
|
|
}
|
2167 |
|
|
|
2168 |
|
|
/**
|
2169 |
|
|
* Creates an index
|
2170 |
|
|
* @param $name The index name
|
2171 |
|
|
* @param $table The table on which to add the index
|
2172 |
|
|
* @param $columns An array of columns that form the index
|
2173 |
|
|
* or a string expression for a functional index
|
2174 |
|
|
* @param $type The index type
|
2175 |
|
|
* @param $unique True if unique, false otherwise
|
2176 |
|
|
* @param $where Index predicate ('' for none)
|
2177 |
|
|
* @param $tablespace The tablespaces ('' means none/default)
|
2178 |
|
|
* @return 0 success
|
2179 |
|
|
*/
|
2180 |
|
|
function createIndex($name, $table, $columns, $type, $unique, $where, $tablespace) {
|
2181 |
|
|
$this->fieldClean($name);
|
2182 |
|
|
$this->fieldClean($table);
|
2183 |
|
|
|
2184 |
|
|
$sql = "CREATE";
|
2185 |
|
|
if ($unique) $sql .= " UNIQUE";
|
2186 |
|
|
$sql .= " INDEX \"{$name}\" ON \"{$table}\" USING {$type} ";
|
2187 |
|
|
|
2188 |
|
|
if (is_array($columns)) {
|
2189 |
|
|
$this->arrayClean($columns);
|
2190 |
|
|
$sql .= "(\"" . implode('","', $columns) . "\")";
|
2191 |
|
|
} else {
|
2192 |
|
|
$sql .= "(" . $columns .")";
|
2193 |
|
|
}
|
2194 |
|
|
|
2195 |
|
|
// Tablespace
|
2196 |
|
|
if ($this->hasTablespaces() && $tablespace != '') {
|
2197 |
|
|
$this->fieldClean($tablespace);
|
2198 |
|
|
$sql .= " TABLESPACE \"{$tablespace}\"";
|
2199 |
|
|
}
|
2200 |
|
|
|
2201 |
|
|
// Predicate
|
2202 |
|
|
if ($this->hasPartialIndexes() && trim($where) != '') {
|
2203 |
|
|
$sql .= " WHERE ({$where})";
|
2204 |
|
|
}
|
2205 |
|
|
|
2206 |
|
|
return $this->execute($sql);
|
2207 |
|
|
}
|
2208 |
|
|
|
2209 |
|
|
/**
|
2210 |
|
|
* Removes an index from the database
|
2211 |
|
|
* @param $index The index to drop
|
2212 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
2213 |
|
|
* @return 0 success
|
2214 |
|
|
*/
|
2215 |
|
|
function dropIndex($index, $cascade) {
|
2216 |
|
|
$this->fieldClean($index);
|
2217 |
|
|
|
2218 |
|
|
$sql = "DROP INDEX \"{$index}\"";
|
2219 |
|
|
if ($cascade) $sql .= " CASCADE";
|
2220 |
|
|
|
2221 |
|
|
return $this->execute($sql);
|
2222 |
|
|
}
|
2223 |
|
|
|
2224 |
|
|
/**
|
2225 |
|
|
* Clusters an index
|
2226 |
|
|
* @param $index The name of the index
|
2227 |
|
|
* @param $table The table the index is on
|
2228 |
|
|
* @return 0 success
|
2229 |
|
|
*/
|
2230 |
|
|
function clusterIndex($index, $table) {
|
2231 |
|
|
$this->fieldClean($index);
|
2232 |
|
|
$this->fieldClean($table);
|
2233 |
|
|
|
2234 |
|
|
// We don't bother with a transaction here, as there's no point rolling
|
2235 |
|
|
// back an expensive cluster if a cheap analyze fails for whatever reason
|
2236 |
|
|
$sql = "CLUSTER \"{$index}\" ON \"{$table}\"";
|
2237 |
|
|
|
2238 |
|
|
return $this->execute($sql);
|
2239 |
|
|
}
|
2240 |
|
|
|
2241 |
|
|
// Rule functions
|
2242 |
|
|
|
2243 |
|
|
/**
|
2244 |
|
|
* Returns a list of all rules on a table
|
2245 |
|
|
* @param $table The table to find rules for
|
2246 |
|
|
* @return A recordset
|
2247 |
|
|
*/
|
2248 |
|
|
function getRules($table) {
|
2249 |
|
|
$this->clean($table);
|
2250 |
|
|
|
2251 |
|
|
$sql = "SELECT
|
2252 |
|
|
*
|
2253 |
|
|
FROM
|
2254 |
|
|
pg_rules
|
2255 |
|
|
WHERE
|
2256 |
|
|
tablename='{$table}'
|
2257 |
|
|
ORDER BY
|
2258 |
|
|
rulename
|
2259 |
|
|
";
|
2260 |
|
|
|
2261 |
|
|
return $this->selectSet($sql);
|
2262 |
|
|
}
|
2263 |
|
|
|
2264 |
|
|
/**
|
2265 |
|
|
* Removes a rule from a relation
|
2266 |
|
|
* @param $rule The rule to drop
|
2267 |
|
|
* @param $relation The relation from which to drop (unused)
|
2268 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
2269 |
|
|
* @return 0 success
|
2270 |
|
|
*/
|
2271 |
|
|
function dropRule($rule, $relation, $cascade) {
|
2272 |
|
|
$this->fieldClean($rule);
|
2273 |
|
|
|
2274 |
|
|
$sql = "DROP RULE \"{$rule}\"";
|
2275 |
|
|
if ($cascade) $sql .= " CASCADE";
|
2276 |
|
|
|
2277 |
|
|
return $this->execute($sql);
|
2278 |
|
|
}
|
2279 |
|
|
|
2280 |
|
|
/**
|
2281 |
|
|
* Creates a rule
|
2282 |
|
|
* @param $name The name of the new rule
|
2283 |
|
|
* @param $event SELECT, INSERT, UPDATE or DELETE
|
2284 |
|
|
* @param $table Table on which to create the rule
|
2285 |
|
|
* @param $where When to execute the rule, '' indicates always
|
2286 |
|
|
* @param $instead True if an INSTEAD rule, false otherwise
|
2287 |
|
|
* @param $type NOTHING for a do nothing rule, SOMETHING to use given action
|
2288 |
|
|
* @param $action The action to take
|
2289 |
|
|
* @param $replace (optional) True to replace existing rule, false otherwise
|
2290 |
|
|
* @return 0 success
|
2291 |
|
|
* @return -1 invalid event
|
2292 |
|
|
*/
|
2293 |
|
|
function createRule($name, $event, $table, $where, $instead, $type, $action, $replace = false) {
|
2294 |
|
|
$this->fieldClean($name);
|
2295 |
|
|
$this->fieldClean($table);
|
2296 |
|
|
if (!in_array($event, $this->rule_events)) return -1;
|
2297 |
|
|
|
2298 |
|
|
$sql = "CREATE";
|
2299 |
|
|
if ($replace) $sql .= " OR REPLACE";
|
2300 |
|
|
$sql .= " RULE \"{$name}\" AS ON {$event} TO \"{$table}\"";
|
2301 |
|
|
// Can't escape WHERE clause
|
2302 |
|
|
if ($where != '') $sql .= " WHERE {$where}";
|
2303 |
|
|
$sql .= " DO";
|
2304 |
|
|
if ($instead) $sql .= " INSTEAD";
|
2305 |
|
|
if ($type == 'NOTHING')
|
2306 |
|
|
$sql .= " NOTHING";
|
2307 |
|
|
else $sql .= " ({$action})";
|
2308 |
|
|
|
2309 |
|
|
return $this->execute($sql);
|
2310 |
|
|
}
|
2311 |
|
|
|
2312 |
|
|
/**
|
2313 |
|
|
* Edits a rule
|
2314 |
|
|
* @param $name The name of the new rule
|
2315 |
|
|
* @param $event SELECT, INSERT, UPDATE or DELETE
|
2316 |
|
|
* @param $table Table on which to create the rule
|
2317 |
|
|
* @param $where When to execute the rule, '' indicates always
|
2318 |
|
|
* @param $instead True if an INSTEAD rule, false otherwise
|
2319 |
|
|
* @param $type NOTHING for a do nothing rule, SOMETHING to use given action
|
2320 |
|
|
* @param $action The action to take
|
2321 |
|
|
* @return 0 success
|
2322 |
|
|
* @return -1 invalid event
|
2323 |
|
|
* @return -2 transaction error
|
2324 |
|
|
* @return -3 drop existing rule error
|
2325 |
|
|
* @return -4 create new rule error
|
2326 |
|
|
*/
|
2327 |
|
|
function setRule($name, $event, $table, $where, $instead, $type, $action) {
|
2328 |
|
|
$status = $this->beginTransaction();
|
2329 |
|
|
if ($status != 0) return -2;
|
2330 |
|
|
|
2331 |
|
|
$status = $this->dropRule($name, $table);
|
2332 |
|
|
if ($status != 0) {
|
2333 |
|
|
$this->rollbackTransaction();
|
2334 |
|
|
return -3;
|
2335 |
|
|
}
|
2336 |
|
|
|
2337 |
|
|
$status = $this->createRule($name, $event, $table, $where, $instead, $type, $action);
|
2338 |
|
|
if ($status != 0) {
|
2339 |
|
|
$this->rollbackTransaction();
|
2340 |
|
|
return -4;
|
2341 |
|
|
}
|
2342 |
|
|
|
2343 |
|
|
$status = $this->endTransaction();
|
2344 |
|
|
return ($status == 0) ? 0 : -2;
|
2345 |
|
|
}
|
2346 |
|
|
|
2347 |
|
|
// View functions
|
2348 |
|
|
|
2349 |
|
|
/**
|
2350 |
|
|
* Returns a list of all views in the database
|
2351 |
|
|
* @return All views
|
2352 |
|
|
*/
|
2353 |
|
|
function getViews() {
|
2354 |
|
|
global $conf;
|
2355 |
|
|
|
2356 |
|
|
if (!$conf['show_system'])
|
2357 |
|
|
$where = " WHERE viewname NOT LIKE 'pg\\\\_%'";
|
2358 |
|
|
else
|
2359 |
|
|
$where = '';
|
2360 |
|
|
|
2361 |
|
|
$sql = "SELECT viewname AS relname, viewowner AS relowner, definition AS vwdefinition,
|
2362 |
|
|
(SELECT description FROM pg_description pd, pg_class pc
|
2363 |
|
|
WHERE pc.oid=pd.objoid AND pc.relname=v.viewname) AS relcomment
|
2364 |
|
|
FROM pg_views v
|
2365 |
|
|
{$where}
|
2366 |
|
|
ORDER BY relname";
|
2367 |
|
|
|
2368 |
|
|
return $this->selectSet($sql);
|
2369 |
|
|
}
|
2370 |
|
|
|
2371 |
|
|
/**
|
2372 |
|
|
* Returns all details for a particular view
|
2373 |
|
|
* @param $view The name of the view to retrieve
|
2374 |
|
|
* @return View info
|
2375 |
|
|
*/
|
2376 |
|
|
function getView($view) {
|
2377 |
|
|
$this->clean($view);
|
2378 |
|
|
|
2379 |
|
|
$sql = "SELECT viewname AS relname, viewowner AS relowner, definition AS vwdefinition,
|
2380 |
|
|
(SELECT description FROM pg_description pd, pg_class pc
|
2381 |
|
|
WHERE pc.oid=pd.objoid AND pc.relname=v.viewname) AS relcomment
|
2382 |
|
|
FROM pg_views v
|
2383 |
|
|
WHERE viewname='{$view}'";
|
2384 |
|
|
|
2385 |
|
|
return $this->selectSet($sql);
|
2386 |
|
|
}
|
2387 |
|
|
|
2388 |
|
|
/**
|
2389 |
|
|
* Creates a new view.
|
2390 |
|
|
* @param $viewname The name of the view to create
|
2391 |
|
|
* @param $definition The definition for the new view
|
2392 |
|
|
* @param $replace True to replace the view, false otherwise
|
2393 |
|
|
* @return 0 success
|
2394 |
|
|
*/
|
2395 |
|
|
function createView($viewname, $definition, $replace, $comment) {
|
2396 |
|
|
$status = $this->beginTransaction();
|
2397 |
|
|
if ($status != 0) return -1;
|
2398 |
|
|
|
2399 |
|
|
$this->fieldClean($viewname);
|
2400 |
|
|
$this->clean($comment);
|
2401 |
|
|
|
2402 |
|
|
// Note: $definition not cleaned
|
2403 |
|
|
|
2404 |
|
|
$sql = "CREATE ";
|
2405 |
|
|
if ($replace) $sql .= "OR REPLACE ";
|
2406 |
|
|
$sql .= "VIEW \"{$viewname}\" AS {$definition}";
|
2407 |
|
|
|
2408 |
|
|
$status = $this->execute($sql);
|
2409 |
|
|
if ($status) {
|
2410 |
|
|
$this->rollbackTransaction();
|
2411 |
|
|
return -1;
|
2412 |
|
|
}
|
2413 |
|
|
|
2414 |
|
|
if ($comment != '') {
|
2415 |
|
|
$status = $this->setComment('VIEW', $viewname, '', $comment);
|
2416 |
|
|
if ($status) {
|
2417 |
|
|
$this->rollbackTransaction();
|
2418 |
|
|
return -1;
|
2419 |
|
|
}
|
2420 |
|
|
}
|
2421 |
|
|
|
2422 |
|
|
return $this->endTransaction();
|
2423 |
|
|
}
|
2424 |
|
|
|
2425 |
|
|
/**
|
2426 |
|
|
* Drops a view.
|
2427 |
|
|
* @param $viewname The name of the view to drop
|
2428 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
2429 |
|
|
* @return 0 success
|
2430 |
|
|
*/
|
2431 |
|
|
function dropView($viewname, $cascade) {
|
2432 |
|
|
$this->fieldClean($viewname);
|
2433 |
|
|
|
2434 |
|
|
$sql = "DROP VIEW \"{$viewname}\"";
|
2435 |
|
|
if ($cascade) $sql .= " CASCADE";
|
2436 |
|
|
|
2437 |
|
|
return $this->execute($sql);
|
2438 |
|
|
}
|
2439 |
|
|
|
2440 |
|
|
/**
|
2441 |
|
|
* Updates a view. Postgres 7.1 and below don't have CREATE OR REPLACE view,
|
2442 |
|
|
* so we do it with a drop and a recreate.
|
2443 |
|
|
* @param $viewname The name fo the view to update
|
2444 |
|
|
* @param $definition The new definition for the view
|
2445 |
|
|
* @return 0 success
|
2446 |
|
|
* @return -1 transaction error
|
2447 |
|
|
* @return -2 drop view error
|
2448 |
|
|
* @return -3 create view error
|
2449 |
|
|
* @return -4 comment error
|
2450 |
|
|
*/
|
2451 |
|
|
function setView($viewname, $definition, $comment) {
|
2452 |
|
|
$status = $this->beginTransaction();
|
2453 |
|
|
if ($status != 0) return -1;
|
2454 |
|
|
|
2455 |
|
|
$status = $this->dropView($viewname, false);
|
2456 |
|
|
if ($status != 0) {
|
2457 |
|
|
$this->rollbackTransaction();
|
2458 |
|
|
return -2;
|
2459 |
|
|
}
|
2460 |
|
|
|
2461 |
|
|
$status = $this->createView($viewname, $definition, false, $comment);
|
2462 |
|
|
if ($status != 0) {
|
2463 |
|
|
$this->rollbackTransaction();
|
2464 |
|
|
return -3;
|
2465 |
|
|
}
|
2466 |
|
|
|
2467 |
|
|
$status = $this->endTransaction();
|
2468 |
|
|
return ($status == 0) ? 0 : -1;
|
2469 |
|
|
}
|
2470 |
|
|
|
2471 |
|
|
// Operator functions
|
2472 |
|
|
|
2473 |
|
|
/**
|
2474 |
|
|
* Returns a list of all operators in the database
|
2475 |
|
|
* @return All operators
|
2476 |
|
|
*/
|
2477 |
|
|
function getOperators() {
|
2478 |
|
|
global $conf;
|
2479 |
|
|
if (!$conf['show_system'])
|
2480 |
|
|
$where = "WHERE po.oid > '{$this->_lastSystemOID}'::oid";
|
2481 |
|
|
else $where = '';
|
2482 |
|
|
|
2483 |
|
|
$sql = "
|
2484 |
|
|
SELECT
|
2485 |
|
|
po.oid,
|
2486 |
|
|
po.oprname,
|
2487 |
|
|
(SELECT typname FROM pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
|
2488 |
|
|
(SELECT typname FROM pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
|
2489 |
|
|
(SELECT typname FROM pg_type pt WHERE pt.oid=po.oprresult) AS resultname,
|
2490 |
|
|
(SELECT description FROM pg_description pd WHERE po.oid=pd.objoid) AS oprcomment
|
2491 |
|
|
FROM
|
2492 |
|
|
pg_operator po
|
2493 |
|
|
{$where}
|
2494 |
|
|
ORDER BY
|
2495 |
|
|
po.oprname, oprleftname, oprrightname
|
2496 |
|
|
";
|
2497 |
|
|
|
2498 |
|
|
return $this->selectSet($sql);
|
2499 |
|
|
}
|
2500 |
|
|
|
2501 |
|
|
/**
|
2502 |
|
|
* Returns all details for a particular operator
|
2503 |
|
|
* @param $operator_oid The oid of the operator
|
2504 |
|
|
* @return Function info
|
2505 |
|
|
*/
|
2506 |
|
|
function getOperator($operator_oid) {
|
2507 |
|
|
$this->clean($operator_oid);
|
2508 |
|
|
|
2509 |
|
|
$sql = "
|
2510 |
|
|
SELECT
|
2511 |
|
|
po.oid,
|
2512 |
|
|
po.oprname,
|
2513 |
|
|
(SELECT typname FROM pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
|
2514 |
|
|
(SELECT typname FROM pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
|
2515 |
|
|
(SELECT typname FROM pg_type pt WHERE pt.oid=po.oprresult) AS resultname,
|
2516 |
|
|
po.oprcanhash,
|
2517 |
|
|
(SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprcom) AS oprcom,
|
2518 |
|
|
(SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprnegate) AS oprnegate,
|
2519 |
|
|
(SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprlsortop) AS oprlsortop,
|
2520 |
|
|
(SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprltcmpop) AS oprltcmpop,
|
2521 |
|
|
(SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprgtcmpop) AS oprgtcmpop,
|
2522 |
|
|
po.oprcode::regproc AS oprcode,
|
2523 |
|
|
--(SELECT proname FROM pg_proc pp WHERE pp.oid=po.oprcode) AS oprcode,
|
2524 |
|
|
(SELECT proname FROM pg_proc pp WHERE pp.oid=po.oprrest) AS oprrest,
|
2525 |
|
|
(SELECT proname FROM pg_proc pp WHERE pp.oid=po.oprjoin) AS oprjoin
|
2526 |
|
|
FROM
|
2527 |
|
|
pg_operator po
|
2528 |
|
|
WHERE
|
2529 |
|
|
po.oid='{$operator_oid}'
|
2530 |
|
|
";
|
2531 |
|
|
|
2532 |
|
|
return $this->selectSet($sql);
|
2533 |
|
|
}
|
2534 |
|
|
|
2535 |
|
|
/**
|
2536 |
|
|
* Drops an operator
|
2537 |
|
|
* @param $operator_oid The OID of the operator to drop
|
2538 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
2539 |
|
|
* @return 0 success
|
2540 |
|
|
*/
|
2541 |
|
|
function dropOperator($operator_oid, $cascade) {
|
2542 |
|
|
// Function comes in with $object as operator OID
|
2543 |
|
|
$opr = $this->getOperator($operator_oid);
|
2544 |
|
|
$this->fieldClean($opr->f['oprname']);
|
2545 |
|
|
|
2546 |
|
|
$sql = "DROP OPERATOR {$opr->f['oprname']} (";
|
2547 |
|
|
// Quoting or formatting here???
|
2548 |
|
|
if ($opr->f['oprleftname'] !== null) $sql .= $opr->f['oprleftname'] . ', ';
|
2549 |
|
|
else $sql .= "NONE, ";
|
2550 |
|
|
if ($opr->f['oprrightname'] !== null) $sql .= $opr->f['oprrightname'] . ')';
|
2551 |
|
|
else $sql .= "NONE)";
|
2552 |
|
|
|
2553 |
|
|
if ($cascade) $sql .= " CASCADE";
|
2554 |
|
|
|
2555 |
|
|
return $this->execute($sql);
|
2556 |
|
|
}
|
2557 |
|
|
|
2558 |
|
|
// User functions
|
2559 |
|
|
|
2560 |
|
|
/**
|
2561 |
|
|
* Changes a user's password
|
2562 |
|
|
* @param $username The username
|
2563 |
|
|
* @param $password The new password
|
2564 |
|
|
* @return 0 success
|
2565 |
|
|
*/
|
2566 |
|
|
function changePassword($username, $password) {
|
2567 |
|
|
$this->fieldClean($username);
|
2568 |
|
|
$this->clean($password);
|
2569 |
|
|
|
2570 |
|
|
$sql = "ALTER USER \"{$username}\" WITH PASSWORD '{$password}'";
|
2571 |
|
|
|
2572 |
|
|
return $this->execute($sql);
|
2573 |
|
|
}
|
2574 |
|
|
|
2575 |
|
|
/**
|
2576 |
|
|
* Returns all users in the database cluster
|
2577 |
|
|
* @return All users
|
2578 |
|
|
*/
|
2579 |
|
|
function getUsers() {
|
2580 |
|
|
$sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires";
|
2581 |
|
|
if ($this->hasUserSessionDefaults()) $sql .= ", useconfig";
|
2582 |
|
|
$sql .= " FROM pg_user ORDER BY usename";
|
2583 |
|
|
|
2584 |
|
|
return $this->selectSet($sql);
|
2585 |
|
|
}
|
2586 |
|
|
|
2587 |
|
|
/**
|
2588 |
|
|
* Returns information about a single user
|
2589 |
|
|
* @param $username The username of the user to retrieve
|
2590 |
|
|
* @return The user's data
|
2591 |
|
|
*/
|
2592 |
|
|
function getUser($username) {
|
2593 |
|
|
$this->clean($username);
|
2594 |
|
|
|
2595 |
|
|
$sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires";
|
2596 |
|
|
if ($this->hasUserSessionDefaults()) $sql .= ", useconfig";
|
2597 |
|
|
$sql .= " FROM pg_user WHERE usename='{$username}'";
|
2598 |
|
|
|
2599 |
|
|
return $this->selectSet($sql);
|
2600 |
|
|
}
|
2601 |
|
|
|
2602 |
|
|
/**
|
2603 |
|
|
* Determines whether or not a user is a super user
|
2604 |
|
|
* @param $username The username of the user
|
2605 |
|
|
* @return True if is a super user, false otherwise
|
2606 |
|
|
*/
|
2607 |
|
|
function isSuperUser($username) {
|
2608 |
|
|
$this->clean($username);
|
2609 |
|
|
|
2610 |
|
|
if (function_exists('pg_parameter_status')) {
|
2611 |
|
|
$val = pg_parameter_status($this->conn->_connectionID, 'is_superuser');
|
2612 |
|
|
if ($val !== false) return $val == 'on';
|
2613 |
|
|
}
|
2614 |
|
|
|
2615 |
|
|
$sql = "SELECT usesuper FROM pg_user WHERE usename='{$username}'";
|
2616 |
|
|
|
2617 |
|
|
$usesuper = $this->selectField($sql, 'usesuper');
|
2618 |
|
|
if ($usesuper == -1) return false;
|
2619 |
|
|
else return $usesuper == 't';
|
2620 |
|
|
}
|
2621 |
|
|
|
2622 |
|
|
/**
|
2623 |
|
|
* Creates a new user
|
2624 |
|
|
* @param $username The username of the user to create
|
2625 |
|
|
* @param $password A password for the user
|
2626 |
|
|
* @param $createdb boolean Whether or not the user can create databases
|
2627 |
|
|
* @param $createuser boolean Whether or not the user can create other users
|
2628 |
|
|
* @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
|
2629 |
|
|
* @param $group (array) The groups to create the user in
|
2630 |
|
|
* @return 0 success
|
2631 |
|
|
*/
|
2632 |
|
|
function createUser($username, $password, $createdb, $createuser, $expiry, $groups) {
|
2633 |
|
|
$this->fieldClean($username);
|
2634 |
|
|
$this->clean($password);
|
2635 |
|
|
$this->clean($expiry);
|
2636 |
|
|
$this->fieldArrayClean($groups);
|
2637 |
|
|
|
2638 |
|
|
$sql = "CREATE USER \"{$username}\"";
|
2639 |
|
|
if ($password != '') $sql .= " WITH PASSWORD '{$password}'";
|
2640 |
|
|
$sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
|
2641 |
|
|
$sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';
|
2642 |
|
|
if (is_array($groups) && sizeof($groups) > 0) $sql .= " IN GROUP \"" . join('", "', $groups) . "\"";
|
2643 |
|
|
if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";
|
2644 |
|
|
else $sql .= " VALID UNTIL 'infinity'";
|
2645 |
|
|
|
2646 |
|
|
return $this->execute($sql);
|
2647 |
|
|
}
|
2648 |
|
|
|
2649 |
|
|
/**
|
2650 |
|
|
* Adjusts a user's info
|
2651 |
|
|
* @param $username The username of the user to modify
|
2652 |
|
|
* @param $password A new password for the user
|
2653 |
|
|
* @param $createdb boolean Whether or not the user can create databases
|
2654 |
|
|
* @param $createuser boolean Whether or not the user can create other users
|
2655 |
|
|
* @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire.
|
2656 |
|
|
* @return 0 success
|
2657 |
|
|
*/
|
2658 |
|
|
function setUser($username, $password, $createdb, $createuser, $expiry) {
|
2659 |
|
|
$this->fieldClean($username);
|
2660 |
|
|
$this->clean($password);
|
2661 |
|
|
$this->clean($expiry);
|
2662 |
|
|
|
2663 |
|
|
$sql = "ALTER USER \"{$username}\"";
|
2664 |
|
|
if ($password != '') $sql .= " WITH PASSWORD '{$password}'";
|
2665 |
|
|
$sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
|
2666 |
|
|
$sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';
|
2667 |
|
|
if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";
|
2668 |
|
|
else $sql .= " VALID UNTIL 'infinity'";
|
2669 |
|
|
|
2670 |
|
|
return $this->execute($sql);
|
2671 |
|
|
}
|
2672 |
|
|
|
2673 |
|
|
/**
|
2674 |
|
|
* Removes a user
|
2675 |
|
|
* @param $username The username of the user to drop
|
2676 |
|
|
* @return 0 success
|
2677 |
|
|
*/
|
2678 |
|
|
function dropUser($username) {
|
2679 |
|
|
$this->fieldClean($username);
|
2680 |
|
|
|
2681 |
|
|
$sql = "DROP USER \"{$username}\"";
|
2682 |
|
|
|
2683 |
|
|
return $this->execute($sql);
|
2684 |
|
|
}
|
2685 |
|
|
|
2686 |
|
|
// Group functions
|
2687 |
|
|
|
2688 |
|
|
/**
|
2689 |
|
|
* Returns all groups in the database cluser
|
2690 |
|
|
* @return All groups
|
2691 |
|
|
*/
|
2692 |
|
|
function getGroups() {
|
2693 |
|
|
$sql = "SELECT groname FROM pg_group ORDER BY groname";
|
2694 |
|
|
|
2695 |
|
|
return $this->selectSet($sql);
|
2696 |
|
|
}
|
2697 |
|
|
|
2698 |
|
|
/**
|
2699 |
|
|
* Returns users in a specific group
|
2700 |
|
|
* @param $groname The name of the group
|
2701 |
|
|
* @return All users in the group
|
2702 |
|
|
*/
|
2703 |
|
|
function getGroup($groname) {
|
2704 |
|
|
$this->clean($groname);
|
2705 |
|
|
|
2706 |
|
|
$sql = "SELECT grolist FROM pg_group WHERE groname = '{$groname}'";
|
2707 |
|
|
|
2708 |
|
|
$grodata = $this->selectSet($sql);
|
2709 |
|
|
if ($grodata->f['grolist'] !== null && $grodata->f['grolist'] != '{}') {
|
2710 |
|
|
$members = $grodata->f['grolist'];
|
2711 |
|
|
$members = ereg_replace("\{|\}","",$members);
|
2712 |
|
|
$this->clean($members);
|
2713 |
|
|
|
2714 |
|
|
$sql = "SELECT usename FROM pg_user WHERE usesysid IN ({$members}) ORDER BY usename";
|
2715 |
|
|
}
|
2716 |
|
|
else $sql = "SELECT usename FROM pg_user WHERE false";
|
2717 |
|
|
|
2718 |
|
|
return $this->selectSet($sql);
|
2719 |
|
|
}
|
2720 |
|
|
|
2721 |
|
|
/**
|
2722 |
|
|
* Creates a new group
|
2723 |
|
|
* @param $groname The name of the group
|
2724 |
|
|
* @param $users An array of users to add to the group
|
2725 |
|
|
* @return 0 success
|
2726 |
|
|
*/
|
2727 |
|
|
function createGroup($groname, $users) {
|
2728 |
|
|
$this->fieldClean($groname);
|
2729 |
|
|
|
2730 |
|
|
$sql = "CREATE GROUP \"{$groname}\"";
|
2731 |
|
|
|
2732 |
|
|
if (is_array($users) && sizeof($users) > 0) {
|
2733 |
|
|
$this->fieldArrayClean($users);
|
2734 |
|
|
$sql .= ' WITH USER "' . join('", "', $users) . '"';
|
2735 |
|
|
}
|
2736 |
|
|
|
2737 |
|
|
return $this->execute($sql);
|
2738 |
|
|
}
|
2739 |
|
|
|
2740 |
|
|
/**
|
2741 |
|
|
* Removes a group
|
2742 |
|
|
* @param $groname The name of the group to drop
|
2743 |
|
|
* @return 0 success
|
2744 |
|
|
*/
|
2745 |
|
|
function dropGroup($groname) {
|
2746 |
|
|
$this->fieldClean($groname);
|
2747 |
|
|
|
2748 |
|
|
$sql = "DROP GROUP \"{$groname}\"";
|
2749 |
|
|
|
2750 |
|
|
return $this->execute($sql);
|
2751 |
|
|
}
|
2752 |
|
|
|
2753 |
|
|
/**
|
2754 |
|
|
* Adds a group member
|
2755 |
|
|
* @param $groname The name of the group
|
2756 |
|
|
* @param $user The name of the user to add to the group
|
2757 |
|
|
* @return 0 success
|
2758 |
|
|
*/
|
2759 |
|
|
function addGroupMember($groname, $user) {
|
2760 |
|
|
$this->fieldClean($groname);
|
2761 |
|
|
$this->fieldClean($user);
|
2762 |
|
|
|
2763 |
|
|
$sql = "ALTER GROUP \"{$groname}\" ADD USER \"{$user}\"";
|
2764 |
|
|
|
2765 |
|
|
return $this->execute($sql);
|
2766 |
|
|
}
|
2767 |
|
|
|
2768 |
|
|
/**
|
2769 |
|
|
* Removes a group member
|
2770 |
|
|
* @param $groname The name of the group
|
2771 |
|
|
* @param $user The name of the user to remove from the group
|
2772 |
|
|
* @return 0 success
|
2773 |
|
|
*/
|
2774 |
|
|
function dropGroupMember($groname, $user) {
|
2775 |
|
|
$this->fieldClean($groname);
|
2776 |
|
|
$this->fieldClean($user);
|
2777 |
|
|
|
2778 |
|
|
$sql = "ALTER GROUP \"{$groname}\" DROP USER \"{$user}\"";
|
2779 |
|
|
|
2780 |
|
|
return $this->execute($sql);
|
2781 |
|
|
}
|
2782 |
|
|
|
2783 |
|
|
// Type functions
|
2784 |
|
|
|
2785 |
|
|
/**
|
2786 |
|
|
* Returns a list of all types in the database
|
2787 |
|
|
* @param $all If true, will find all available functions, if false just those in search path
|
2788 |
|
|
* @param $tabletypes If true, will include table types
|
2789 |
|
|
* @param $domains Ignored
|
2790 |
|
|
* @return A recordet
|
2791 |
|
|
*/
|
2792 |
|
|
function getTypes($all = false, $tabletypes = false, $domains = false) {
|
2793 |
|
|
global $conf;
|
2794 |
|
|
|
2795 |
|
|
if ($all || $conf['show_system']) {
|
2796 |
|
|
$where = '';
|
2797 |
|
|
} else {
|
2798 |
|
|
$where = "AND pt.oid > '{$this->_lastSystemOID}'::oid";
|
2799 |
|
|
}
|
2800 |
|
|
// Never show system table types
|
2801 |
|
|
$where2 = "AND c.oid > '{$this->_lastSystemOID}'::oid";
|
2802 |
|
|
|
2803 |
|
|
// Create type filter
|
2804 |
|
|
$tqry = "'c'";
|
2805 |
|
|
if ($tabletypes)
|
2806 |
|
|
$tqry .= ", 'r', 'v'";
|
2807 |
|
|
|
2808 |
|
|
$sql = "SELECT
|
2809 |
|
|
pt.typname AS basename,
|
2810 |
|
|
pt.typname,
|
2811 |
|
|
pu.usename AS typowner,
|
2812 |
|
|
(SELECT description FROM pg_description pd WHERE pt.oid=pd.objoid) AS typcomment
|
2813 |
|
|
FROM
|
2814 |
|
|
pg_type pt,
|
2815 |
|
|
pg_user pu
|
2816 |
|
|
WHERE
|
2817 |
|
|
pt.typowner = pu.usesysid
|
2818 |
|
|
AND (pt.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_class c WHERE c.oid = pt.typrelid {$where2}))
|
2819 |
|
|
AND typname !~ '^_'
|
2820 |
|
|
{$where}
|
2821 |
|
|
ORDER BY typname
|
2822 |
|
|
";
|
2823 |
|
|
|
2824 |
|
|
return $this->selectSet($sql);
|
2825 |
|
|
}
|
2826 |
|
|
|
2827 |
|
|
/**
|
2828 |
|
|
* Returns all details for a particular type
|
2829 |
|
|
* @param $typname The name of the view to retrieve
|
2830 |
|
|
* @return Type info
|
2831 |
|
|
*/
|
2832 |
|
|
function getType($typname) {
|
2833 |
|
|
$this->clean($typname);
|
2834 |
|
|
|
2835 |
|
|
$sql = "SELECT *, typinput AS typin, typoutput AS typout
|
2836 |
|
|
FROM pg_type WHERE typname='{$typname}'";
|
2837 |
|
|
|
2838 |
|
|
return $this->selectSet($sql);
|
2839 |
|
|
}
|
2840 |
|
|
|
2841 |
|
|
/**
|
2842 |
|
|
* Creates a new type
|
2843 |
|
|
* @param ...
|
2844 |
|
|
* @return 0 success
|
2845 |
|
|
*/
|
2846 |
|
|
function createType($typname, $typin, $typout, $typlen, $typdef,
|
2847 |
|
|
$typelem, $typdelim, $typbyval, $typalign, $typstorage) {
|
2848 |
|
|
$this->fieldClean($typname);
|
2849 |
|
|
$this->fieldClean($typin);
|
2850 |
|
|
$this->fieldClean($typout);
|
2851 |
|
|
|
2852 |
|
|
$sql = "
|
2853 |
|
|
CREATE TYPE \"{$typname}\" (
|
2854 |
|
|
INPUT = \"{$typin}\",
|
2855 |
|
|
OUTPUT = \"{$typout}\",
|
2856 |
|
|
INTERNALLENGTH = {$typlen}";
|
2857 |
|
|
if ($typdef != '') $sql .= ", DEFAULT = {$typdef}";
|
2858 |
|
|
if ($typelem != '') $sql .= ", ELEMENT = {$typelem}";
|
2859 |
|
|
if ($typdelim != '') $sql .= ", DELIMITER = {$typdelim}";
|
2860 |
|
|
if ($typbyval) $sql .= ", PASSEDBYVALUE, ";
|
2861 |
|
|
if ($typalign != '') $sql .= ", ALIGNMENT = {$typalign}";
|
2862 |
|
|
if ($typstorage != '') $sql .= ", STORAGE = {$typstorage}";
|
2863 |
|
|
|
2864 |
|
|
$sql .= ")";
|
2865 |
|
|
|
2866 |
|
|
return $this->execute($sql);
|
2867 |
|
|
}
|
2868 |
|
|
|
2869 |
|
|
/**
|
2870 |
|
|
* Drops a type.
|
2871 |
|
|
* @param $typname The name of the type to drop
|
2872 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
2873 |
|
|
* @return 0 success
|
2874 |
|
|
*/
|
2875 |
|
|
function dropType($typname, $cascade) {
|
2876 |
|
|
$this->fieldClean($typname);
|
2877 |
|
|
|
2878 |
|
|
$sql = "DROP TYPE \"{$typname}\"";
|
2879 |
|
|
if ($cascade) $sql .= " CASCADE";
|
2880 |
|
|
|
2881 |
|
|
return $this->execute($sql);
|
2882 |
|
|
}
|
2883 |
|
|
|
2884 |
|
|
// Trigger functions
|
2885 |
|
|
|
2886 |
|
|
/**
|
2887 |
|
|
* A helper function for getTriggers that translates
|
2888 |
|
|
* an array of attribute numbers to an array of field names.
|
2889 |
|
|
* @param $trigger An array containing fields from the trigger table
|
2890 |
|
|
* @return The trigger definition string
|
2891 |
|
|
*/
|
2892 |
|
|
function getTriggerDef($trigger) {
|
2893 |
|
|
// Constants to figure out tgtype
|
2894 |
|
|
|
2895 |
|
|
if (!defined('TRIGGER_TYPE_ROW')) define ('TRIGGER_TYPE_ROW', (1 << 0));
|
2896 |
|
|
if (!defined('TRIGGER_TYPE_BEFORE')) define ('TRIGGER_TYPE_BEFORE', (1 << 1));
|
2897 |
|
|
if (!defined('TRIGGER_TYPE_INSERT')) define ('TRIGGER_TYPE_INSERT', (1 << 2));
|
2898 |
|
|
if (!defined('TRIGGER_TYPE_DELETE')) define ('TRIGGER_TYPE_DELETE', (1 << 3));
|
2899 |
|
|
if (!defined('TRIGGER_TYPE_UPDATE')) define ('TRIGGER_TYPE_UPDATE', (1 << 4));
|
2900 |
|
|
|
2901 |
|
|
$trigger['tgisconstraint'] = $this->phpBool($trigger['tgisconstraint']);
|
2902 |
|
|
$trigger['tgdeferrable'] = $this->phpBool($trigger['tgdeferrable']);
|
2903 |
|
|
$trigger['tginitdeferred'] = $this->phpBool($trigger['tginitdeferred']);
|
2904 |
|
|
|
2905 |
|
|
// Constraint trigger or normal trigger
|
2906 |
|
|
if ($trigger['tgisconstraint'])
|
2907 |
|
|
$tgdef = 'CREATE CONSTRAINT TRIGGER ';
|
2908 |
|
|
else
|
2909 |
|
|
$tgdef = 'CREATE TRIGGER ';
|
2910 |
|
|
|
2911 |
|
|
$tgdef .= "\"{$trigger['tgname']}\" ";
|
2912 |
|
|
|
2913 |
|
|
// Trigger type
|
2914 |
|
|
$findx = 0;
|
2915 |
|
|
if (($trigger['tgtype'] & TRIGGER_TYPE_BEFORE) == TRIGGER_TYPE_BEFORE)
|
2916 |
|
|
$tgdef .= 'BEFORE';
|
2917 |
|
|
else
|
2918 |
|
|
$tgdef .= 'AFTER';
|
2919 |
|
|
|
2920 |
|
|
if (($trigger['tgtype'] & TRIGGER_TYPE_INSERT) == TRIGGER_TYPE_INSERT) {
|
2921 |
|
|
$tgdef .= ' INSERT';
|
2922 |
|
|
$findx++;
|
2923 |
|
|
}
|
2924 |
|
|
if (($trigger['tgtype'] & TRIGGER_TYPE_DELETE) == TRIGGER_TYPE_DELETE) {
|
2925 |
|
|
if ($findx > 0)
|
2926 |
|
|
$tgdef .= ' OR DELETE';
|
2927 |
|
|
else {
|
2928 |
|
|
$tgdef .= ' DELETE';
|
2929 |
|
|
$findx++;
|
2930 |
|
|
}
|
2931 |
|
|
}
|
2932 |
|
|
if (($trigger['tgtype'] & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE) {
|
2933 |
|
|
if ($findx > 0)
|
2934 |
|
|
$tgdef .= ' OR UPDATE';
|
2935 |
|
|
else
|
2936 |
|
|
$tgdef .= ' UPDATE';
|
2937 |
|
|
}
|
2938 |
|
|
|
2939 |
|
|
// Table name
|
2940 |
|
|
$tgdef .= " ON \"{$trigger['relname']}\" ";
|
2941 |
|
|
|
2942 |
|
|
// Deferrability
|
2943 |
|
|
if ($trigger['tgisconstraint']) {
|
2944 |
|
|
if ($trigger['tgconstrrelid'] != 0) {
|
2945 |
|
|
// Assume constrelname is not null
|
2946 |
|
|
$tgdef .= " FROM \"{$trigger['tgconstrrelname']}\" ";
|
2947 |
|
|
}
|
2948 |
|
|
if (!$trigger['tgdeferrable'])
|
2949 |
|
|
$tgdef .= 'NOT ';
|
2950 |
|
|
$tgdef .= 'DEFERRABLE INITIALLY ';
|
2951 |
|
|
if ($trigger['tginitdeferred'])
|
2952 |
|
|
$tgdef .= 'DEFERRED ';
|
2953 |
|
|
else
|
2954 |
|
|
$tgdef .= 'IMMEDIATE ';
|
2955 |
|
|
}
|
2956 |
|
|
|
2957 |
|
|
// Row or statement
|
2958 |
|
|
if ($trigger['tgtype'] & TRIGGER_TYPE_ROW == TRIGGER_TYPE_ROW)
|
2959 |
|
|
$tgdef .= 'FOR EACH ROW ';
|
2960 |
|
|
else
|
2961 |
|
|
$tgdef .= 'FOR EACH STATEMENT ';
|
2962 |
|
|
|
2963 |
|
|
// Execute procedure
|
2964 |
|
|
$tgdef .= "EXECUTE PROCEDURE \"{$trigger['tgfname']}\"(";
|
2965 |
|
|
|
2966 |
|
|
// Parameters
|
2967 |
|
|
// Escape null characters
|
2968 |
|
|
$v = addCSlashes($trigger['tgargs'], "\0");
|
2969 |
|
|
// Split on escaped null characters
|
2970 |
|
|
$params = explode('\\000', $v);
|
2971 |
|
|
for ($findx = 0; $findx < $trigger['tgnargs']; $findx++) {
|
2972 |
|
|
$param = "'" . str_replace('\'', '\\\'', $params[$findx]) . "'";
|
2973 |
|
|
$tgdef .= $param;
|
2974 |
|
|
if ($findx < ($trigger['tgnargs'] - 1))
|
2975 |
|
|
$tgdef .= ', ';
|
2976 |
|
|
}
|
2977 |
|
|
|
2978 |
|
|
// Finish it off
|
2979 |
|
|
$tgdef .= ')';
|
2980 |
|
|
|
2981 |
|
|
return $tgdef;
|
2982 |
|
|
}
|
2983 |
|
|
|
2984 |
|
|
/**
|
2985 |
|
|
* Grabs a list of triggers on a table
|
2986 |
|
|
* @param $table The name of a table whose triggers to retrieve
|
2987 |
|
|
* @return A recordset
|
2988 |
|
|
*/
|
2989 |
|
|
function getTriggers($table = '') {
|
2990 |
|
|
$this->clean($table);
|
2991 |
|
|
|
2992 |
|
|
// We include constraint triggers
|
2993 |
|
|
$sql = "SELECT t.tgname, t.tgisconstraint, t.tgdeferrable, t.tginitdeferred, t.tgtype,
|
2994 |
|
|
t.tgargs, t.tgnargs, t.tgconstrrelid,
|
2995 |
|
|
(SELECT relname FROM pg_class c2 WHERE c2.oid=t.tgconstrrelid) AS tgconstrrelname,
|
2996 |
|
|
(SELECT proname FROM pg_proc p WHERE t.tgfoid=p.oid) AS tgfname,
|
2997 |
|
|
c.relname, NULL AS tgdef
|
2998 |
|
|
FROM pg_trigger t, pg_class c
|
2999 |
|
|
WHERE t.tgrelid=c.oid
|
3000 |
|
|
AND c.relname='{$table}'";
|
3001 |
|
|
|
3002 |
|
|
return $this->selectSet($sql);
|
3003 |
|
|
}
|
3004 |
|
|
|
3005 |
|
|
/**
|
3006 |
|
|
* Creates a trigger
|
3007 |
|
|
* @param $tgname The name of the trigger to create
|
3008 |
|
|
* @param $table The name of the table
|
3009 |
|
|
* @param $tgproc The function to execute
|
3010 |
|
|
* @param $tgtime BEFORE or AFTER
|
3011 |
|
|
* @param $tgevent Event
|
3012 |
|
|
* @param $tgargs The function arguments
|
3013 |
|
|
* @return 0 success
|
3014 |
|
|
*/
|
3015 |
|
|
function createTrigger($tgname, $table, $tgproc, $tgtime, $tgevent, $tgfrequency, $tgargs) {
|
3016 |
|
|
$this->fieldClean($tgname);
|
3017 |
|
|
$this->fieldClean($table);
|
3018 |
|
|
$this->fieldClean($tgproc);
|
3019 |
|
|
|
3020 |
|
|
/* No Statement Level Triggers in PostgreSQL (by now) */
|
3021 |
|
|
$sql = "CREATE TRIGGER \"{$tgname}\" {$tgtime}
|
3022 |
|
|
{$tgevent} ON \"{$table}\"
|
3023 |
|
|
FOR EACH {$tgfrequency} EXECUTE PROCEDURE \"{$tgproc}\"({$tgargs})";
|
3024 |
|
|
|
3025 |
|
|
return $this->execute($sql);
|
3026 |
|
|
}
|
3027 |
|
|
|
3028 |
|
|
/**
|
3029 |
|
|
* Drops a trigger
|
3030 |
|
|
* @param $tgname The name of the trigger to drop
|
3031 |
|
|
* @param $table The table from which to drop the trigger
|
3032 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
3033 |
|
|
* @return 0 success
|
3034 |
|
|
*/
|
3035 |
|
|
function dropTrigger($tgname, $table, $cascade) {
|
3036 |
|
|
$this->fieldClean($tgname);
|
3037 |
|
|
$this->fieldClean($table);
|
3038 |
|
|
|
3039 |
|
|
$sql = "DROP TRIGGER \"{$tgname}\" ON \"{$table}\"";
|
3040 |
|
|
if ($cascade) $sql .= " CASCADE";
|
3041 |
|
|
|
3042 |
|
|
return $this->execute($sql);
|
3043 |
|
|
}
|
3044 |
|
|
|
3045 |
|
|
// Privilege functions
|
3046 |
|
|
|
3047 |
|
|
/**
|
3048 |
|
|
* Internal function used for parsing ACLs
|
3049 |
|
|
* @param $acl The ACL to parse (of type aclitem[])
|
3050 |
|
|
* @return Privileges array
|
3051 |
|
|
*/
|
3052 |
|
|
function _parseACL($acl) {
|
3053 |
|
|
// Take off the first and last characters (the braces)
|
3054 |
|
|
$acl = substr($acl, 1, strlen($acl) - 2);
|
3055 |
|
|
|
3056 |
|
|
// Pick out individual ACE's by carefully parsing. This is necessary in order
|
3057 |
|
|
// to cope with usernames and stuff that contain commas
|
3058 |
|
|
$aces = array();
|
3059 |
|
|
$i = $j = 0;
|
3060 |
|
|
$in_quotes = false;
|
3061 |
|
|
while ($i < strlen($acl)) {
|
3062 |
|
|
// If current char is a double quote and it's not escaped, then
|
3063 |
|
|
// enter quoted bit
|
3064 |
|
|
$char = substr($acl, $i, 1);
|
3065 |
|
|
if ($char == '"' && ($i == 0 || substr($acl, $i - 1, 1) != '\\'))
|
3066 |
|
|
$in_quotes = !$in_quotes;
|
3067 |
|
|
elseif ($char == ',' && !$in_quotes) {
|
3068 |
|
|
// Add text so far to the array
|
3069 |
|
|
$aces[] = substr($acl, $j, $i - $j);
|
3070 |
|
|
$j = $i + 1;
|
3071 |
|
|
}
|
3072 |
|
|
$i++;
|
3073 |
|
|
}
|
3074 |
|
|
// Add final text to the array
|
3075 |
|
|
$aces[] = substr($acl, $j);
|
3076 |
|
|
|
3077 |
|
|
// Create the array to be returned
|
3078 |
|
|
$temp = array();
|
3079 |
|
|
|
3080 |
|
|
// For each ACE, generate an entry in $temp
|
3081 |
|
|
foreach ($aces as $v) {
|
3082 |
|
|
|
3083 |
|
|
// If the ACE begins with a double quote, strip them off both ends
|
3084 |
|
|
// and unescape backslashes and double quotes
|
3085 |
|
|
$unquote = false;
|
3086 |
|
|
if (strpos($v, '"') === 0) {
|
3087 |
|
|
$v = substr($v, 1, strlen($v) - 2);
|
3088 |
|
|
$v = str_replace('\\"', '"', $v);
|
3089 |
|
|
$v = str_replace('\\\\', '\\', $v);
|
3090 |
|
|
}
|
3091 |
|
|
|
3092 |
|
|
// Figure out type of ACE (public, user or group)
|
3093 |
|
|
if (strpos($v, '=') === 0)
|
3094 |
|
|
$atype = 'public';
|
3095 |
|
|
elseif (strpos($v, 'group ') === 0) {
|
3096 |
|
|
$atype = 'group';
|
3097 |
|
|
// Tear off 'group' prefix
|
3098 |
|
|
$v = substr($v, 6);
|
3099 |
|
|
}
|
3100 |
|
|
else
|
3101 |
|
|
$atype = 'user';
|
3102 |
|
|
|
3103 |
|
|
// Break on unquoted equals sign...
|
3104 |
|
|
$i = 0;
|
3105 |
|
|
$in_quotes = false;
|
3106 |
|
|
$entity = null;
|
3107 |
|
|
$chars = null;
|
3108 |
|
|
while ($i < strlen($v)) {
|
3109 |
|
|
// If current char is a double quote and it's not escaped, then
|
3110 |
|
|
// enter quoted bit
|
3111 |
|
|
$char = substr($v, $i, 1);
|
3112 |
|
|
$next_char = substr($v, $i + 1, 1);
|
3113 |
|
|
if ($char == '"' && ($i == 0 || $next_char != '"')) {
|
3114 |
|
|
$in_quotes = !$in_quotes;
|
3115 |
|
|
}
|
3116 |
|
|
// Skip over escaped double quotes
|
3117 |
|
|
elseif ($char == '"' && $next_char == '"') {
|
3118 |
|
|
$i++;
|
3119 |
|
|
}
|
3120 |
|
|
elseif ($char == '=' && !$in_quotes) {
|
3121 |
|
|
// Split on current equals sign
|
3122 |
|
|
$entity = substr($v, 0, $i);
|
3123 |
|
|
$chars = substr($v, $i + 1);
|
3124 |
|
|
break;
|
3125 |
|
|
}
|
3126 |
|
|
$i++;
|
3127 |
|
|
}
|
3128 |
|
|
|
3129 |
|
|
// Check for quoting on entity name, and unescape if necessary
|
3130 |
|
|
if (strpos($entity, '"') === 0) {
|
3131 |
|
|
$entity = substr($entity, 1, strlen($entity) - 2);
|
3132 |
|
|
$entity = str_replace('""', '"', $entity);
|
3133 |
|
|
}
|
3134 |
|
|
|
3135 |
|
|
// New row to be added to $temp
|
3136 |
|
|
// (type, grantee, privileges, grantor, grant option?
|
3137 |
|
|
$row = array($atype, $entity, array(), '', array());
|
3138 |
|
|
|
3139 |
|
|
// Loop over chars and add privs to $row
|
3140 |
|
|
for ($i = 0; $i < strlen($chars); $i++) {
|
3141 |
|
|
// Append to row's privs list the string representing
|
3142 |
|
|
// the privilege
|
3143 |
|
|
$char = substr($chars, $i, 1);
|
3144 |
|
|
if ($char == '*')
|
3145 |
|
|
$row[4][] = $this->privmap[substr($chars, $i - 1, 1)];
|
3146 |
|
|
elseif ($char == '/') {
|
3147 |
|
|
$grantor = substr($chars, $i + 1);
|
3148 |
|
|
// Check for quoting
|
3149 |
|
|
if (strpos($grantor, '"') === 0) {
|
3150 |
|
|
$grantor = substr($grantor, 1, strlen($grantor) - 2);
|
3151 |
|
|
$grantor = str_replace('""', '"', $grantor);
|
3152 |
|
|
}
|
3153 |
|
|
$row[3] = $grantor;
|
3154 |
|
|
break;
|
3155 |
|
|
}
|
3156 |
|
|
else {
|
3157 |
|
|
if (!isset($this->privmap[$char]))
|
3158 |
|
|
return -3;
|
3159 |
|
|
else
|
3160 |
|
|
$row[2][] = $this->privmap[$char];
|
3161 |
|
|
}
|
3162 |
|
|
}
|
3163 |
|
|
|
3164 |
|
|
// Append row to temp
|
3165 |
|
|
$temp[] = $row;
|
3166 |
|
|
}
|
3167 |
|
|
|
3168 |
|
|
return $temp;
|
3169 |
|
|
}
|
3170 |
|
|
|
3171 |
|
|
/**
|
3172 |
|
|
* Grabs an array of users and their privileges for an object,
|
3173 |
|
|
* given its type.
|
3174 |
|
|
* @param $object The name of the object whose privileges are to be retrieved
|
3175 |
|
|
* @param $type The type of the object (eg. relation, view or sequence)
|
3176 |
|
|
* @return Privileges array
|
3177 |
|
|
* @return -1 invalid type
|
3178 |
|
|
* @return -2 object not found
|
3179 |
|
|
* @return -3 unknown privilege type
|
3180 |
|
|
*/
|
3181 |
|
|
function getPrivileges($object, $type) {
|
3182 |
|
|
$this->clean($object);
|
3183 |
|
|
|
3184 |
|
|
switch ($type) {
|
3185 |
|
|
case 'table':
|
3186 |
|
|
case 'view':
|
3187 |
|
|
case 'sequence':
|
3188 |
|
|
$sql = "SELECT relacl AS acl FROM pg_class WHERE relname='{$object}'";
|
3189 |
|
|
break;
|
3190 |
|
|
default:
|
3191 |
|
|
return -1;
|
3192 |
|
|
}
|
3193 |
|
|
|
3194 |
|
|
// Fetch the ACL for object
|
3195 |
|
|
$acl = $this->selectField($sql, 'acl');
|
3196 |
|
|
if ($acl == -1) return -2;
|
3197 |
|
|
elseif ($acl == '' || $acl == null) return array();
|
3198 |
|
|
else return $this->_parseACL($acl);
|
3199 |
|
|
}
|
3200 |
|
|
|
3201 |
|
|
/**
|
3202 |
|
|
* Grants a privilege to a user, group or public
|
3203 |
|
|
* @param $mode 'GRANT' or 'REVOKE';
|
3204 |
|
|
* @param $type The type of object
|
3205 |
|
|
* @param $object The name of the object
|
3206 |
|
|
* @param $public True to grant to public, false otherwise
|
3207 |
|
|
* @param $usernames The array of usernames to grant privs to.
|
3208 |
|
|
* @param $groupnames The array of group names to grant privs to.
|
3209 |
|
|
* @param $privileges The array of privileges to grant (eg. ('SELECT', 'ALL PRIVILEGES', etc.) )
|
3210 |
|
|
* @param $grantoption True if has grant option, false otherwise
|
3211 |
|
|
* @param $cascade True for cascade revoke, false otherwise
|
3212 |
|
|
* @return 0 success
|
3213 |
|
|
* @return -1 invalid type
|
3214 |
|
|
* @return -2 invalid entity
|
3215 |
|
|
* @return -3 invalid privileges
|
3216 |
|
|
* @return -4 not granting to anything
|
3217 |
|
|
* @return -4 invalid mode
|
3218 |
|
|
*/
|
3219 |
|
|
function setPrivileges($mode, $type, $object, $public, $usernames, $groupnames, $privileges, $grantoption, $cascade) {
|
3220 |
|
|
$this->fieldArrayClean($usernames);
|
3221 |
|
|
$this->fieldArrayClean($groupnames);
|
3222 |
|
|
|
3223 |
|
|
// Input checking
|
3224 |
|
|
if (!is_array($privileges) || sizeof($privileges) == 0) return -3;
|
3225 |
|
|
if (!is_array($usernames) || !is_array($groupnames) ||
|
3226 |
|
|
(!$public && sizeof($usernames) == 0 && sizeof($groupnames) == 0)) return -4;
|
3227 |
|
|
if ($mode != 'GRANT' && $mode != 'REVOKE') return -5;
|
3228 |
|
|
|
3229 |
|
|
$sql = $mode;
|
3230 |
|
|
|
3231 |
|
|
// Grant option
|
3232 |
|
|
if ($this->hasGrantOption() && $mode == 'REVOKE' && $grantoption) {
|
3233 |
|
|
$sql .= ' GRANT OPTION FOR';
|
3234 |
|
|
}
|
3235 |
|
|
|
3236 |
|
|
if (in_array('ALL PRIVILEGES', $privileges))
|
3237 |
|
|
$sql .= " ALL PRIVILEGES ON";
|
3238 |
|
|
else
|
3239 |
|
|
$sql .= " " . join(', ', $privileges) . " ON";
|
3240 |
|
|
switch ($type) {
|
3241 |
|
|
case 'table':
|
3242 |
|
|
case 'view':
|
3243 |
|
|
case 'sequence':
|
3244 |
|
|
$this->fieldClean($object);
|
3245 |
|
|
$sql .= " \"{$object}\"";
|
3246 |
|
|
break;
|
3247 |
|
|
case 'database':
|
3248 |
|
|
$this->fieldClean($object);
|
3249 |
|
|
$sql .= " DATABASE \"{$object}\"";
|
3250 |
|
|
break;
|
3251 |
|
|
case 'function':
|
3252 |
|
|
// Function comes in with $object as function OID
|
3253 |
|
|
$fn = $this->getFunction($object);
|
3254 |
|
|
$this->fieldClean($fn->f['proname']);
|
3255 |
|
|
$sql .= " FUNCTION \"{$fn->f['proname']}\"({$fn->f['proarguments']})";
|
3256 |
|
|
break;
|
3257 |
|
|
case 'language':
|
3258 |
|
|
$this->fieldClean($object);
|
3259 |
|
|
$sql .= " LANGUAGE \"{$object}\"";
|
3260 |
|
|
break;
|
3261 |
|
|
case 'schema':
|
3262 |
|
|
$this->fieldClean($object);
|
3263 |
|
|
$sql .= " SCHEMA \"{$object}\"";
|
3264 |
|
|
break;
|
3265 |
|
|
case 'tablespace':
|
3266 |
|
|
$this->fieldClean($object);
|
3267 |
|
|
$sql .= " TABLESPACE \"{$object}\"";
|
3268 |
|
|
break;
|
3269 |
|
|
default:
|
3270 |
|
|
return -1;
|
3271 |
|
|
}
|
3272 |
|
|
|
3273 |
|
|
// Dump PUBLIC
|
3274 |
|
|
$first = true;
|
3275 |
|
|
$sql .= ($mode == 'GRANT') ? ' TO ' : ' FROM ';
|
3276 |
|
|
if ($public) {
|
3277 |
|
|
$sql .= 'PUBLIC';
|
3278 |
|
|
$first = false;
|
3279 |
|
|
}
|
3280 |
|
|
// Dump users
|
3281 |
|
|
foreach ($usernames as $v) {
|
3282 |
|
|
if ($first) {
|
3283 |
|
|
$sql .= "\"{$v}\"";
|
3284 |
|
|
$first = false;
|
3285 |
|
|
}
|
3286 |
|
|
else {
|
3287 |
|
|
$sql .= ", \"{$v}\"";
|
3288 |
|
|
}
|
3289 |
|
|
}
|
3290 |
|
|
// Dump groups
|
3291 |
|
|
foreach ($groupnames as $v) {
|
3292 |
|
|
if ($first) {
|
3293 |
|
|
$sql .= "GROUP \"{$v}\"";
|
3294 |
|
|
$first = false;
|
3295 |
|
|
}
|
3296 |
|
|
else {
|
3297 |
|
|
$sql .= ", GROUP \"{$v}\"";
|
3298 |
|
|
}
|
3299 |
|
|
}
|
3300 |
|
|
|
3301 |
|
|
// Grant option
|
3302 |
|
|
if ($this->hasGrantOption() && $mode == 'GRANT' && $grantoption) {
|
3303 |
|
|
$sql .= ' WITH GRANT OPTION';
|
3304 |
|
|
}
|
3305 |
|
|
|
3306 |
|
|
// Cascade revoke
|
3307 |
|
|
if ($this->hasGrantOption() && $mode == 'REVOKE' && $cascade) {
|
3308 |
|
|
$sql .= ' CASCADE';
|
3309 |
|
|
}
|
3310 |
|
|
|
3311 |
|
|
return $this->execute($sql);
|
3312 |
|
|
}
|
3313 |
|
|
|
3314 |
|
|
// Administration functions
|
3315 |
|
|
|
3316 |
|
|
/**
|
3317 |
|
|
* Vacuums a database
|
3318 |
|
|
* @param $table The table to vacuum
|
3319 |
|
|
* @param $analyze If true, also does analyze
|
3320 |
|
|
* @param $full If true, selects "full" vacuum (PostgreSQL >= 7.2)
|
3321 |
|
|
* @param $freeze If true, selects aggressive "freezing" of tuples (PostgreSQL >= 7.2)
|
3322 |
|
|
*/
|
3323 |
|
|
function vacuumDB($table = '', $analyze = false, $full = false, $freeze = false) {
|
3324 |
|
|
$sql = "VACUUM";
|
3325 |
|
|
if ($analyze) $sql .= " ANALYZE";
|
3326 |
|
|
if ($table != '') {
|
3327 |
|
|
$this->fieldClean($table);
|
3328 |
|
|
$sql .= " \"{$table}\"";
|
3329 |
|
|
}
|
3330 |
|
|
|
3331 |
|
|
return $this->execute($sql);
|
3332 |
|
|
}
|
3333 |
|
|
|
3334 |
|
|
/**
|
3335 |
|
|
* Analyze a database
|
3336 |
|
|
* @param $table (optional) The table to analyze
|
3337 |
|
|
*/
|
3338 |
|
|
function analyzeDB($table = '') {
|
3339 |
|
|
if ($table != '') {
|
3340 |
|
|
$this->fieldClean($table);
|
3341 |
|
|
$sql = "VACUUM ANALYZE \"{$table}\"";
|
3342 |
|
|
}
|
3343 |
|
|
else
|
3344 |
|
|
$sql = "VACUUM ANALYZE";
|
3345 |
|
|
|
3346 |
|
|
return $this->execute($sql);
|
3347 |
|
|
}
|
3348 |
|
|
|
3349 |
|
|
/**
|
3350 |
|
|
* Rebuild indexes
|
3351 |
|
|
* @param $type 'DATABASE' or 'TABLE' or 'INDEX'
|
3352 |
|
|
* @param $name The name of the specific database, table, or index to be reindexed
|
3353 |
|
|
* @param $force If true, recreates indexes forcedly in PostgreSQL 7.0-7.1, forces rebuild of system indexes in 7.2-7.3, ignored in >=7.4
|
3354 |
|
|
*/
|
3355 |
|
|
function reindex($type, $name, $force = false) {
|
3356 |
|
|
$this->fieldClean($name);
|
3357 |
|
|
switch($type) {
|
3358 |
|
|
case 'DATABASE':
|
3359 |
|
|
case 'TABLE':
|
3360 |
|
|
case 'INDEX':
|
3361 |
|
|
$sql = "REINDEX {$type} \"{$name}\"";
|
3362 |
|
|
if ($force) $sql .= ' FORCE';
|
3363 |
|
|
break;
|
3364 |
|
|
default:
|
3365 |
|
|
return -1;
|
3366 |
|
|
}
|
3367 |
|
|
|
3368 |
|
|
return $this->execute($sql);
|
3369 |
|
|
}
|
3370 |
|
|
|
3371 |
|
|
// Function functions
|
3372 |
|
|
|
3373 |
|
|
/**
|
3374 |
|
|
* Returns a list of all functions in the database
|
3375 |
|
|
* @param $all If true, will find all available functions, if false just userland ones
|
3376 |
|
|
* @return All functions
|
3377 |
|
|
*/
|
3378 |
|
|
function getFunctions($all = false) {
|
3379 |
|
|
global $conf;
|
3380 |
|
|
|
3381 |
|
|
if ($all || $conf['show_system'])
|
3382 |
|
|
$where = '';
|
3383 |
|
|
else
|
3384 |
|
|
$where = "AND pc.oid > '{$this->_lastSystemOID}'::oid";
|
3385 |
|
|
|
3386 |
|
|
$sql = "SELECT
|
3387 |
|
|
pc.oid AS prooid,
|
3388 |
|
|
proname,
|
3389 |
|
|
proretset,
|
3390 |
|
|
pt.typname AS proresult,
|
3391 |
|
|
pl.lanname AS prolanguage,
|
3392 |
|
|
oidvectortypes(pc.proargtypes) AS proarguments,
|
3393 |
|
|
(SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment,
|
3394 |
|
|
proname || ' (' || oidvectortypes(pc.proargtypes) || ')' AS proproto,
|
3395 |
|
|
CASE WHEN proretset THEN 'setof '::text ELSE '' END || pt.typname AS proreturns,
|
3396 |
|
|
usename as proowner
|
3397 |
|
|
FROM
|
3398 |
|
|
pg_proc pc, pg_user pu, pg_type pt, pg_language pl
|
3399 |
|
|
WHERE
|
3400 |
|
|
pc.proowner = pu.usesysid
|
3401 |
|
|
AND pc.prorettype = pt.oid
|
3402 |
|
|
AND pc.prolang = pl.oid
|
3403 |
|
|
{$where}
|
3404 |
|
|
UNION
|
3405 |
|
|
SELECT
|
3406 |
|
|
pc.oid AS prooid,
|
3407 |
|
|
proname,
|
3408 |
|
|
proretset,
|
3409 |
|
|
'opaque' AS proresult,
|
3410 |
|
|
pl.lanname AS prolanguage,
|
3411 |
|
|
oidvectortypes(pc.proargtypes) AS proarguments,
|
3412 |
|
|
(SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment,
|
3413 |
|
|
proname || ' (' || oidvectortypes(pc.proargtypes) || ')' AS proproto,
|
3414 |
|
|
CASE WHEN proretset THEN 'setof '::text ELSE '' END || 'opaque' AS proreturns,
|
3415 |
|
|
usename as proowner
|
3416 |
|
|
FROM
|
3417 |
|
|
pg_proc pc, pg_user pu, pg_type pt, pg_language pl
|
3418 |
|
|
WHERE
|
3419 |
|
|
pc.proowner = pu.usesysid
|
3420 |
|
|
AND pc.prorettype = 0
|
3421 |
|
|
AND pc.prolang = pl.oid
|
3422 |
|
|
{$where}
|
3423 |
|
|
ORDER BY
|
3424 |
|
|
proname, proresult
|
3425 |
|
|
";
|
3426 |
|
|
|
3427 |
|
|
return $this->selectSet($sql);
|
3428 |
|
|
}
|
3429 |
|
|
|
3430 |
|
|
/**
|
3431 |
|
|
* Returns a list of all functions that can be used in triggers
|
3432 |
|
|
*/
|
3433 |
|
|
function getTriggerFunctions() {
|
3434 |
|
|
return $this->getFunctions(true);
|
3435 |
|
|
}
|
3436 |
|
|
|
3437 |
|
|
/**
|
3438 |
|
|
* Returns all details for a particular function
|
3439 |
|
|
* @param $function_oid The OID of the function to retrieve
|
3440 |
|
|
* @return Function info
|
3441 |
|
|
*/
|
3442 |
|
|
function getFunction($function_oid) {
|
3443 |
|
|
$this->clean($function_oid);
|
3444 |
|
|
|
3445 |
|
|
$sql = "SELECT
|
3446 |
|
|
pc.oid AS prooid,
|
3447 |
|
|
proname,
|
3448 |
|
|
lanname AS prolanguage,
|
3449 |
|
|
pt.typname AS proresult,
|
3450 |
|
|
prosrc,
|
3451 |
|
|
probin,
|
3452 |
|
|
proretset,
|
3453 |
|
|
proiscachable,
|
3454 |
|
|
oidvectortypes(pc.proargtypes) AS proarguments,
|
3455 |
|
|
(SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment
|
3456 |
|
|
FROM
|
3457 |
|
|
pg_proc pc, pg_language pl, pg_type pt
|
3458 |
|
|
WHERE
|
3459 |
|
|
pc.oid = '$function_oid'::oid
|
3460 |
|
|
AND pc.prolang = pl.oid
|
3461 |
|
|
AND pc.prorettype = pt.oid
|
3462 |
|
|
";
|
3463 |
|
|
|
3464 |
|
|
return $this->selectSet($sql);
|
3465 |
|
|
}
|
3466 |
|
|
|
3467 |
|
|
/**
|
3468 |
|
|
* Returns an array containing a function's properties
|
3469 |
|
|
* @param $f The array of data for the function
|
3470 |
|
|
* @return An array containing the properties
|
3471 |
|
|
*/
|
3472 |
|
|
function getFunctionProperties($f) {
|
3473 |
|
|
$temp = array();
|
3474 |
|
|
|
3475 |
|
|
// Cachable
|
3476 |
|
|
$f['proiscachable'] = $this->phpBool($f['proiscachable']);
|
3477 |
|
|
if ($f['proiscachable'])
|
3478 |
|
|
$temp[] = 'ISCACHABLE';
|
3479 |
|
|
else
|
3480 |
|
|
$temp[] = '';
|
3481 |
|
|
|
3482 |
|
|
return $temp;
|
3483 |
|
|
}
|
3484 |
|
|
|
3485 |
|
|
/**
|
3486 |
|
|
* Updates a function. Postgres 7.1 doesn't have CREATE OR REPLACE function,
|
3487 |
|
|
* so we do it with a drop and a recreate.
|
3488 |
|
|
* @param $function_oid The OID of the function
|
3489 |
|
|
* @param $funcname The name of the function to create
|
3490 |
|
|
* @param $newname The new name for the function
|
3491 |
|
|
* @param $args The array of argument types
|
3492 |
|
|
* @param $returns The return type
|
3493 |
|
|
* @param $definition The definition for the new function
|
3494 |
|
|
* @param $language The language the function is written for
|
3495 |
|
|
* @param $flags An array of optional flags
|
3496 |
|
|
* @param $setof True if returns a set, false otherwise
|
3497 |
|
|
* @param $comment The comment on the function
|
3498 |
|
|
* @return 0 success
|
3499 |
|
|
* @return -1 transaction error
|
3500 |
|
|
* @return -2 drop function error
|
3501 |
|
|
* @return -3 create function error
|
3502 |
|
|
* @return -4 comment error
|
3503 |
|
|
*/
|
3504 |
|
|
function setFunction($function_oid, $funcname, $newname, $args, $returns, $definition, $language, $flags, $setof, $comment) {
|
3505 |
|
|
$status = $this->beginTransaction();
|
3506 |
|
|
if ($status != 0) return -1;
|
3507 |
|
|
|
3508 |
|
|
// Drop existing function
|
3509 |
|
|
$status = $this->dropFunction($function_oid, false);
|
3510 |
|
|
if ($status != 0) {
|
3511 |
|
|
$this->rollbackTransaction();
|
3512 |
|
|
return -2;
|
3513 |
|
|
}
|
3514 |
|
|
|
3515 |
|
|
// Create function with new name
|
3516 |
|
|
$status = $this->createFunction($newname, $args, $returns, $definition, $language, $flags, $setof, false);
|
3517 |
|
|
if ($status != 0) {
|
3518 |
|
|
$this->rollbackTransaction();
|
3519 |
|
|
return -3;
|
3520 |
|
|
}
|
3521 |
|
|
|
3522 |
|
|
// Comment on the function
|
3523 |
|
|
$this->fieldClean($newname);
|
3524 |
|
|
$this->clean($comment);
|
3525 |
|
|
$status = $this->setComment('FUNCTION', "\"{$newname}\"({$args})", null, $comment);
|
3526 |
|
|
if ($status != 0) {
|
3527 |
|
|
$this->rollbackTransaction();
|
3528 |
|
|
return -4;
|
3529 |
|
|
}
|
3530 |
|
|
|
3531 |
|
|
$status = $this->endTransaction();
|
3532 |
|
|
return ($status == 0) ? 0 : -1;
|
3533 |
|
|
}
|
3534 |
|
|
|
3535 |
|
|
/**
|
3536 |
|
|
* Creates a new function.
|
3537 |
|
|
* @param $funcname The name of the function to create
|
3538 |
|
|
* @param $args A comma separated string of types
|
3539 |
|
|
* @param $returns The return type
|
3540 |
|
|
* @param $definition The definition for the new function
|
3541 |
|
|
* @param $language The language the function is written for
|
3542 |
|
|
* @param $flags An array of optional flags
|
3543 |
|
|
* @param $setof True if it returns a set, false otherwise
|
3544 |
|
|
* @param $replace (optional) True if OR REPLACE, false for normal
|
3545 |
|
|
* @return 0 success
|
3546 |
|
|
*/
|
3547 |
|
|
function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $replace = false) {
|
3548 |
|
|
$this->fieldClean($funcname);
|
3549 |
|
|
$this->clean($args);
|
3550 |
|
|
$this->clean($language);
|
3551 |
|
|
$this->arrayClean($flags);
|
3552 |
|
|
|
3553 |
|
|
$sql = "CREATE";
|
3554 |
|
|
if ($replace) $sql .= " OR REPLACE";
|
3555 |
|
|
$sql .= " FUNCTION \"{$funcname}\" (";
|
3556 |
|
|
|
3557 |
|
|
if ($args != '')
|
3558 |
|
|
$sql .= $args;
|
3559 |
|
|
|
3560 |
|
|
// For some reason, the returns field cannot have quotes...
|
3561 |
|
|
$sql .= ") RETURNS ";
|
3562 |
|
|
if ($setof) $sql .= "SETOF ";
|
3563 |
|
|
$sql .= "{$returns} AS ";
|
3564 |
|
|
|
3565 |
|
|
if (is_array($definition)) {
|
3566 |
|
|
$this->arrayClean($definition);
|
3567 |
|
|
$sql .= "'" . $definition[0] . "'";
|
3568 |
|
|
if ($definition[1]) {
|
3569 |
|
|
$sql .= ",'" . $definition[1] . "'";
|
3570 |
|
|
}
|
3571 |
|
|
} else {
|
3572 |
|
|
$this->clean($definition);
|
3573 |
|
|
$sql .= "'" . $definition . "'";
|
3574 |
|
|
}
|
3575 |
|
|
|
3576 |
|
|
$sql .= " LANGUAGE '{$language}'";
|
3577 |
|
|
|
3578 |
|
|
// Add flags
|
3579 |
|
|
$first = true;
|
3580 |
|
|
foreach ($flags as $v) {
|
3581 |
|
|
// Skip default flags
|
3582 |
|
|
if ($v == '') continue;
|
3583 |
|
|
elseif ($first) {
|
3584 |
|
|
$sql .= " WITH ({$v}";
|
3585 |
|
|
$first = false;
|
3586 |
|
|
}
|
3587 |
|
|
else {
|
3588 |
|
|
$sql .= ", {$v}";
|
3589 |
|
|
}
|
3590 |
|
|
}
|
3591 |
|
|
// Close off WITH clause if necessary
|
3592 |
|
|
if (!$first) $sql .= ")";
|
3593 |
|
|
|
3594 |
|
|
return $this->execute($sql);
|
3595 |
|
|
}
|
3596 |
|
|
|
3597 |
|
|
/**
|
3598 |
|
|
* Drops a function.
|
3599 |
|
|
* @param $function_oid The OID of the function to drop
|
3600 |
|
|
* @param $cascade True to cascade drop, false to restrict
|
3601 |
|
|
* @return 0 success
|
3602 |
|
|
*/
|
3603 |
|
|
function dropFunction($function_oid, $cascade) {
|
3604 |
|
|
// Function comes in with $object as function OID
|
3605 |
|
|
$fn = $this->getFunction($function_oid);
|
3606 |
|
|
$this->fieldClean($fn->f['proname']);
|
3607 |
|
|
|
3608 |
|
|
$sql = "DROP FUNCTION \"{$fn->f['proname']}\"({$fn->f['proarguments']})";
|
3609 |
|
|
if ($cascade) $sql .= " CASCADE";
|
3610 |
|
|
|
3611 |
|
|
return $this->execute($sql);
|
3612 |
|
|
}
|
3613 |
|
|
|
3614 |
|
|
// Language functions
|
3615 |
|
|
|
3616 |
|
|
/**
|
3617 |
|
|
* Gets all languages
|
3618 |
|
|
* @param $all True to get all languages, regardless of show_system
|
3619 |
|
|
* @return A recordset
|
3620 |
|
|
*/
|
3621 |
|
|
function getLanguages($all = false) {
|
3622 |
|
|
global $conf;
|
3623 |
|
|
|
3624 |
|
|
if ($conf['show_system'] || $all)
|
3625 |
|
|
$where = '';
|
3626 |
|
|
else
|
3627 |
|
|
$where = 'WHERE lanispl';
|
3628 |
|
|
|
3629 |
|
|
$sql = "
|
3630 |
|
|
SELECT
|
3631 |
|
|
lanname,
|
3632 |
|
|
lanpltrusted,
|
3633 |
|
|
lanplcallfoid::regproc AS lanplcallf
|
3634 |
|
|
FROM
|
3635 |
|
|
pg_language
|
3636 |
|
|
{$where}
|
3637 |
|
|
ORDER BY
|
3638 |
|
|
lanname
|
3639 |
|
|
";
|
3640 |
|
|
|
3641 |
|
|
return $this->selectSet($sql);
|
3642 |
|
|
}
|
3643 |
|
|
|
3644 |
|
|
// Aggregate functions
|
3645 |
|
|
|
3646 |
|
|
/**
|
3647 |
|
|
* Gets all aggregates
|
3648 |
|
|
* @return A recordset
|
3649 |
|
|
*/
|
3650 |
|
|
function getAggregates() {
|
3651 |
|
|
global $conf;
|
3652 |
|
|
|
3653 |
|
|
if ($conf['show_system'])
|
3654 |
|
|
$where = '';
|
3655 |
|
|
else
|
3656 |
|
|
$where = "WHERE a.oid > '{$this->_lastSystemOID}'::oid";
|
3657 |
|
|
|
3658 |
|
|
$sql = "
|
3659 |
|
|
SELECT
|
3660 |
|
|
a.aggname AS proname,
|
3661 |
|
|
CASE a.aggbasetype
|
3662 |
|
|
WHEN 0 THEN NULL
|
3663 |
|
|
ELSE (SELECT typname FROM pg_type t WHERE t.oid=a.aggbasetype)
|
3664 |
|
|
END AS proargtypes,
|
3665 |
|
|
(SELECT description FROM pg_description pd WHERE a.oid=pd.objoid) AS aggcomment
|
3666 |
|
|
FROM
|
3667 |
|
|
pg_aggregate a
|
3668 |
|
|
{$where}
|
3669 |
|
|
ORDER BY
|
3670 |
|
|
1, 2;
|
3671 |
|
|
";
|
3672 |
|
|
|
3673 |
|
|
return $this->selectSet($sql);
|
3674 |
|
|
}
|
3675 |
|
|
|
3676 |
|
|
// Operator Class functions
|
3677 |
|
|
|
3678 |
|
|
/**
|
3679 |
|
|
* Gets all opclasses
|
3680 |
|
|
* @return A recordset
|
3681 |
|
|
*/
|
3682 |
|
|
function getOpClasses() {
|
3683 |
|
|
global $conf;
|
3684 |
|
|
|
3685 |
|
|
if ($conf['show_system'])
|
3686 |
|
|
$where = '';
|
3687 |
|
|
else
|
3688 |
|
|
$where = "AND po.oid > '{$this->_lastSystemOID}'::oid";
|
3689 |
|
|
|
3690 |
|
|
$sql = "
|
3691 |
|
|
SELECT DISTINCT
|
3692 |
|
|
pa.amname,
|
3693 |
|
|
po.opcname,
|
3694 |
|
|
(SELECT typname FROM pg_type t WHERE t.oid=opcdeftype) AS opcintype,
|
3695 |
|
|
TRUE AS opcdefault,
|
3696 |
|
|
NULL::text AS opccomment
|
3697 |
|
|
FROM
|
3698 |
|
|
pg_opclass po, pg_am pa, pg_amop pam
|
3699 |
|
|
WHERE
|
3700 |
|
|
pam.amopid=pa.oid
|
3701 |
|
|
AND pam.amopclaid=po.oid
|
3702 |
|
|
{$where}
|
3703 |
|
|
ORDER BY 1,2
|
3704 |
|
|
";
|
3705 |
|
|
|
3706 |
|
|
return $this->selectSet($sql);
|
3707 |
|
|
}
|
3708 |
|
|
|
3709 |
|
|
// Type conversion routines
|
3710 |
|
|
|
3711 |
|
|
/**
|
3712 |
|
|
* Change the value of a parameter to 't' or 'f' depending on whether it evaluates to true or false
|
3713 |
|
|
* @param $parameter the parameter
|
3714 |
|
|
*/
|
3715 |
|
|
function dbBool(&$parameter) {
|
3716 |
|
|
if ($parameter) $parameter = 't';
|
3717 |
|
|
else $parameter = 'f';
|
3718 |
|
|
|
3719 |
|
|
return $parameter;
|
3720 |
|
|
}
|
3721 |
|
|
|
3722 |
|
|
/**
|
3723 |
|
|
* Change a parameter from 't' or 'f' to a boolean, (others evaluate to false)
|
3724 |
|
|
* @param $parameter the parameter
|
3725 |
|
|
*/
|
3726 |
|
|
function phpBool($parameter) {
|
3727 |
|
|
$parameter = ($parameter == 't');
|
3728 |
|
|
return $parameter;
|
3729 |
|
|
}
|
3730 |
|
|
|
3731 |
|
|
// Misc functions
|
3732 |
|
|
|
3733 |
|
|
/**
|
3734 |
|
|
* Sets the comment for an object in the database
|
3735 |
|
|
* @pre All parameters must already be cleaned
|
3736 |
|
|
* @param $obj_type One of 'TABLE' | 'COLUMN' | 'VIEW' | 'SCHEMA' | 'SEQUENCE' | 'TYPE' | 'FUNCTION'
|
3737 |
|
|
* @param $obj_name The name of the object for which to attach a comment.
|
3738 |
|
|
* @param $table Name of table that $obj_name belongs to. Ignored unless $obj_type is 'TABLE' or 'COLUMN'.
|
3739 |
|
|
* @param $comment The comment to add.
|
3740 |
|
|
* @return 0 success
|
3741 |
|
|
*/
|
3742 |
|
|
function setComment($obj_type, $obj_name, $table, $comment) {
|
3743 |
|
|
$sql = "COMMENT ON {$obj_type} " ;
|
3744 |
|
|
|
3745 |
|
|
switch ($obj_type) {
|
3746 |
|
|
case 'TABLE':
|
3747 |
|
|
$sql .= "\"{$table}\" IS ";
|
3748 |
|
|
break;
|
3749 |
|
|
case 'COLUMN':
|
3750 |
|
|
$sql .= "\"{$table}\".\"{$obj_name}\" IS ";
|
3751 |
|
|
break;
|
3752 |
|
|
case 'VIEW':
|
3753 |
|
|
case 'SCHEMA':
|
3754 |
|
|
case 'SEQUENCE':
|
3755 |
|
|
case 'TYPE':
|
3756 |
|
|
$sql .= "\"{$obj_name}\" IS ";
|
3757 |
|
|
break;
|
3758 |
|
|
case 'FUNCTION':
|
3759 |
|
|
$sql .= "{$obj_name} IS ";
|
3760 |
|
|
break;
|
3761 |
|
|
default:
|
3762 |
|
|
// Unknown object type
|
3763 |
|
|
return -1;
|
3764 |
|
|
}
|
3765 |
|
|
|
3766 |
|
|
if ($comment != '')
|
3767 |
|
|
$sql .= "'{$comment}';";
|
3768 |
|
|
else
|
3769 |
|
|
$sql .= 'NULL;';
|
3770 |
|
|
|
3771 |
|
|
return $this->execute($sql);
|
3772 |
|
|
|
3773 |
|
|
}
|
3774 |
|
|
|
3775 |
|
|
/**
|
3776 |
|
|
* Returns the SQL for changing the current user
|
3777 |
|
|
* @param $user The user to change to
|
3778 |
|
|
* @return The SQL
|
3779 |
|
|
*/
|
3780 |
|
|
function getChangeUserSQL($user) {
|
3781 |
|
|
$this->fieldClean($user);
|
3782 |
|
|
return "\\connect - \"{$user}\"";
|
3783 |
|
|
}
|
3784 |
|
|
|
3785 |
|
|
/**
|
3786 |
|
|
* Sets up the data object for a dump. eg. Starts the appropriate
|
3787 |
|
|
* transaction, sets variables, etc.
|
3788 |
|
|
* @return 0 success
|
3789 |
|
|
*/
|
3790 |
|
|
function beginDump() {
|
3791 |
|
|
// Begin serializable transaction (to dump consistent data)
|
3792 |
|
|
$status = $this->beginTransaction();
|
3793 |
|
|
if ($status != 0) return -1;
|
3794 |
|
|
|
3795 |
|
|
// Set serializable
|
3796 |
|
|
$sql = "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE";
|
3797 |
|
|
$status = $this->execute($sql);
|
3798 |
|
|
if ($status != 0) {
|
3799 |
|
|
$this->rollbackTransaction();
|
3800 |
|
|
return -1;
|
3801 |
|
|
}
|
3802 |
|
|
|
3803 |
|
|
// Set datestyle to ISO
|
3804 |
|
|
$sql = "SET DATESTYLE = ISO";
|
3805 |
|
|
$status = $this->execute($sql);
|
3806 |
|
|
if ($status != 0) {
|
3807 |
|
|
$this->rollbackTransaction();
|
3808 |
|
|
return -1;
|
3809 |
|
|
}
|
3810 |
|
|
}
|
3811 |
|
|
|
3812 |
|
|
/**
|
3813 |
|
|
* Ends the data object for a dump.
|
3814 |
|
|
* @return 0 success
|
3815 |
|
|
*/
|
3816 |
|
|
function endDump() {
|
3817 |
|
|
return $this->endTransaction();
|
3818 |
|
|
}
|
3819 |
|
|
|
3820 |
|
|
/**
|
3821 |
|
|
* Generates the SQL for the 'select' function
|
3822 |
|
|
* @param $table The table from which to select
|
3823 |
|
|
* @param $show An array of columns to show. Empty array means all columns.
|
3824 |
|
|
* @param $values An array mapping columns to values
|
3825 |
|
|
* @param $ops An array of the operators to use
|
3826 |
|
|
* @param $orderby (optional) An array of column numbers or names (one based)
|
3827 |
|
|
* mapped to sort direction (asc or desc or '' or null) to order by
|
3828 |
|
|
* @return The SQL query
|
3829 |
|
|
*/
|
3830 |
|
|
function getSelectSQL($table, $show, $values, $ops, $orderby = array()) {
|
3831 |
|
|
$this->fieldClean($table);
|
3832 |
|
|
$this->fieldArrayClean($show);
|
3833 |
|
|
|
3834 |
|
|
// If an empty array is passed in, then show all columns
|
3835 |
|
|
if (sizeof($show) == 0) {
|
3836 |
|
|
if ($this->hasObjectID($table))
|
3837 |
|
|
$sql = "SELECT \"{$this->id}\", * FROM ";
|
3838 |
|
|
else
|
3839 |
|
|
$sql = "SELECT * FROM ";
|
3840 |
|
|
}
|
3841 |
|
|
else {
|
3842 |
|
|
// Add oid column automatically to results for editing purposes
|
3843 |
|
|
if (!in_array($this->id, $show) && $this->hasObjectID($table))
|
3844 |
|
|
$sql = "SELECT \"{$this->id}\", \"";
|
3845 |
|
|
else
|
3846 |
|
|
$sql = "SELECT \"";
|
3847 |
|
|
|
3848 |
|
|
$sql .= join('","', $show) . "\" FROM ";
|
3849 |
|
|
}
|
3850 |
|
|
|
3851 |
|
|
if ($this->hasSchemas() && isset($_REQUEST['schema'])) {
|
3852 |
|
|
$this->fieldClean($_REQUEST['schema']);
|
3853 |
|
|
$sql .= "\"{$_REQUEST['schema']}\".";
|
3854 |
|
|
}
|
3855 |
|
|
$sql .= "\"{$table}\"";
|
3856 |
|
|
|
3857 |
|
|
// If we have values specified, add them to the WHERE clause
|
3858 |
|
|
$first = true;
|
3859 |
|
|
if (is_array($values) && sizeof($values) > 0) {
|
3860 |
|
|
foreach ($values as $k => $v) {
|
3861 |
|
|
if ($v != '' || $this->selectOps[$ops[$k]] == 'p') {
|
3862 |
|
|
$this->fieldClean($k);
|
3863 |
|
|
$this->clean($v);
|
3864 |
|
|
if ($first) {
|
3865 |
|
|
$sql .= " WHERE ";
|
3866 |
|
|
$first = false;
|
3867 |
|
|
} else {
|
3868 |
|
|
$sql .= " AND ";
|
3869 |
|
|
}
|
3870 |
|
|
// Different query format depending on operator type
|
3871 |
|
|
switch ($this->selectOps[$ops[$k]]) {
|
3872 |
|
|
case 'i':
|
3873 |
|
|
$sql .= "\"{$k}\" {$ops[$k]} '{$v}'";
|
3874 |
|
|
break;
|
3875 |
|
|
case 'p':
|
3876 |
|
|
$sql .= "\"{$k}\" {$ops[$k]}";
|
3877 |
|
|
break;
|
3878 |
|
|
case 'x':
|
3879 |
|
|
$sql .= "\"{$k}\" {$ops[$k]} ({$v})";
|
3880 |
|
|
break;
|
3881 |
|
|
default:
|
3882 |
|
|
// Shouldn't happen
|
3883 |
|
|
}
|
3884 |
|
|
}
|
3885 |
|
|
}
|
3886 |
|
|
}
|
3887 |
|
|
|
3888 |
|
|
// ORDER BY
|
3889 |
|
|
if (is_array($orderby) && sizeof($orderby) > 0) {
|
3890 |
|
|
$sql .= " ORDER BY ";
|
3891 |
|
|
$first = true;
|
3892 |
|
|
foreach ($orderby as $k => $v) {
|
3893 |
|
|
if ($first) $first = false;
|
3894 |
|
|
else $sql .= ', ';
|
3895 |
|
|
if (ereg('^[0-9]+$', $k)) {
|
3896 |
|
|
$sql .= $k;
|
3897 |
|
|
}
|
3898 |
|
|
else {
|
3899 |
|
|
$this->fieldClean($k);
|
3900 |
|
|
$sql .= '"' . $k . '"';
|
3901 |
|
|
}
|
3902 |
|
|
if (strtoupper($v) == 'DESC') $sql .= " DESC";
|
3903 |
|
|
}
|
3904 |
|
|
}
|
3905 |
|
|
|
3906 |
|
|
return $sql;
|
3907 |
|
|
}
|
3908 |
|
|
|
3909 |
|
|
/**
|
3910 |
|
|
* Finds the number of rows that would be returned by a
|
3911 |
|
|
* query.
|
3912 |
|
|
* @param $query The SQL query
|
3913 |
|
|
* @param $count The count query
|
3914 |
|
|
* @return The count of rows
|
3915 |
|
|
* @return -1 error
|
3916 |
|
|
*/
|
3917 |
|
|
function browseQueryCount($query, $count) {
|
3918 |
|
|
// Count the number of rows
|
3919 |
|
|
$rs = $this->selectSet($query);
|
3920 |
|
|
if (!is_object($rs)) {
|
3921 |
|
|
return -1;
|
3922 |
|
|
}
|
3923 |
|
|
|
3924 |
|
|
return $rs->recordCount();
|
3925 |
|
|
}
|
3926 |
|
|
|
3927 |
|
|
/**
|
3928 |
|
|
* Returns a recordset of all columns in a query. Supports paging.
|
3929 |
|
|
* @param $type Either 'QUERY' if it is an SQL query, or 'TABLE' if it is a table identifier,
|
3930 |
|
|
* or 'SELECT" if it's a select query
|
3931 |
|
|
* @param $table The base table of the query. NULL for no table.
|
3932 |
|
|
* @param $query The query that is being executed. NULL for no query.
|
3933 |
|
|
* @param $sortkey The column number to sort by, or '' or null for no sorting
|
3934 |
|
|
* @param $sortdir The direction in which to sort the specified column ('asc' or 'desc')
|
3935 |
|
|
* @param $page The page of the relation to retrieve
|
3936 |
|
|
* @param $page_size The number of rows per page
|
3937 |
|
|
* @param &$max_pages (return-by-ref) The max number of pages in the relation
|
3938 |
|
|
* @return A recordset on success
|
3939 |
|
|
* @return -1 transaction error
|
3940 |
|
|
* @return -2 counting error
|
3941 |
|
|
* @return -3 page or page_size invalid
|
3942 |
|
|
* @return -4 unknown type
|
3943 |
|
|
* @return -5 failed setting transaction read only
|
3944 |
|
|
*/
|
3945 |
|
|
function browseQuery($type, $table, $query, $sortkey, $sortdir, $page, $page_size, &$max_pages) {
|
3946 |
|
|
// Check that we're not going to divide by zero
|
3947 |
|
|
if (!is_numeric($page_size) || $page_size != (int)$page_size || $page_size <= 0) return -3;
|
3948 |
|
|
|
3949 |
|
|
// If $type is TABLE, then generate the query
|
3950 |
|
|
switch ($type) {
|
3951 |
|
|
case 'TABLE':
|
3952 |
|
|
if (ereg('^[0-9]+$', $sortkey) && $sortkey > 0) $orderby = array($sortkey => $sortdir);
|
3953 |
|
|
else $orderby = array();
|
3954 |
|
|
$query = $this->getSelectSQL($table, array(), array(), array(), $orderby);
|
3955 |
|
|
break;
|
3956 |
|
|
case 'QUERY':
|
3957 |
|
|
case 'SELECT':
|
3958 |
|
|
// Trim query
|
3959 |
|
|
$query = trim($query);
|
3960 |
|
|
// Trim off trailing semi-colon if there is one
|
3961 |
|
|
if (substr($query, strlen($query) - 1, 1) == ';')
|
3962 |
|
|
$query = substr($query, 0, strlen($query) - 1);
|
3963 |
|
|
break;
|
3964 |
|
|
default:
|
3965 |
|
|
return -4;
|
3966 |
|
|
}
|
3967 |
|
|
|
3968 |
|
|
// Generate count query
|
3969 |
|
|
$count = "SELECT COUNT(*) AS total FROM ($query) AS sub";
|
3970 |
|
|
|
3971 |
|
|
// Open a transaction
|
3972 |
|
|
$status = $this->beginTransaction();
|
3973 |
|
|
if ($status != 0) return -1;
|
3974 |
|
|
|
3975 |
|
|
// If backend supports read only queries, then specify read only mode
|
3976 |
|
|
// to avoid side effects from repeating queries that do writes.
|
3977 |
|
|
if ($this->hasReadOnlyQueries()) {
|
3978 |
|
|
$status = $this->execute("SET TRANSACTION READ ONLY");
|
3979 |
|
|
if ($status != 0) {
|
3980 |
|
|
$this->rollbackTransaction();
|
3981 |
|
|
return -5;
|
3982 |
|
|
}
|
3983 |
|
|
}
|
3984 |
|
|
|
3985 |
|
|
|
3986 |
|
|
// Count the number of rows
|
3987 |
|
|
$total = $this->browseQueryCount($query, $count);
|
3988 |
|
|
if ($total < 0) {
|
3989 |
|
|
$this->rollbackTransaction();
|
3990 |
|
|
return -2;
|
3991 |
|
|
}
|
3992 |
|
|
|
3993 |
|
|
// Calculate max pages
|
3994 |
|
|
$max_pages = ceil($total / $page_size);
|
3995 |
|
|
|
3996 |
|
|
// Check that page is less than or equal to max pages
|
3997 |
|
|
if (!is_numeric($page) || $page != (int)$page || $page > $max_pages || $page < 1) {
|
3998 |
|
|
$this->rollbackTransaction();
|
3999 |
|
|
return -3;
|
4000 |
|
|
}
|
4001 |
|
|
|
4002 |
|
|
// Set fetch mode to NUM so that duplicate field names are properly returned
|
4003 |
|
|
// for non-table queries. Since the SELECT feature only allows selecting one
|
4004 |
|
|
// table, duplicate fields shouldn't appear.
|
4005 |
|
|
if ($type == 'QUERY') $this->conn->setFetchMode(ADODB_FETCH_NUM);
|
4006 |
|
|
|
4007 |
|
|
// Figure out ORDER BY. Sort key is always the column number (based from one)
|
4008 |
|
|
// of the column to order by. Only need to do this for non-TABLE queries
|
4009 |
|
|
if ($type != 'TABLE' && ereg('^[0-9]+$', $sortkey) && $sortkey > 0) {
|
4010 |
|
|
$orderby = " ORDER BY {$sortkey}";
|
4011 |
|
|
// Add sort order
|
4012 |
|
|
if ($sortdir == 'desc')
|
4013 |
|
|
$orderby .= ' DESC';
|
4014 |
|
|
else
|
4015 |
|
|
$orderby .= ' ASC';
|
4016 |
|
|
}
|
4017 |
|
|
else $orderby = '';
|
4018 |
|
|
|
4019 |
|
|
// Actually retrieve the rows, with offset and limit
|
4020 |
|
|
if ($this->hasFullSubqueries())
|
4021 |
|
|
$rs = $this->selectSet("SELECT * FROM ({$query}) AS sub {$orderby} LIMIT {$page_size} OFFSET " . ($page - 1) * $page_size);
|
4022 |
|
|
else
|
4023 |
|
|
$rs = $this->selectSet("{$query} LIMIT {$page_size} OFFSET " . ($page - 1) * $page_size);
|
4024 |
|
|
$status = $this->endTransaction();
|
4025 |
|
|
if ($status != 0) {
|
4026 |
|
|
$this->rollbackTransaction();
|
4027 |
|
|
return -1;
|
4028 |
|
|
}
|
4029 |
|
|
|
4030 |
|
|
return $rs;
|
4031 |
|
|
}
|
4032 |
|
|
|
4033 |
|
|
/**
|
4034 |
|
|
* Returns a recordset of all columns in a relation. Used for data export.
|
4035 |
|
|
* @@ Note: Really needs to use a cursor
|
4036 |
|
|
* @param $relation The name of a relation
|
4037 |
|
|
* @return A recordset on success
|
4038 |
|
|
* @return -1 Failed to set datestyle
|
4039 |
|
|
*/
|
4040 |
|
|
function dumpRelation($relation, $oids) {
|
4041 |
|
|
$this->fieldClean($relation);
|
4042 |
|
|
|
4043 |
|
|
// Actually retrieve the rows
|
4044 |
|
|
if ($oids) $oid_str = $this->id . ', ';
|
4045 |
|
|
else $oid_str = '';
|
4046 |
|
|
|
4047 |
|
|
return $this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\"");
|
4048 |
|
|
}
|
4049 |
|
|
|
4050 |
|
|
/**
|
4051 |
|
|
* Searches all system catalogs to find objects that match a certain name.
|
4052 |
|
|
* @param $term The search term
|
4053 |
|
|
* @param $filter The object type to restrict to ('' means no restriction)
|
4054 |
|
|
* @return A recordset
|
4055 |
|
|
*/
|
4056 |
|
|
function findObject($term, $filter) {
|
4057 |
|
|
global $conf;
|
4058 |
|
|
|
4059 |
|
|
// Escape search term for ~* match
|
4060 |
|
|
$special = array('.', '*', '^', '$', ':', '?', '+', ',', '=', '!', '[', ']', '(', ')', '{', '}', '<', '>', '-', '\\');
|
4061 |
|
|
foreach ($special as $v) {
|
4062 |
|
|
$term = str_replace($v, "\\{$v}", $term);
|
4063 |
|
|
}
|
4064 |
|
|
$this->clean($term);
|
4065 |
|
|
$this->clean($filter);
|
4066 |
|
|
|
4067 |
|
|
// Build SQL, excluding system relations as necessary
|
4068 |
|
|
// Relations
|
4069 |
|
|
$case_clause = "CASE WHEN relkind='r' THEN (CASE WHEN EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = pc.oid AND r.ev_type = '1') THEN 'VIEW'::VARCHAR ELSE 'TABLE'::VARCHAR END) WHEN relkind='v' THEN 'VIEW'::VARCHAR WHEN relkind='S' THEN 'SEQUENCE'::VARCHAR END";
|
4070 |
|
|
$sql = "
|
4071 |
|
|
SELECT {$case_clause} AS type,
|
4072 |
|
|
pc.oid, NULL::VARCHAR AS schemaname, NULL::VARCHAR AS relname, pc.relname AS name FROM pg_class pc
|
4073 |
|
|
WHERE relkind IN ('r', 'v', 'S') AND relname ~* '.*{$term}.*'";
|
4074 |
|
|
if (!$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg\\\\_%'";
|
4075 |
|
|
if ($filter == 'TABLE' || $filter == 'VIEW' || $filter == 'SEQUENCE') $sql .= " AND {$case_clause} = '{$filter}'";
|
4076 |
|
|
elseif ($filter != '') $sql .= " AND FALSE";
|
4077 |
|
|
|
4078 |
|
|
// Columns
|
4079 |
|
|
$sql .= "
|
4080 |
|
|
UNION ALL
|
4081 |
|
|
SELECT CASE WHEN relkind='r' THEN (CASE WHEN EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = pc.oid AND r.ev_type = '1') THEN 'COLUMNVIEW'::VARCHAR ELSE 'COLUMNTABLE'::VARCHAR END) WHEN relkind='v' THEN 'COLUMNVIEW'::VARCHAR END,
|
4082 |
|
|
NULL, NULL, pc.relname, pa.attname FROM pg_class pc,
|
4083 |
|
|
pg_attribute pa WHERE pc.oid=pa.attrelid
|
4084 |
|
|
AND pa.attname ~* '.*{$term}.*' AND pa.attnum > 0 AND pc.relkind IN ('r', 'v')";
|
4085 |
|
|
if (!$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg\\\\_%'";
|
4086 |
|
|
if ($filter != '' && $filter != 'COLUMNTABLE' || $filter != 'COLUMNVIEW') $sql .= " AND FALSE";
|
4087 |
|
|
|
4088 |
|
|
// Functions
|
4089 |
|
|
$sql .= "
|
4090 |
|
|
UNION ALL
|
4091 |
|
|
SELECT 'FUNCTION', pp.oid, NULL, NULL, pp.proname || '(' || oidvectortypes(pp.proargtypes) || ')' FROM pg_proc pp
|
4092 |
|
|
WHERE proname ~* '.*{$term}.*'";
|
4093 |
|
|
if (!$conf['show_system']) $sql .= " AND pp.oid > '{$this->_lastSystemOID}'::oid";
|
4094 |
|
|
if ($filter != '' && $filter != 'FUNCTION') $sql .= " AND FALSE";
|
4095 |
|
|
|
4096 |
|
|
// Indexes
|
4097 |
|
|
$sql .= "
|
4098 |
|
|
UNION ALL
|
4099 |
|
|
SELECT 'INDEX', NULL, NULL, pc.relname, pc2.relname FROM pg_class pc,
|
4100 |
|
|
pg_index pi, pg_class pc2 WHERE pc.oid=pi.indrelid
|
4101 |
|
|
AND pi.indexrelid=pc2.oid
|
4102 |
|
|
AND pc2.relname ~* '.*{$term}.*' AND NOT pi.indisprimary AND NOT pi.indisunique";
|
4103 |
|
|
if (!$conf['show_system']) $sql .= " AND pc2.relname NOT LIKE 'pg\\\\_%'";
|
4104 |
|
|
if ($filter != '' && $filter != 'INDEX') $sql .= " AND FALSE";
|
4105 |
|
|
|
4106 |
|
|
// Check Constraints
|
4107 |
|
|
$sql .= "
|
4108 |
|
|
UNION ALL
|
4109 |
|
|
SELECT 'CONSTRAINTTABLE', NULL, NULL, pc.relname, pr.rcname FROM pg_class pc,
|
4110 |
|
|
pg_relcheck pr WHERE pc.oid=pr.rcrelid
|
4111 |
|
|
AND pr.rcname ~* '.*{$term}.*'";
|
4112 |
|
|
if (!$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg\\\\_%'";
|
4113 |
|
|
if ($filter != '' && $filter != 'CONSTRAINT') $sql .= " AND FALSE";
|
4114 |
|
|
|
4115 |
|
|
// Unique and Primary Key Constraints
|
4116 |
|
|
$sql .= "
|
4117 |
|
|
UNION ALL
|
4118 |
|
|
SELECT 'CONSTRAINTTABLE', NULL, NULL, pc.relname, pc2.relname FROM pg_class pc,
|
4119 |
|
|
pg_index pi, pg_class pc2 WHERE pc.oid=pi.indrelid
|
4120 |
|
|
AND pi.indexrelid=pc2.oid
|
4121 |
|
|
AND pc2.relname ~* '.*{$term}.*' AND (pi.indisprimary OR pi.indisunique)";
|
4122 |
|
|
if (!$conf['show_system']) $sql .= " AND pc2.relname NOT LIKE 'pg\\\\_%'";
|
4123 |
|
|
if ($filter != '' && $filter != 'CONSTRAINT') $sql .= " AND FALSE";
|
4124 |
|
|
|
4125 |
|
|
// Triggers
|
4126 |
|
|
$sql .= "
|
4127 |
|
|
UNION ALL
|
4128 |
|
|
SELECT 'TRIGGER', NULL, NULL, pc.relname, pt.tgname FROM pg_class pc,
|
4129 |
|
|
pg_trigger pt WHERE pc.oid=pt.tgrelid
|
4130 |
|
|
AND pt.tgname ~* '.*{$term}.*'";
|
4131 |
|
|
if (!$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg\\\\_%'";
|
4132 |
|
|
if ($filter != '' && $filter != 'TRIGGER') $sql .= " AND FALSE";
|
4133 |
|
|
|
4134 |
|
|
// Table Rules
|
4135 |
|
|
$sql .= "
|
4136 |
|
|
UNION ALL
|
4137 |
|
|
SELECT 'RULETABLE', NULL, NULL, c.relname AS tablename, r.rulename
|
4138 |
|
|
FROM pg_rewrite r, pg_class c
|
4139 |
|
|
WHERE c.relkind='r' AND NOT EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = c.oid AND r.ev_type = '1')
|
4140 |
|
|
AND r.rulename !~ '^_RET' AND c.oid = r.ev_class AND r.rulename ~* '.*{$term}.*'";
|
4141 |
|
|
if (!$conf['show_system']) $sql .= " AND c.relname NOT LIKE 'pg\\\\_%'";
|
4142 |
|
|
if ($filter != '' && $filter != 'RULE') $sql .= " AND FALSE";
|
4143 |
|
|
|
4144 |
|
|
// View Rules
|
4145 |
|
|
$sql .= "
|
4146 |
|
|
UNION ALL
|
4147 |
|
|
SELECT 'RULEVIEW', NULL, NULL, c.relname AS tablename, r.rulename
|
4148 |
|
|
FROM pg_rewrite r, pg_class c
|
4149 |
|
|
WHERE c.relkind='r' AND EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = c.oid AND r.ev_type = '1')
|
4150 |
|
|
AND r.rulename !~ '^_RET' AND c.oid = r.ev_class AND r.rulename ~* '.*{$term}.*'";
|
4151 |
|
|
if (!$conf['show_system']) $sql .= " AND c.relname NOT LIKE 'pg\\\\_%'";
|
4152 |
|
|
if ($filter != '' && $filter != 'RULE') $sql .= " AND FALSE";
|
4153 |
|
|
|
4154 |
|
|
// Advanced Objects
|
4155 |
|
|
if ($conf['show_advanced']) {
|
4156 |
|
|
// Types
|
4157 |
|
|
$sql .= "
|
4158 |
|
|
UNION ALL
|
4159 |
|
|
SELECT 'TYPE', pt.oid, NULL, NULL, pt.typname FROM pg_type pt
|
4160 |
|
|
WHERE typname ~* '.*{$term}.*' AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_class c WHERE c.oid = pt.typrelid))";
|
4161 |
|
|
if (!$conf['show_system']) $sql .= " AND pt.oid > '{$this->_lastSystemOID}'::oid";
|
4162 |
|
|
if ($filter != '' && $filter != 'TYPE') $sql .= " AND FALSE";
|
4163 |
|
|
|
4164 |
|
|
// Operators
|
4165 |
|
|
$sql .= "
|
4166 |
|
|
UNION ALL
|
4167 |
|
|
SELECT 'OPERATOR', po.oid, NULL, NULL, po.oprname FROM pg_operator po
|
4168 |
|
|
WHERE oprname ~* '.*{$term}.*'";
|
4169 |
|
|
if (!$conf['show_system']) $sql .= " AND po.oid > '{$this->_lastSystemOID}'::oid";
|
4170 |
|
|
if ($filter != '' && $filter != 'OPERATOR') $sql .= " AND FALSE";
|
4171 |
|
|
|
4172 |
|
|
// Languages
|
4173 |
|
|
$sql .= "
|
4174 |
|
|
UNION ALL
|
4175 |
|
|
SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_language pl
|
4176 |
|
|
WHERE lanname ~* '.*{$term}.*'";
|
4177 |
|
|
if (!$conf['show_system']) $sql .= " AND pl.lanispl";
|
4178 |
|
|
if ($filter != '' && $filter != 'LANGUAGE') $sql .= " AND FALSE";
|
4179 |
|
|
|
4180 |
|
|
// Aggregates
|
4181 |
|
|
$sql .= "
|
4182 |
|
|
UNION ALL
|
4183 |
|
|
SELECT DISTINCT ON (a.aggname) 'AGGREGATE', a.oid, NULL, NULL, a.aggname FROM pg_aggregate a
|
4184 |
|
|
WHERE aggname ~* '.*{$term}.*'";
|
4185 |
|
|
if (!$conf['show_system']) $sql .= " AND a.oid > '{$this->_lastSystemOID}'::oid";
|
4186 |
|
|
if ($filter != '' && $filter != 'AGGREGATE') $sql .= " AND FALSE";
|
4187 |
|
|
|
4188 |
|
|
// Op Classes
|
4189 |
|
|
$sql .= "
|
4190 |
|
|
UNION ALL
|
4191 |
|
|
SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, NULL, NULL, po.opcname FROM pg_opclass po
|
4192 |
|
|
WHERE po.opcname ~* '.*{$term}.*'";
|
4193 |
|
|
if (!$conf['show_system']) $sql .= " AND po.oid > '{$this->_lastSystemOID}'::oid";
|
4194 |
|
|
if ($filter != '' && $filter != 'OPCLASS') $sql .= " AND FALSE";
|
4195 |
|
|
}
|
4196 |
|
|
|
4197 |
|
|
$sql .= " ORDER BY type, schemaname, relname, name";
|
4198 |
|
|
|
4199 |
|
|
return $this->selectSet($sql);
|
4200 |
|
|
}
|
4201 |
|
|
|
4202 |
|
|
/**
|
4203 |
|
|
* Private helper method to detect a valid $foo$ quote delimiter at
|
4204 |
|
|
* the start of the parameter dquote
|
4205 |
|
|
* @return True if valid, false otherwise
|
4206 |
|
|
*/
|
4207 |
|
|
function valid_dolquote($dquote) {
|
4208 |
|
|
// XXX: support multibyte
|
4209 |
|
|
return (ereg('^[$][$]', $dquote) || ereg('^[$][_[:alpha:]][_[:alnum:]]*[$]', $dquote));
|
4210 |
|
|
}
|
4211 |
|
|
|
4212 |
|
|
/**
|
4213 |
|
|
* A private helper method for executeScript that advances the
|
4214 |
|
|
* character by 1. In psql this is careful to take into account
|
4215 |
|
|
* multibyte languages, but we don't at the moment, so this function
|
4216 |
|
|
* is someone redundant, since it will always advance by 1
|
4217 |
|
|
* @param &$i The current character position in the line
|
4218 |
|
|
* @param &$prevlen Length of previous character (ie. 1)
|
4219 |
|
|
* @param &$thislen Length of current character (ie. 1)
|
4220 |
|
|
*/
|
4221 |
|
|
function advance_1(&$i, &$prevlen, &$thislen) {
|
4222 |
|
|
$prevlen = $thislen;
|
4223 |
|
|
$i += $thislen;
|
4224 |
|
|
$thislen = 1;
|
4225 |
|
|
}
|
4226 |
|
|
|
4227 |
|
|
/**
|
4228 |
|
|
* Executes an SQL script as a series of SQL statements. Returns
|
4229 |
|
|
* the result of the final step. This is a very complicated lexer
|
4230 |
|
|
* based on the REL7_4_STABLE src/bin/psql/mainloop.c lexer in
|
4231 |
|
|
* the PostgreSQL source code.
|
4232 |
|
|
* XXX: It does not handle multibyte languages properly.
|
4233 |
|
|
* @param $name Entry in $_FILES to use
|
4234 |
|
|
* @param $callback (optional) Callback function to call with each query,
|
4235 |
|
|
its result and line number.
|
4236 |
|
|
* @return True for general success, false on any failure.
|
4237 |
|
|
*/
|
4238 |
|
|
function executeScript($name, $callback = null) {
|
4239 |
|
|
global $data;
|
4240 |
|
|
|
4241 |
|
|
// This whole function isn't very encapsulated, but hey...
|
4242 |
|
|
$conn = $data->conn->_connectionID;
|
4243 |
|
|
if (!is_uploaded_file($_FILES[$name]['tmp_name'])) return false;
|
4244 |
|
|
|
4245 |
|
|
$fd = fopen($_FILES[$name]['tmp_name'], 'r');
|
4246 |
|
|
if (!$fd) return false;
|
4247 |
|
|
|
4248 |
|
|
// Build up each SQL statement, they can be multiline
|
4249 |
|
|
$query_buf = null;
|
4250 |
|
|
$query_start = 0;
|
4251 |
|
|
$in_quote = 0;
|
4252 |
|
|
$in_xcomment = 0;
|
4253 |
|
|
$bslash_count = 0;
|
4254 |
|
|
$dol_quote = null;
|
4255 |
|
|
$paren_level = 0;
|
4256 |
|
|
$len = 0;
|
4257 |
|
|
$i = 0;
|
4258 |
|
|
$prevlen = 0;
|
4259 |
|
|
$thislen = 0;
|
4260 |
|
|
$lineno = 0;
|
4261 |
|
|
|
4262 |
|
|
// Loop over each line in the file
|
4263 |
|
|
while (!feof($fd)) {
|
4264 |
|
|
$line = fgets($fd, 32768);
|
4265 |
|
|
$lineno++;
|
4266 |
|
|
|
4267 |
|
|
// Nothing left on line? Then ignore...
|
4268 |
|
|
if (trim($line) == '') continue;
|
4269 |
|
|
|
4270 |
|
|
$len = strlen($line);
|
4271 |
|
|
$query_start = 0;
|
4272 |
|
|
|
4273 |
|
|
/*
|
4274 |
|
|
* Parse line, looking for command separators.
|
4275 |
|
|
*
|
4276 |
|
|
* The current character is at line[i], the prior character at line[i
|
4277 |
|
|
* - prevlen], the next character at line[i + thislen].
|
4278 |
|
|
*/
|
4279 |
|
|
$prevlen = 0;
|
4280 |
|
|
$thislen = ($len > 0) ? 1 : 0;
|
4281 |
|
|
|
4282 |
|
|
for ($i = 0; $i < $len; $this->advance_1($i, $prevlen, $thislen)) {
|
4283 |
|
|
|
4284 |
|
|
/* was the previous character a backslash? */
|
4285 |
|
|
if ($i > 0 && substr($line, $i - $prevlen, 1) == '\\')
|
4286 |
|
|
$bslash_count++;
|
4287 |
|
|
else
|
4288 |
|
|
$bslash_count = 0;
|
4289 |
|
|
|
4290 |
|
|
/*
|
4291 |
|
|
* It is important to place the in_* test routines before the
|
4292 |
|
|
* in_* detection routines. i.e. we have to test if we are in
|
4293 |
|
|
* a quote before testing for comments.
|
4294 |
|
|
*/
|
4295 |
|
|
|
4296 |
|
|
/* in quote? */
|
4297 |
|
|
if ($in_quote != 0)
|
4298 |
|
|
{
|
4299 |
|
|
/*
|
4300 |
|
|
* end of quote if matching non-backslashed character.
|
4301 |
|
|
* backslashes don't count for double quotes, though.
|
4302 |
|
|
*/
|
4303 |
|
|
if (substr($line, $i, 1) == $in_quote &&
|
4304 |
|
|
($bslash_count % 2 == 0 || $in_quote == '"'))
|
4305 |
|
|
$in_quote = 0;
|
4306 |
|
|
}
|
4307 |
|
|
|
4308 |
|
|
/* in or end of $foo$ type quote? */
|
4309 |
|
|
else if ($dol_quote) {
|
4310 |
|
|
if (strncmp(substr($line, $i), $dol_quote, strlen($dol_quote)) == 0) {
|
4311 |
|
|
$this->advance_1($i, $prevlen, $thislen);
|
4312 |
|
|
while(substr($line, $i, 1) != '$')
|
4313 |
|
|
$this->advance_1($i, $prevlen, $thislen);
|
4314 |
|
|
$dol_quote = null;
|
4315 |
|
|
}
|
4316 |
|
|
}
|
4317 |
|
|
|
4318 |
|
|
/* start of extended comment? */
|
4319 |
|
|
else if (substr($line, $i, 2) == '/*')
|
4320 |
|
|
{
|
4321 |
|
|
$in_xcomment++;
|
4322 |
|
|
if ($in_xcomment == 1)
|
4323 |
|
|
$this->advance_1($i, $prevlen, $thislen);
|
4324 |
|
|
}
|
4325 |
|
|
|
4326 |
|
|
/* in or end of extended comment? */
|
4327 |
|
|
else if ($in_xcomment)
|
4328 |
|
|
{
|
4329 |
|
|
if (substr($line, $i, 2) == '*/' && !--$in_xcomment)
|
4330 |
|
|
$this->advance_1($i, $prevlen, $thislen);
|
4331 |
|
|
}
|
4332 |
|
|
|
4333 |
|
|
/* start of quote? */
|
4334 |
|
|
else if (substr($line, $i, 1) == '\'' || substr($line, $i, 1) == '"') {
|
4335 |
|
|
$in_quote = substr($line, $i, 1);
|
4336 |
|
|
}
|
4337 |
|
|
|
4338 |
|
|
/*
|
4339 |
|
|
* start of $foo$ type quote?
|
4340 |
|
|
*/
|
4341 |
|
|
else if (!$dol_quote && $this->valid_dolquote(substr($line, $i))) {
|
4342 |
|
|
$dol_end = strpos(substr($line, $i + 1), '$');
|
4343 |
|
|
$dol_quote = substr($line, $i, $dol_end + 1);
|
4344 |
|
|
$this->advance_1($i, $prevlen, $thislen);
|
4345 |
|
|
while (substr($line, $i, 1) != '$') {
|
4346 |
|
|
$this->advance_1($i, $prevlen, $thislen);
|
4347 |
|
|
}
|
4348 |
|
|
|
4349 |
|
|
}
|
4350 |
|
|
|
4351 |
|
|
/* single-line comment? truncate line */
|
4352 |
|
|
else if (substr($line, $i, 2) == '--')
|
4353 |
|
|
{
|
4354 |
|
|
$line = substr($line, 0, $i); /* remove comment */
|
4355 |
|
|
break;
|
4356 |
|
|
}
|
4357 |
|
|
|
4358 |
|
|
/* count nested parentheses */
|
4359 |
|
|
else if (substr($line, $i, 1) == '(') {
|
4360 |
|
|
$paren_level++;
|
4361 |
|
|
}
|
4362 |
|
|
|
4363 |
|
|
else if (substr($line, $i, 1) == ')' && $paren_level > 0) {
|
4364 |
|
|
$paren_level--;
|
4365 |
|
|
}
|
4366 |
|
|
|
4367 |
|
|
/* semicolon? then send query */
|
4368 |
|
|
else if (substr($line, $i, 1) == ';' && !$bslash_count && !$paren_level)
|
4369 |
|
|
{
|
4370 |
|
|
$subline = substr(substr($line, 0, $i), $query_start);
|
4371 |
|
|
/* is there anything else on the line? */
|
4372 |
|
|
if (strspn($subline, " \t\n\r") != strlen($subline))
|
4373 |
|
|
{
|
4374 |
|
|
/*
|
4375 |
|
|
* insert a cosmetic newline, if this is not the first
|
4376 |
|
|
* line in the buffer
|
4377 |
|
|
*/
|
4378 |
|
|
if (strlen($query_buf) > 0)
|
4379 |
|
|
$query_buf .= "\n";
|
4380 |
|
|
/* append the line to the query buffer */
|
4381 |
|
|
$query_buf .= $subline;
|
4382 |
|
|
$query_buf .= ';';
|
4383 |
|
|
|
4384 |
|
|
// Execute the query (supporting 4.1.x PHP...). PHP cannot execute
|
4385 |
|
|
// empty queries, unlike libpq
|
4386 |
|
|
if (function_exists('pg_query'))
|
4387 |
|
|
$res = @pg_query($conn, $query_buf);
|
4388 |
|
|
else
|
4389 |
|
|
$res = @pg_exec($conn, $query_buf);
|
4390 |
|
|
// Call the callback function for display
|
4391 |
|
|
if ($callback !== null) $callback($query_buf, $res, $lineno);
|
4392 |
|
|
// Check for COPY request
|
4393 |
|
|
if (pg_result_status($res) == 4) { // 4 == PGSQL_COPY_FROM
|
4394 |
|
|
while (!feof($fd)) {
|
4395 |
|
|
$copy = fgets($fd, 32768);
|
4396 |
|
|
$lineno++;
|
4397 |
|
|
pg_put_line($conn, $copy);
|
4398 |
|
|
if ($copy == "\\.\n" || $copy == "\\.\r\n") {
|
4399 |
|
|
pg_end_copy($conn);
|
4400 |
|
|
break;
|
4401 |
|
|
}
|
4402 |
|
|
}
|
4403 |
|
|
}
|
4404 |
|
|
}
|
4405 |
|
|
|
4406 |
|
|
$query_buf = null;
|
4407 |
|
|
$query_start = $i + $thislen;
|
4408 |
|
|
}
|
4409 |
|
|
|
4410 |
|
|
/*
|
4411 |
|
|
* keyword or identifier?
|
4412 |
|
|
* We grab the whole string so that we don't
|
4413 |
|
|
* mistakenly see $foo$ inside an identifier as the start
|
4414 |
|
|
* of a dollar quote.
|
4415 |
|
|
*/
|
4416 |
|
|
// XXX: multibyte here
|
4417 |
|
|
else if (ereg('^[_[:alpha:]]$', substr($line, $i, 1))) {
|
4418 |
|
|
$sub = substr($line, $i, $thislen);
|
4419 |
|
|
while (ereg('^[\$_A-Za-z0-9]$', $sub)) {
|
4420 |
|
|
/* keep going while we still have identifier chars */
|
4421 |
|
|
$this->advance_1($i, $prevlen, $thislen);
|
4422 |
|
|
$sub = substr($line, $i, $thislen);
|
4423 |
|
|
}
|
4424 |
|
|
// Since we're now over the next character to be examined, it is necessary
|
4425 |
|
|
// to move back one space.
|
4426 |
|
|
$i-=$prevlen;
|
4427 |
|
|
}
|
4428 |
|
|
} // end for
|
4429 |
|
|
|
4430 |
|
|
/* Put the rest of the line in the query buffer. */
|
4431 |
|
|
$subline = substr($line, $query_start);
|
4432 |
|
|
if ($in_quote || $dol_quote || strspn($subline, " \t\n\r") != strlen($subline))
|
4433 |
|
|
{
|
4434 |
|
|
if (strlen($query_buf) > 0)
|
4435 |
|
|
$query_buf .= "\n";
|
4436 |
|
|
$query_buf .= $subline;
|
4437 |
|
|
}
|
4438 |
|
|
|
4439 |
|
|
$line = null;
|
4440 |
|
|
|
4441 |
|
|
} // end while
|
4442 |
|
|
|
4443 |
|
|
/*
|
4444 |
|
|
* Process query at the end of file without a semicolon, so long as
|
4445 |
|
|
* it's non-empty.
|
4446 |
|
|
*/
|
4447 |
|
|
if (strlen($query_buf) > 0 && strspn($query_buf, " \t\n\r") != strlen($query_buf))
|
4448 |
|
|
{
|
4449 |
|
|
// Execute the query (supporting 4.1.x PHP...)
|
4450 |
|
|
if (function_exists('pg_query'))
|
4451 |
|
|
$res = @pg_query($conn, $query_buf);
|
4452 |
|
|
else
|
4453 |
|
|
$res = @pg_exec($conn, $query_buf);
|
4454 |
|
|
// Call the callback function for display
|
4455 |
|
|
if ($callback !== null) $callback($query_buf, $res, $lineno);
|
4456 |
|
|
// Check for COPY request
|
4457 |
|
|
if (pg_result_status($res) == 4) { // 4 == PGSQL_COPY_FROM
|
4458 |
|
|
while (!feof($fd)) {
|
4459 |
|
|
$copy = fgets($fd, 32768);
|
4460 |
|
|
$lineno++;
|
4461 |
|
|
pg_put_line($conn, $copy);
|
4462 |
|
|
if ($copy == "\\.\n" || $copy == "\\.\r\n") {
|
4463 |
|
|
pg_end_copy($conn);
|
4464 |
|
|
break;
|
4465 |
|
|
}
|
4466 |
|
|
}
|
4467 |
|
|
}
|
4468 |
|
|
}
|
4469 |
|
|
|
4470 |
|
|
fclose($fd);
|
4471 |
|
|
|
4472 |
|
|
return true;
|
4473 |
|
|
}
|
4474 |
|
|
|
4475 |
|
|
// Capabilities
|
4476 |
|
|
function hasAlterDatabaseOwner() { return false; }
|
4477 |
|
|
function hasAlterDatabaseRename() { return false; }
|
4478 |
|
|
function hasAlterDatabase() { return $this->hasAlterDatabaseRename(); }
|
4479 |
|
|
function hasSchemas() { return false; }
|
4480 |
|
|
function hasConversions() { return false; }
|
4481 |
|
|
function hasGrantOption() { return false; }
|
4482 |
|
|
function hasIsClustered() { return false; }
|
4483 |
|
|
function hasDropBehavior() { return false; }
|
4484 |
|
|
function hasDropColumn() { return false; }
|
4485 |
|
|
function hasDomains() { return false; }
|
4486 |
|
|
function hasDomainConstraints() { return false; }
|
4487 |
|
|
function hasAlterTrigger() { return false; }
|
4488 |
|
|
function hasWithoutOIDs() { return false; }
|
4489 |
|
|
function hasAlterTableOwner() { return false; }
|
4490 |
|
|
function hasPartialIndexes() { return false; }
|
4491 |
|
|
function hasCasts() { return false; }
|
4492 |
|
|
function hasFullSubqueries() { return false; }
|
4493 |
|
|
function hasPrepare() { return false; }
|
4494 |
|
|
function hasProcesses() { return false; }
|
4495 |
|
|
function hasVariables() { return false; }
|
4496 |
|
|
function hasFullExplain() { return false; }
|
4497 |
|
|
function hasStatsCollector() { return false; }
|
4498 |
|
|
function hasAlterColumnType() { return false; }
|
4499 |
|
|
function hasUserSessionDefaults() { return false; }
|
4500 |
|
|
function hasUserRename() { return false; }
|
4501 |
|
|
function hasRecluster() { return false; }
|
4502 |
|
|
function hasFullVacuum() { return false; }
|
4503 |
|
|
function hasForeignKeysInfo() { return false; }
|
4504 |
|
|
function hasViewColumnRename() { return false; }
|
4505 |
|
|
function hasTablespaces() { return false; }
|
4506 |
|
|
function hasSignals() { return false; }
|
4507 |
|
|
function hasNamedParams() { return false; }
|
4508 |
|
|
function hasUserAndDbVariables() { return false; }
|
4509 |
|
|
function hasCompositeTypes() { return false; }
|
4510 |
|
|
function hasReadOnlyQueries() { return false; }
|
4511 |
|
|
function hasFuncPrivs() { return false; }
|
4512 |
|
|
|
4513 |
|
|
}
|
4514 |
|
|
|
4515 |
|
|
?>
|