HuasoFoundries /
phpPgAdmin6
| 1 | <?php |
||
| 2 | |||
|
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||
| 3 | /* |
||
| 4 | * PHPPgAdmin v6.0.0-beta.30 |
||
| 5 | */ |
||
| 6 | |||
| 7 | namespace PHPPgAdmin\Controller; |
||
| 8 | |||
| 9 | /** |
||
| 10 | * Base controller class. |
||
| 11 | */ |
||
| 12 | class SqlController extends BaseController |
||
| 13 | { |
||
| 14 | public $controller_name = 'SqlController'; |
||
| 15 | public $query = ''; |
||
| 16 | public $subject = ''; |
||
| 17 | public $start_time; |
||
| 18 | public $duration; |
||
| 19 | |||
| 20 | /** |
||
| 21 | * Default method to render the controller according to the action parameter. |
||
| 22 | */ |
||
| 23 | public function render() |
||
| 24 | { |
||
| 25 | $conf = $this->conf; |
||
| 26 | $lang = $this->lang; |
||
| 27 | |||
| 28 | $data = $this->misc->getDatabaseAccessor(); |
||
| 29 | |||
| 30 | set_time_limit(0); |
||
| 31 | |||
| 32 | // We need to store the query in a session for editing purposes |
||
| 33 | // We avoid GPC vars to avoid truncating long queries |
||
| 34 | if (isset($_REQUEST['subject']) && 'history' == $_REQUEST['subject']) { |
||
| 35 | // Or maybe we came from the history popup |
||
| 36 | $_SESSION['sqlquery'] = $_SESSION['history'][$_REQUEST['server']][$_REQUEST['database']][$_GET['queryid']]['query']; |
||
| 37 | $this->query = $_SESSION['sqlquery']; |
||
| 38 | } elseif (isset($_POST['query'])) { |
||
| 39 | // Or maybe we came from an sql form |
||
| 40 | $_SESSION['sqlquery'] = $_POST['query']; |
||
| 41 | $this->query = $_SESSION['sqlquery']; |
||
| 42 | } else { |
||
| 43 | echo 'could not find the query!!'; |
||
| 44 | } |
||
| 45 | |||
| 46 | // Pagination maybe set by a get link that has it as FALSE, |
||
| 47 | // if that's the case, unset the variable. |
||
| 48 | if (isset($_REQUEST['paginate']) && 'f' == $_REQUEST['paginate']) { |
||
| 49 | unset($_REQUEST['paginate'], $_POST['paginate'], $_GET['paginate']); |
||
| 50 | } |
||
| 51 | |||
| 52 | if (isset($_REQUEST['subject'])) { |
||
| 53 | $this->subject = $_REQUEST['subject']; |
||
| 54 | } |
||
| 55 | |||
| 56 | // Check to see if pagination has been specified. In that case, send to display |
||
| 57 | // script for pagination |
||
| 58 | // if a file is given or the request is an explain, do not paginate |
||
| 59 | if (isset($_REQUEST['paginate']) && !(isset($_FILES['script']) && $_FILES['script']['size'] > 0) && (0 == preg_match('/^\s*explain/i', $this->query))) { |
||
| 60 | //if (!(isset($_FILES['script']) && $_FILES['script']['size'] > 0)) { |
||
| 61 | |||
| 62 | $display_controller = new DisplayController($this->getContainer()); |
||
| 63 | |||
| 64 | return $display_controller->render(); |
||
| 65 | } |
||
| 66 | |||
| 67 | $this->printHeader($lang['strqueryresults'], null, true, 'header_sqledit.twig'); |
||
| 68 | $this->printBody(); |
||
| 69 | $this->printTrail('database'); |
||
| 70 | $this->printTitle($lang['strqueryresults']); |
||
| 71 | |||
| 72 | // Set the schema search path |
||
| 73 | if (isset($_REQUEST['search_path'])) { |
||
| 74 | if (0 != $data->setSearchPath(array_map('trim', explode(',', $_REQUEST['search_path'])))) { |
||
| 75 | return $this->printFooter(); |
||
| 76 | } |
||
| 77 | } |
||
| 78 | |||
| 79 | // May as well try to time the query |
||
| 80 | if (function_exists('microtime')) { |
||
| 81 | list($usec, $sec) = explode(' ', microtime()); |
||
| 82 | $this->start_time = ((float) $usec + (float) $sec); |
||
| 83 | } |
||
| 84 | |||
| 85 | $this->doDefault(); |
||
| 86 | |||
| 87 | $this->doFooter(true, 'footer_sqledit.twig'); |
||
| 88 | } |
||
| 89 | |||
| 90 | public function doDefault() |
||
| 91 | { |
||
| 92 | $conf = $this->conf; |
||
| 93 | $this->misc = $this->misc; |
||
| 94 | $lang = $this->lang; |
||
| 95 | $data = $this->misc->getDatabaseAccessor(); |
||
| 96 | $_connection = $this->misc->getConnection(); |
||
| 97 | |||
| 98 | try { |
||
| 99 | // Execute the query. If it's a script upload, special handling is necessary |
||
| 100 | if (isset($_FILES['script']) && $_FILES['script']['size'] > 0) { |
||
| 101 | return $this->execute_script(); |
||
| 102 | } |
||
| 103 | |||
| 104 | return $this->execute_query(); |
||
| 105 | } catch (\PHPPgAdmin\ADOdbException $e) { |
||
| 106 | $message = $e->getMessage(); |
||
| 107 | $trace = $e->getTraceAsString(); |
||
| 108 | $lastError = $_connection->getLastError(); |
||
| 109 | $this->prtrace(['message' => $message, 'trace' => $trace, 'lastError' => $lastError]); |
||
| 110 | |||
| 111 | return null; |
||
| 112 | } |
||
| 113 | } |
||
| 114 | |||
| 115 | private function execute_script() |
||
| 116 | { |
||
| 117 | $conf = $this->conf; |
||
| 118 | $misc = $this->misc; |
||
| 119 | $lang = $this->lang; |
||
| 120 | $data = $this->misc->getDatabaseAccessor(); |
||
| 121 | $_connection = $this->misc->getConnection(); |
||
| 122 | |||
| 123 | /** |
||
| 124 | * This is a callback function to display the result of each separate query. |
||
| 125 | * |
||
| 126 | * @param ADORecordSet $rs The recordset returned by the script execetor |
||
| 127 | */ |
||
| 128 | $sqlCallback = function ($query, $rs, $lineno) use ($data, $misc, $lang, $_connection) { |
||
| 129 | // Check if $rs is false, if so then there was a fatal error |
||
| 130 | if (false === $rs) { |
||
| 131 | echo htmlspecialchars($_FILES['script']['name']), ':', $lineno, ': ', nl2br(htmlspecialchars($_connection->getLastError())), "<br/>\n"; |
||
| 132 | } else { |
||
| 133 | // Print query results |
||
| 134 | switch (pg_result_status($rs)) { |
||
| 135 | case PGSQL_TUPLES_OK: |
||
| 136 | // If rows returned, then display the results |
||
| 137 | $num_fields = pg_numfields($rs); |
||
| 138 | echo "<p><table>\n<tr>"; |
||
| 139 | for ($k = 0; $k < $num_fields; ++$k) { |
||
| 140 | echo '<th class="data">', $misc->printVal(pg_fieldname($rs, $k)), '</th>'; |
||
| 141 | } |
||
| 142 | |||
| 143 | $i = 0; |
||
| 144 | $row = pg_fetch_row($rs); |
||
| 145 | while (false !== $row) { |
||
| 146 | $id = (0 == ($i % 2) ? '1' : '2'); |
||
| 147 | echo "<tr class=\"data{$id}\">\n"; |
||
| 148 | foreach ($row as $k => $v) { |
||
| 149 | echo '<td style="white-space:nowrap;">', $misc->printVal($v, pg_fieldtype($rs, $k), ['null' => true]), '</td>'; |
||
| 150 | } |
||
| 151 | echo "</tr>\n"; |
||
| 152 | $row = pg_fetch_row($rs); |
||
| 153 | ++$i; |
||
| 154 | } |
||
| 155 | |||
| 156 | echo "</table><br/>\n"; |
||
| 157 | echo $i, " {$lang['strrows']}</p>\n"; |
||
| 158 | |||
| 159 | break; |
||
| 160 | case PGSQL_COMMAND_OK: |
||
| 161 | // If we have the command completion tag |
||
| 162 | if (version_compare(phpversion(), '4.3', '>=')) { |
||
| 163 | echo htmlspecialchars(pg_result_status($rs, PGSQL_STATUS_STRING)), "<br/>\n"; |
||
| 164 | } |
||
| 165 | // Otherwise if any rows have been affected |
||
| 166 | elseif ($data->conn->Affected_Rows() > 0) { |
||
| 167 | echo $data->conn->Affected_Rows(), " {$lang['strrowsaff']}<br/>\n"; |
||
| 168 | } |
||
| 169 | // Otherwise output nothing... |
||
| 170 | break; |
||
| 171 | case PGSQL_EMPTY_QUERY: |
||
| 172 | break; |
||
| 173 | default: |
||
| 174 | break; |
||
| 175 | } |
||
| 176 | } |
||
| 177 | }; |
||
| 178 | |||
| 179 | return $data->executeScript('script', $sqlCallback); |
||
| 180 | } |
||
| 181 | |||
| 182 | private function execute_query() |
||
| 183 | { |
||
| 184 | $conf = $this->conf; |
||
| 185 | |||
| 186 | $lang = $this->lang; |
||
| 187 | $data = $this->misc->getDatabaseAccessor(); |
||
| 188 | |||
| 189 | // Set fetch mode to NUM so that duplicate field names are properly returned |
||
| 190 | $data->conn->setFetchMode(ADODB_FETCH_NUM); |
||
| 191 | |||
| 192 | $rs = $data->conn->Execute($this->query); |
||
| 193 | |||
| 194 | echo '<form method="post" id="sqlform" action="'.$_SERVER['REQUEST_URI'].'">'; |
||
| 195 | echo '<textarea width="90%" name="query" id="query" rows="5" cols="100" resizable="true">'; |
||
| 196 | |||
| 197 | echo htmlspecialchars($this->query); |
||
| 198 | echo '</textarea><br>'; |
||
| 199 | echo $this->misc->setForm(); |
||
| 200 | echo '<input type="submit"/></form>'; |
||
| 201 | |||
| 202 | // $rs will only be an object if there is no error |
||
| 203 | if (is_object($rs)) { |
||
| 204 | // Request was run, saving it in history |
||
| 205 | if (!isset($_REQUEST['nohistory'])) { |
||
| 206 | $this->misc->saveScriptHistory($this->query); |
||
| 207 | } |
||
| 208 | |||
| 209 | // Now, depending on what happened do various things |
||
| 210 | |||
| 211 | // First, if rows returned, then display the results |
||
| 212 | if ($rs->recordCount() > 0) { |
||
| 213 | echo "<table>\n<tr>"; |
||
| 214 | foreach ($rs->fields as $k => $v) { |
||
| 215 | $finfo = $rs->fetchField($k); |
||
| 216 | echo '<th class="data">', $this->misc->printVal($finfo->name), '</th>'; |
||
| 217 | } |
||
| 218 | echo "</tr>\n"; |
||
| 219 | $i = 0; |
||
| 220 | while (!$rs->EOF) { |
||
| 221 | $id = (0 == ($i % 2) ? '1' : '2'); |
||
| 222 | echo "<tr class=\"data{$id}\">\n"; |
||
| 223 | foreach ($rs->fields as $k => $v) { |
||
| 224 | $finfo = $rs->fetchField($k); |
||
| 225 | echo '<td style="white-space:nowrap;">', $this->misc->printVal($v, $finfo->type, ['null' => true]), '</td>'; |
||
| 226 | } |
||
| 227 | echo "</tr>\n"; |
||
| 228 | $rs->moveNext(); |
||
| 229 | ++$i; |
||
| 230 | } |
||
| 231 | echo "</table>\n"; |
||
| 232 | echo '<p>', $rs->recordCount(), " {$lang['strrows']}</p>\n"; |
||
| 233 | } elseif ($data->conn->Affected_Rows() > 0) { |
||
| 234 | // Otherwise if any rows have been affected |
||
| 235 | echo '<p>', $data->conn->Affected_Rows(), " {$lang['strrowsaff']}</p>\n"; |
||
| 236 | } else { |
||
| 237 | // Otherwise nodata to print |
||
| 238 | echo '<p>', $lang['strnodata'], "</p>\n"; |
||
| 239 | } |
||
| 240 | } |
||
| 241 | } |
||
| 242 | |||
| 243 | private function doFooter($doBody = true, $template = 'footer.twig') |
||
| 244 | { |
||
| 245 | $conf = $this->conf; |
||
| 246 | |||
| 247 | $lang = $this->lang; |
||
| 248 | $data = $this->misc->getDatabaseAccessor(); |
||
| 249 | |||
| 250 | // May as well try to time the query |
||
| 251 | if (null !== $this->start_time) { |
||
| 252 | list($usec, $sec) = explode(' ', microtime()); |
||
| 253 | $end_time = ((float) $usec + (float) $sec); |
||
| 254 | // Get duration in milliseconds, round to 3dp's |
||
| 255 | $this->duration = number_format(($end_time - $this->start_time) * 1000, 3); |
||
| 256 | } |
||
| 257 | |||
| 258 | // Reload the browser as we may have made schema changes |
||
| 259 | $this->misc->setReloadBrowser(true); |
||
| 260 | |||
| 261 | // Display duration if we know it |
||
| 262 | if (null !== $this->duration) { |
||
| 263 | echo '<p>', sprintf($lang['strruntime'], $this->duration), "</p>\n"; |
||
| 264 | } |
||
| 265 | |||
| 266 | echo "<p>{$lang['strsqlexecuted']}</p>\n"; |
||
| 267 | |||
| 268 | $navlinks = []; |
||
| 269 | $fields = [ |
||
| 270 | 'server' => $_REQUEST['server'], |
||
| 271 | 'database' => $_REQUEST['database'], |
||
| 272 | ]; |
||
| 273 | |||
| 274 | if (isset($_REQUEST['schema'])) { |
||
| 275 | $fields['schema'] = $_REQUEST['schema']; |
||
| 276 | } |
||
| 277 | |||
| 278 | // Return |
||
| 279 | if (isset($_REQUEST['return'])) { |
||
| 280 | $urlvars = $this->misc->getSubjectParams($_REQUEST['return']); |
||
| 281 | $navlinks['back'] = [ |
||
| 282 | 'attr' => [ |
||
| 283 | 'href' => [ |
||
| 284 | 'url' => $urlvars['url'], |
||
| 285 | 'urlvars' => $urlvars['params'], |
||
| 286 | ], |
||
| 287 | ], |
||
| 288 | 'content' => $lang['strback'], |
||
| 289 | ]; |
||
| 290 | } |
||
| 291 | |||
| 292 | // Edit |
||
| 293 | $navlinks['alter'] = [ |
||
| 294 | 'attr' => [ |
||
| 295 | 'href' => [ |
||
| 296 | 'url' => 'database.php', |
||
| 297 | 'urlvars' => array_merge($fields, [ |
||
| 298 | 'action' => 'sql', |
||
| 299 | ]), |
||
| 300 | ], |
||
| 301 | ], |
||
| 302 | 'content' => $lang['streditsql'], |
||
| 303 | ]; |
||
| 304 | |||
| 305 | // Create view and download |
||
| 306 | if ('' !== $this->query && isset($rs) && is_object($rs) && $rs->recordCount() > 0) { |
||
| 307 | // Report views don't set a schema, so we need to disable create view in that case |
||
| 308 | if (isset($_REQUEST['schema'])) { |
||
| 309 | $navlinks['createview'] = [ |
||
| 310 | 'attr' => [ |
||
| 311 | 'href' => [ |
||
| 312 | 'url' => 'views.php', |
||
| 313 | 'urlvars' => array_merge($fields, [ |
||
| 314 | 'action' => 'create', |
||
| 315 | ]), |
||
| 316 | ], |
||
| 317 | ], |
||
| 318 | 'content' => $lang['strcreateview'], |
||
| 319 | ]; |
||
| 320 | } |
||
| 321 | |||
| 322 | if (isset($_REQUEST['search_path'])) { |
||
| 323 | $fields['search_path'] = $_REQUEST['search_path']; |
||
| 324 | } |
||
| 325 | |||
| 326 | $navlinks['download'] = [ |
||
| 327 | 'attr' => [ |
||
| 328 | 'href' => [ |
||
| 329 | 'url' => 'dataexport.php', |
||
| 330 | 'urlvars' => $fields, |
||
| 331 | ], |
||
| 332 | ], |
||
| 333 | 'content' => $lang['strdownload'], |
||
| 334 | ]; |
||
| 335 | } |
||
| 336 | |||
| 337 | $this->printNavLinks($navlinks, 'sql-form', get_defined_vars()); |
||
| 338 | |||
| 339 | return $this->printFooter($doBody, $template); |
||
| 340 | } |
||
| 341 | } |
||
| 342 |