mirror of
https://github.com/vrana/adminer.git
synced 2025-08-05 06:07:57 +02:00
PostgreSQL: Partial Indexes
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
- MySQL, PostgreSQL: Support index algorithms (bug #1030)
|
- MySQL, PostgreSQL: Support index algorithms (bug #1030)
|
||||||
- PostgreSQL, CockroachDB: Creating partitioned tables (bug #1031)
|
- PostgreSQL, CockroachDB: Creating partitioned tables (bug #1031)
|
||||||
- PostgreSQL: Move partitioned tables from table list to parent table
|
- PostgreSQL: Move partitioned tables from table list to parent table
|
||||||
|
- PostgreSQL: Support partial indices (bug #1048)
|
||||||
- PostgreSQL: Support calling functions returning table (bug #1040)
|
- PostgreSQL: Support calling functions returning table (bug #1040)
|
||||||
- Designs: adminer.css with 'prefers-color-scheme: dark' doesn't disable dark mode
|
- Designs: adminer.css with 'prefers-color-scheme: dark' doesn't disable dark mode
|
||||||
- Plugins: Method bodyClass() to add <body class>
|
- Plugins: Method bodyClass() to add <body class>
|
||||||
|
@@ -754,7 +754,7 @@ if (!defined('Adminer\DRIVER')) {
|
|||||||
|
|
||||||
/** Run commands to alter indexes
|
/** Run commands to alter indexes
|
||||||
* @param string $table escaped table name
|
* @param string $table escaped table name
|
||||||
* @param list<array{string, string, 'DROP'|list<string>, 3?: string}> $alter of ["index type", "name", ["column definition", ...], "algorithm"] or ["index type", "name", "DROP"]
|
* @param list<array{string, string, 'DROP'|list<string>, 3?: string, 4?: string}> $alter of ["index type", "name", ["column definition", ...], "algorithm", "condition"] or ["index type", "name", "DROP"]
|
||||||
* @return Result|bool
|
* @return Result|bool
|
||||||
*/
|
*/
|
||||||
function alter_indexes(string $table, $alter) {
|
function alter_indexes(string $table, $alter) {
|
||||||
|
@@ -529,7 +529,7 @@ ORDER BY a.attnum") as $row
|
|||||||
$table_oid = driver()->tableOid($table);
|
$table_oid = driver()->tableOid($table);
|
||||||
$columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2);
|
$columns = get_key_vals("SELECT attnum, attname FROM pg_attribute WHERE attrelid = $table_oid AND attnum > 0", $connection2);
|
||||||
foreach (
|
foreach (
|
||||||
get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial, pg_am.amname as algorithm
|
get_rows("SELECT relname, indisunique::int, indisprimary::int, indkey, indoption, (indpred IS NOT NULL)::int as indispartial, pg_am.amname as algorithm, pg_get_expr(pg_index.indpred, pg_index.indrelid, true) AS partial
|
||||||
FROM pg_index
|
FROM pg_index
|
||||||
JOIN pg_class ON indexrelid = oid
|
JOIN pg_class ON indexrelid = oid
|
||||||
JOIN pg_am ON pg_am.oid = pg_class.relam
|
JOIN pg_am ON pg_am.oid = pg_class.relam
|
||||||
@@ -541,6 +541,7 @@ ORDER BY indisprimary DESC, indisunique DESC", $connection2) as $row
|
|||||||
$return[$relname]["columns"] = array();
|
$return[$relname]["columns"] = array();
|
||||||
$return[$relname]["descs"] = array();
|
$return[$relname]["descs"] = array();
|
||||||
$return[$relname]["algorithm"] = $row["algorithm"];
|
$return[$relname]["algorithm"] = $row["algorithm"];
|
||||||
|
$return[$relname]["partial"] = $row["partial"];
|
||||||
if ($row["indkey"]) {
|
if ($row["indkey"]) {
|
||||||
foreach (explode(" ", $row["indkey"]) as $indkey) {
|
foreach (explode(" ", $row["indkey"]) as $indkey) {
|
||||||
$return[$relname]["columns"][] = $columns[$indkey];
|
$return[$relname]["columns"][] = $columns[$indkey];
|
||||||
@@ -721,7 +722,12 @@ ORDER BY conkey, conname") as $row
|
|||||||
} elseif ($val[2] == "DROP") {
|
} elseif ($val[2] == "DROP") {
|
||||||
$drop[] = idf_escape($val[1]);
|
$drop[] = idf_escape($val[1]);
|
||||||
} else {
|
} else {
|
||||||
$queries[] = "CREATE INDEX " . idf_escape($val[1] != "" ? $val[1] : uniqid($table . "_")) . " ON " . table($table) . ($val[3] ? " USING $val[3]" : "") . " (" . implode(", ", $val[2]) . ")";
|
$queries[] = "CREATE INDEX " . idf_escape($val[1] != "" ? $val[1] : uniqid($table . "_"))
|
||||||
|
. " ON " . table($table)
|
||||||
|
. ($val[3] ? " USING $val[3]" : "")
|
||||||
|
. " (" . implode(", ", $val[2]) . ")"
|
||||||
|
. ($val[4] ? " WHERE $val[4]" : "")
|
||||||
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($create) {
|
if ($create) {
|
||||||
@@ -1028,9 +1034,11 @@ AND typelem = 0"
|
|||||||
}
|
}
|
||||||
|
|
||||||
function support($feature) {
|
function support($feature) {
|
||||||
return preg_match('~^(check|database|table|columns|sql|indexes|descidx|comment|view|' . (min_version(9.3) ? 'materializedview|' : '') . 'scheme|' . (min_version(11) ? 'procedure|' : '') . 'routine|sequence|trigger|type|variables|drop_col'
|
return preg_match('~^(check|columns|comment|database|drop_col|dump|descidx|indexes|kill|partial_indexes|routine|scheme|sequence|sql|table|trigger|type|variables|view'
|
||||||
|
. (min_version(9.3) ? '|materializedview' : '')
|
||||||
|
. (min_version(11) ? '|procedure' : '')
|
||||||
. (connection()->flavor == 'cockroach' ? '' : '|processlist') // https://github.com/cockroachdb/cockroach/issues/24745
|
. (connection()->flavor == 'cockroach' ? '' : '|processlist') // https://github.com/cockroachdb/cockroach/issues/24745
|
||||||
. '|kill|dump)$~', $feature)
|
. ')$~', $feature)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -355,6 +355,10 @@ class Adminer {
|
|||||||
* @param TableStatus $tableStatus
|
* @param TableStatus $tableStatus
|
||||||
*/
|
*/
|
||||||
function tableIndexesPrint(array $indexes, array $tableStatus): void {
|
function tableIndexesPrint(array $indexes, array $tableStatus): void {
|
||||||
|
$partial = false;
|
||||||
|
foreach ($indexes as $name => $index) {
|
||||||
|
$partial |= !!$index["partial"];
|
||||||
|
}
|
||||||
echo "<table>\n";
|
echo "<table>\n";
|
||||||
$default_algorithm = first(driver()->indexAlgorithms($tableStatus));
|
$default_algorithm = first(driver()->indexAlgorithms($tableStatus));
|
||||||
foreach ($indexes as $name => $index) {
|
foreach ($indexes as $name => $index) {
|
||||||
@@ -370,6 +374,9 @@ class Adminer {
|
|||||||
echo "<tr title='" . h($name) . "'>";
|
echo "<tr title='" . h($name) . "'>";
|
||||||
echo "<th>$index[type]" . ($default_algorithm && $index['algorithm'] != $default_algorithm ? " ($index[algorithm])" : "");
|
echo "<th>$index[type]" . ($default_algorithm && $index['algorithm'] != $default_algorithm ? " ($index[algorithm])" : "");
|
||||||
echo "<td>" . implode(", ", $print);
|
echo "<td>" . implode(", ", $print);
|
||||||
|
if ($partial) {
|
||||||
|
echo "<td>" . ($index['partial'] ? "<code class='jush-" . JUSH . "'>WHERE " . h($index['partial']) : "");
|
||||||
|
}
|
||||||
echo "\n";
|
echo "\n";
|
||||||
}
|
}
|
||||||
echo "</table>\n";
|
echo "</table>\n";
|
||||||
|
@@ -30,6 +30,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
|
|||||||
$columns = array();
|
$columns = array();
|
||||||
$lengths = array();
|
$lengths = array();
|
||||||
$descs = array();
|
$descs = array();
|
||||||
|
$index_condition = (support("partial_indexes") ? $index["partial"] : "");
|
||||||
$index_algorithm = (in_array($index["algorithm"], $index_algorithms) ? $index["algorithm"] : "");
|
$index_algorithm = (in_array($index["algorithm"], $index_algorithms) ? $index["algorithm"] : "");
|
||||||
$set = array();
|
$set = array();
|
||||||
ksort($index["columns"]);
|
ksort($index["columns"]);
|
||||||
@@ -54,6 +55,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
|
|||||||
&& array_values($existing["columns"]) === $columns
|
&& array_values($existing["columns"]) === $columns
|
||||||
&& (!$existing["lengths"] || array_values($existing["lengths"]) === $lengths)
|
&& (!$existing["lengths"] || array_values($existing["lengths"]) === $lengths)
|
||||||
&& array_values($existing["descs"]) === $descs
|
&& array_values($existing["descs"]) === $descs
|
||||||
|
&& $existing["partial"] == $index_condition
|
||||||
&& (!$index_algorithms || $existing["algorithm"] == $index_algorithm)
|
&& (!$index_algorithms || $existing["algorithm"] == $index_algorithm)
|
||||||
) {
|
) {
|
||||||
// skip existing index
|
// skip existing index
|
||||||
@@ -62,7 +64,7 @@ if ($_POST && !$error && !$_POST["add"] && !$_POST["drop_col"]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($columns) {
|
if ($columns) {
|
||||||
$alter[] = array($index["type"], $name, $set, $index_algorithm);
|
$alter[] = array($index["type"], $name, $set, $index_algorithm, $index_condition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,6 +123,11 @@ if ($lengths || support("descidx")) {
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<th id="label-name"><?php echo lang('Name'); ?>
|
<th id="label-name"><?php echo lang('Name'); ?>
|
||||||
|
<?php
|
||||||
|
if (support("partial_indexes")) {
|
||||||
|
echo "<th id='label-condition' class='idxopts" . ($show_options ? "" : " hidden") . "'>" . lang('Condition');
|
||||||
|
}
|
||||||
|
?>
|
||||||
<th><noscript><?php echo icon("plus", "add[0]", "+", lang('Add next')); ?></noscript>
|
<th><noscript><?php echo icon("plus", "add[0]", "+", lang('Add next')); ?></noscript>
|
||||||
</thead>
|
</thead>
|
||||||
<?php
|
<?php
|
||||||
@@ -159,6 +166,9 @@ foreach ($row["indexes"] as $index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
echo "<td><input name='indexes[$j][name]' value='" . h($index["name"]) . "' autocapitalize='off' aria-labelledby='label-name'>\n";
|
echo "<td><input name='indexes[$j][name]' value='" . h($index["name"]) . "' autocapitalize='off' aria-labelledby='label-name'>\n";
|
||||||
|
if (support("partial_indexes")) {
|
||||||
|
echo "<td class='idxopts" . ($show_options ? "" : " hidden") . "'><input name='indexes[$j][partial]' value='" . h($index["partial"]) . "' autocapitalize='off' aria-labelledby='label-condition'>\n";
|
||||||
|
}
|
||||||
echo "<td>" . icon("cross", "drop_col[$j]", "x", lang('Remove')) . script("qsl('button').onclick = partial(editingRemoveRow, 'indexes\$1[type]');");
|
echo "<td>" . icon("cross", "drop_col[$j]", "x", lang('Remove')) . script("qsl('button').onclick = partial(editingRemoveRow, 'indexes\$1[type]');");
|
||||||
}
|
}
|
||||||
$j++;
|
$j++;
|
||||||
|
@@ -211,6 +211,7 @@ Lang::$translations = array(
|
|||||||
'Index Type' => 'Typ indexu',
|
'Index Type' => 'Typ indexu',
|
||||||
'length' => 'délka',
|
'length' => 'délka',
|
||||||
'Algorithm' => 'Algoritmus',
|
'Algorithm' => 'Algoritmus',
|
||||||
|
'Condition' => 'Podmínka',
|
||||||
|
|
||||||
'Foreign keys' => 'Cizí klíče',
|
'Foreign keys' => 'Cizí klíče',
|
||||||
'Foreign key' => 'Cizí klíč',
|
'Foreign key' => 'Cizí klíč',
|
||||||
|
@@ -213,6 +213,7 @@ Lang::$translations = array(
|
|||||||
'Index Type' => 'Xx',
|
'Index Type' => 'Xx',
|
||||||
'length' => 'xx',
|
'length' => 'xx',
|
||||||
'Algorithm' => 'Xx',
|
'Algorithm' => 'Xx',
|
||||||
|
'Condition' => 'Xx',
|
||||||
|
|
||||||
'Foreign keys' => 'Xx',
|
'Foreign keys' => 'Xx',
|
||||||
'Foreign key' => 'Xx',
|
'Foreign key' => 'Xx',
|
||||||
|
@@ -65,7 +65,7 @@ parameters:
|
|||||||
Field: "array{field?:string, full_type:string, type:string, length:numeric-string, unsigned:string, default?:string, null:bool, auto_increment:bool, collation:string, privileges:int[], comment:string, primary:bool, generated:string, orig?:string, on_update?:string, on_delete?:string, default_constraint?: string}"
|
Field: "array{field?:string, full_type:string, type:string, length:numeric-string, unsigned:string, default?:string, null:bool, auto_increment:bool, collation:string, privileges:int[], comment:string, primary:bool, generated:string, orig?:string, on_update?:string, on_delete?:string, default_constraint?: string}"
|
||||||
FieldType: "array{type:string, length:numeric-string, unsigned:string, collation:string}" # subset of RoutineField and Field
|
FieldType: "array{type:string, length:numeric-string, unsigned:string, collation:string}" # subset of RoutineField and Field
|
||||||
RoutineField: "array{field:string, type:string, length:numeric-string, unsigned:string, null:bool, full_type:string, collation:string, inout?:string}"
|
RoutineField: "array{field:string, type:string, length:numeric-string, unsigned:string, null:bool, full_type:string, collation:string, inout?:string}"
|
||||||
Index: "array{type:string, columns:list<string>, lengths:list<numeric-string>, descs:list<?bool>, algorithm?:string}"
|
Index: "array{type:string, columns:list<string>, lengths:list<numeric-string>, descs:list<?bool>, algorithm?:string, partial?:string}"
|
||||||
ForeignKey: "array{db?:string, ns?:string, table:string, source:list<string>, target:list<?string>, on_delete:string, on_update?:string, definition?:string, deferrable?:string}"
|
ForeignKey: "array{db?:string, ns?:string, table:string, source:list<string>, target:list<?string>, on_delete:string, on_update?:string, definition?:string, deferrable?:string}"
|
||||||
Trigger: "array{Trigger?:string, Timing?:string, Event?:string, Of?:string, Type?:string, Statement?:string}"
|
Trigger: "array{Trigger?:string, Timing?:string, Event?:string, Of?:string, Type?:string, Statement?:string}"
|
||||||
Routine: "array{name?:string, fields:list<RoutineField>, comment:string, returns?:FieldType, definition:string, language?:string}"
|
Routine: "array{name?:string, fields:list<RoutineField>, comment:string, returns?:FieldType, definition:string, language?:string}"
|
||||||
|
@@ -31,6 +31,9 @@
|
|||||||
<tr><td>type</td><td>fields[1.1][field]</td><td>name</td></tr>
|
<tr><td>type</td><td>fields[1.1][field]</td><td>name</td></tr>
|
||||||
<tr><td>select</td><td>fields[1.1][type]</td><td>label=character varying</td></tr>
|
<tr><td>select</td><td>fields[1.1][type]</td><td>label=character varying</td></tr>
|
||||||
<tr><td>type</td><td>fields[1.1][length]</td><td>50</td></tr>
|
<tr><td>type</td><td>fields[1.1][length]</td><td>50</td></tr>
|
||||||
|
<tr><td>type</td><td>fields[1.11][field]</td><td>surname</td></tr>
|
||||||
|
<tr><td>select</td><td>fields[1.11][type]</td><td>label=character varying</td></tr>
|
||||||
|
<tr><td>type</td><td>fields[1.11][length]</td><td>50</td></tr>
|
||||||
<tr><td>uncheck</td><td>name=comments</td><td></td></tr>
|
<tr><td>uncheck</td><td>name=comments</td><td></td></tr>
|
||||||
<tr><td>clickAndWait</td><td>name=comments</td><td></td></tr>
|
<tr><td>clickAndWait</td><td>name=comments</td><td></td></tr>
|
||||||
<tr><td>type</td><td>fields[1.1][comment]</td><td>Interpret</td></tr>
|
<tr><td>type</td><td>fields[1.1][comment]</td><td>Interpret</td></tr>
|
||||||
@@ -50,8 +53,16 @@
|
|||||||
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
||||||
<tr><td>verifyTextPresent</td><td>multiple primary keys for table "interprets" are not allowed</td><td></td></tr>
|
<tr><td>verifyTextPresent</td><td>multiple primary keys for table "interprets" are not allowed</td><td></td></tr>
|
||||||
<tr><td>select</td><td>indexes[2][type]</td><td>label=INDEX</td></tr>
|
<tr><td>select</td><td>indexes[2][type]</td><td>label=INDEX</td></tr>
|
||||||
|
<tr><td>click</td><td>//input[@name='options']</td><td></td></tr>
|
||||||
|
<tr><td>select</td><td>indexes[3][type]</td><td>label=INDEX</td></tr>
|
||||||
|
<tr><td>select</td><td>indexes[3][columns][1]</td><td>label=surname</td></tr>
|
||||||
|
<tr><td>select</td><td>indexes[3][algorithm]</td><td>label=hash</td></tr>
|
||||||
|
<tr><td>type</td><td>indexes[3][partial]</td><td>surname IS NOT NULL</td></tr>
|
||||||
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
<tr><td>clickAndWait</td><td>//input[@value='Save']</td><td></td></tr>
|
||||||
<tr><td>verifyTextPresent</td><td>Indexes have been altered.</td><td></td></tr>
|
<tr><td>verifyTextPresent</td><td>Indexes have been altered.</td><td></td></tr>
|
||||||
|
<tr><td>verifyTextPresent</td><td>//tr[@title='interprets_surname']</td><td>INDEX (hash)</td></tr>
|
||||||
|
<tr><td>verifyTextPresent</td><td>//tr[@title='interprets_surname']</td><td>surname</td></tr>
|
||||||
|
<tr><td>verifyTextPresent</td><td>//tr[@title='interprets_surname']</td><td>WHERE surname IS NOT NULL</td></tr>
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
|
|
||||||
<table cellpadding="1" cellspacing="1" border="1">
|
<table cellpadding="1" cellspacing="1" border="1">
|
||||||
|
Reference in New Issue
Block a user