Browse Source

Initial-Final Commit

master
sheychen 3 years ago
commit
d9b752dc9e
  1. 5
      README.md
  2. 4
      config.php
  3. 12
      index.php
  4. 150
      model.php
  5. 31
      pages/connect.php
  6. 5
      pages/disconnect.php
  7. 2
      pages/fail.php
  8. 10
      pages/schema.php
  9. 7
      pages/schemas.php
  10. 13
      pages/sql.php
  11. 19
      pages/table.php
  12. 35
      pages/table_display.php
  13. 38
      pages/table_insert.php
  14. 10
      pages/tables.php
  15. 35
      router.php
  16. 99
      style.css

5
README.md

@ -0,0 +1,5 @@
# DB-Xplorer
### A minimal, unsafe and maybe usefull sql database explorer
Please don't use that *really*

4
config.php

@ -0,0 +1,4 @@
<?php return array(
'limit_size' => 10,
'display_length' => 50
);

12
index.php

@ -0,0 +1,12 @@
<?php
session_start();
$_page = isset($_GET['page']) ? htmlspecialchars($_GET['page']) : 'index';
$_schema = isset($_GET['schema']) ? htmlspecialchars($_GET['schema']) : null;
$_table = isset($_GET['table']) ? htmlspecialchars($_GET['table']) : null;
$_offset = isset($_GET['offset']) ? intval(htmlspecialchars($_GET['offset'])) : 0;
$config=include('config.php');
include('model.php');
include('router.php');
?>

150
model.php

@ -0,0 +1,150 @@
<?php
$_loginFields = array("db_string", "db_name", "db_user", "db_password");
$logable = true;
foreach ($_loginFields as $key) {
if(!isset($_SESSION[$key]))
$logable = false;
}
if($logable){
try {
$db = new PDO($_SESSION['db_string'].';dbname='.$_SESSION['db_name'],
$_SESSION['db_user'],
$_SESSION['db_password'],
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
));
} catch (PDOException $e) {
$_error = $e->getMessage();
$_page = 'fail';
}
}else{
if($_page != 'disconnect')
$_page = 'connect';
}
function getQuery($query){
global $db;
return $db->query($query)->fetchall();
}
function getPrepareQuery($query, $args){
global $db;
$sth = $db->prepare($query);
$sth->execute($args);
return $sth->fetchAll();
}
function getSchemasList(){
$schemas = array();
foreach (getQuery('SELECT schema_name FROM information_schema.schemata') as $schema) {
$schemas[] = $schema['schema_name'];
}
return $schemas;
}
function getTablesList(){
global $db;
return $db->query("SELECT table_schema, table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema')")->fetchall();
}
function getTablesListBySchema($schema){
$tables = array();
foreach (getPrepareQuery("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema = ?", array($schema)) as $table) {
$tables[] = $table['table_name'];
}
return $tables;
}
function getTableColumnsList($schema, $table){
return getPrepareQuery("SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_scale, datetime_precision FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ordinal_position", array($schema, $table));
}
function getTableColumnsNames($schema, $table){
$columns = array();
foreach (getPrepareQuery("SELECT column_name FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ordinal_position", array($schema, $table)) as $column) {
$columns[] = $column['column_name'];
}
return $columns;
}
function getTableContentList($schema, $table, $offset = 0){
global $config;
//Note: check schema and table before !!!
return getPrepareQuery("SELECT * FROM \"$schema\".\"$table\" LIMIT ".$config["limit_size"]." OFFSET ?", array($offset));
}
function getTableCount($schema, $table){
//Note: check schema and table before !!!
return getQuery("SELECT COUNT(*) count FROM \"$schema\".\"$table\"")[0]["count"];
}
function insertTableLine($schema, $table, $data){
//REALLY UNSAFE
//Note: check schema and table before !!!
global $db;
$sql = "INSERT INTO \"$schema\".\"$table\"(";
$first = key($data);
foreach ($data as $key => $value) {
if($key !== $first) $sql .= ", ";
$sql .= "\"$key\"";
}
$sql .= ") VALUES (";
foreach ($data as $key => $value) {
if($key !== $first) $sql .= ", ";
$sql .= $db->quote($value);
}
$sql .= ")";
return getQuery($sql);
}
//render
function printAssocTable($data, $fn){
$str = '<table>';
$str .= '<tr><th>'.implode('</th><th>', array_keys($data[0])).'</th></tr>';
foreach($data as $row){
$str .= '<tr>'.($fn != null ? fn($row) : '<td>'.implode('</td><td>', $row).'</td>').'</tr>';
}
return $str.'</table>';
}
function checkSchema($schema){
if($schema == null)
return '<p class="error">Any schema selected</p>';
if(!in_array($schema, getSchemasList()))
return '<p class="error">Unknown schema</p>';
return true;
}
function checkTable($schema, $table){
$check_schema = checkSchema($schema);
if($check_schema !== true)
return $check_schema;
if ($table == null)
return '<p class="error">Any base selected</p>';
if (!in_array($table, getTablesListBySchema($schema)))
return '<p class="error">Unknown base</p>';
return true;
}
function printCheck($check){
if($check !== true)
echo $check;
return $check === true;
}
function endsWith($haystack, $needle)
{
$length = strlen($needle);
return $length === 0 ||
(substr($haystack, -$length) === $needle);
}

31
pages/connect.php

@ -0,0 +1,31 @@
<?php
if(!empty($_POST)){
foreach ($_loginFields as $key) {
if(isset($_POST[$key]))
$_SESSION[$key] = $_POST[$key];
}
header("Location: .");
}
?>
<h2>Connect</h2>
<form action="." method="post">
<p>
<label for="string">String</label>
<input type="text" name="db_string" id="string" placeholder="pgsql:host=localhost">
</p>
<p>
<label for="name">Database</label>
<input type="text" name="db_name" id="name">
</p>
<p>
<label for="user">User</label>
<input type="text" name="db_user" id="user">
</p>
<p>
<label for="password">Password</label>
<input type="password" name="db_password" id="password">
</p>
<p>
<input type="submit" value="Go">
</p>
</form>

5
pages/disconnect.php

@ -0,0 +1,5 @@
<?php
foreach ($_loginFields as $key)
unset($_SESSION[$key]);
?>
<h2>Disconnected</h2>

2
pages/fail.php

@ -0,0 +1,2 @@
<h2 class="error">Connection failed: <?= $_error ?></h2>
<p><a href="?page=disconnect">Try reconnect</a></p>

10
pages/schema.php

@ -0,0 +1,10 @@
<h2>Schema</h2>
<?php if(printCheck(checkSchema($_schema))): ?>
<p><?= $_schema ?></p>
<table>
<tr><th>Table</th></tr>
<?php foreach(getTablesListBySchema($_schema) as $table):?>
<tr><td><a href="?page=table&schema=<?= $_schema ?>&table=<?= $table ?>"><?= $table ?></a></td></tr>
<?php endforeach ?>
</table>
<?php endif ?>

7
pages/schemas.php

@ -0,0 +1,7 @@
<h2>Schemas</h2>
<table>
<tr><th>Schema</th></tr>
<?php foreach(getSchemasList() as $schema):?>
<tr><td><a href="?page=schema&schema=<?= $schema ?>"><?= $schema ?></a></td></tr>
<?php endforeach ?>
</table>

13
pages/sql.php

@ -0,0 +1,13 @@
<h2>SQL</h2>
<?php if(isset($_POST['sql'])):
try{
echo printAssocTable(getQuery($_POST['sql']), null);
}catch(Exception $e){
echo "<p class=\"error\">".$e->getMessage()."</p>";
} ?>
<?php else: ?>
<form action="?page=sql" method="post">
<p><textarea name="sql" id="sql" cols="30" rows="10" placeholder="SELECT * FROM ..."></textarea></p>
<p><input type="submit" value="Run"></p>
</form>
<?php endif ?>

19
pages/table.php

@ -0,0 +1,19 @@
<h2>Table</h2>
<?php if(printCheck(checkTable($_schema, $_table))): ?>
<p><?= $_schema ?> : <?= $_table ?></p>
<table>
<tr><th>Column</th><th>Type</th><th>Nullable</th><th>Default</th></tr>
<?php foreach (getTableColumnsList($_schema, $_table) as $column): ?>
<tr>
<td><?= $column['column_name'] ?></td>
<td><?= $column['data_type'] //character_maximum_length, numeric_precision, numeric_scale, datetime_precision ?></td>
<td><?= $column['is_nullable'] ?></td>
<td><?= $column['column_default'] ?></td>
</tr>
<?php endforeach ?>
</table>
<ul class="nav">
<li><a href="?page=table_display&schema=<?= $_schema ?>&table=<?= $_table ?>">Display</a></li>
<li><a href="?page=table_insert&schema=<?= $_schema ?>&table=<?= $_table ?>">Insert</a></li>
</ul>
<?php endif ?>

35
pages/table_display.php

@ -0,0 +1,35 @@
<h2>Table Display</h2>
<?php if(printCheck(checkTable($_schema, $_table))):
$pageCount = ceil(getTableCount($_schema, $_table) / $config['limit_size']);
if($_offset < 0 || $_offset > $pageCount): ?>
<p class="error">Offset out of range</p>
<?php else: ?>
<p><?= $_schema ?> : <?= $_table ?></p>
<div>
<p><?php if($_offset > 0): ?>
<a href="?page=table_display&schema=<?= $_schema ?>&table=<?= $_table ?>&offset=<?= $_offset-1 ?>">&lt;</a>
<?php endif;
echo ($_offset+1)." / ".($pageCount);
if($_offset < $pageCount-1): ?>
<a href="?page=table_display&schema=<?= $_schema ?>&table=<?= $_table ?>&offset=<?= $_offset+1 ?>">&gt;</a>
<?php endif ?>
</p>
</div>
<table>
<?php $content = getTableContentList($_schema, $_table, $_offset*$config["limit_size"]); ?>
<tr><th><?= implode('</th><th>', array_keys($content[0])) ?></th></tr>
<?php foreach($content as $row): ?>
<tr>
<?php foreach ($row as $value): ?>
<td><?= is_null($value) ? "<span class=\"null-val\">NULL</span>" : substr($value, 0, $config["display_length"]).(strlen($value) > $config["display_length"] ? '...' : '') ?></td>
<?php endforeach
/*<td><a href="?page=table_edit&schema=<?= $_schema ?>&table=<?= $_table ?>">&#9998;</a></td>
<td><a href="?page=table_remove&schema=<?= $_schema ?>&table=<?= $_table ?>">&#10060;</a></td> */ ?>
</tr>
<?php endforeach ?>
</table>
<ul class="nav">
<li><a href="?page=table&schema=<?= $_schema ?>&table=<?= $_table ?>">Back</a></li>
<li><a href="?page=table_insert&schema=<?= $_schema ?>&table=<?= $_table ?>">Insert</a></li>
</ul>
<?php endif; endif ?>

38
pages/table_insert.php

@ -0,0 +1,38 @@
<h2>Table Insert</h2>
<?php if(printCheck(checkTable($_schema, $_table))):
$columns = getTableColumnsNames($_schema, $_table); ?>
<p><?= $_schema ?> : <?= $_table ?></p>
<?php if(!empty($_POST)):
try{
$data = array();
foreach ($_POST as $key => $value) {
if(!endsWith($key, "-null")){
if(!in_array($key, $columns))
throw new Exception("Wrong column name : ".htmlspecialchars($key));
if(!isset($_POST[$key.'-null']))
$data[$key] = $value;
}
}
insertTableLine($_schema, $_table, $data);
echo "<p>Insertion complete</p>";
}catch(Exception $e){
echo "<p class=\"error\">".$e->getMessage()."</p>";
} ?>
<?php else: ?>
<form action="?page=table_insert&schema=<?= $_schema ?>&table=<?= $_table ?>" method="post">
<?php foreach ($columns as $column): ?>
<p>
<label for="<?= $column ?>"><?= $column ?></label>
<input type="text" name="<?= $column ?>" id="<?= $column ?>">
<br><input type="checkbox" name="<?= $column ?>-null"> NULL
</p>
<?php endforeach ?>
<input type="submit" value="Save">
</form>
<?php endif ?>
<ul class="nav">
<li><a href="?page=table&schema=<?= $_schema ?>&table=<?= $_table ?>">Back</a></li>
<li><a href="?page=table_display&schema=<?= $_schema ?>&table=<?= $_table ?>">Display</a></li>
</ul>
<?php endif ?>

10
pages/tables.php

@ -0,0 +1,10 @@
<h2>Tables</h2>
<table border="1">
<tr><th>Schema</th><th>Table</th></tr>
<?php foreach (getTablesList() as $table): ?>
<tr>
<td><a href="?page=schema&schema=<?= $table['table_schema'] ?>"><?= $table['table_schema'] ?></a></td>
<td><a href="?page=table&schema=<?= $table['table_schema'] ?>&table=<?= $table['table_name'] ?>"><?= $table['table_name'] ?></a></td>
</tr>
<?php endforeach ?>
</table>

35
router.php

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="style.css">
<title>DB Xplorer - <?= $_page ?></title>
</head>
<body><div id="content">
<header>
<?php if(!empty($_SESSION)): ?><p class="logout"><a href="?page=disconnect">&#128682;</a></p><?php endif ?>
<h1><a href="./">DB Xplorer</a></h1>
</header>
<nav>
<p><ul>
<li><a href="?page=schemas">Schemas</a></li>
<li><a href="?page=tables">Tables</a></li>
<li><a href="?page=sql">SQL</a></li>
</ul></p>
</nav>
<section>
<?php
if(in_array($_page, array('sql', 'connect', 'disconnect', 'fail', 'schemas', 'schema', 'tables', 'table', 'table_display', 'table_insert')))
include("pages/$_page.php");
else
echo "<p>A minimal, unsafe and maybe usefull sql database explorer</p>";
?>
</section>
<footer>
<p>2018 &copy; Bois Clément</p>
</footer>
</div></body>
</html>

99
style.css

@ -0,0 +1,99 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
color: black;
text-decoration: none;
}
.error {
color: red;
font-weight: bold;
}
.null-val {
font-style: italic;
}
html,
body {
height: 100%;
text-align: center;
}
#content {
display: flex;
flex-flow: column;
height: 100%;
}
header, footer, nav {
background: #333;
padding: 10px;
color: white;
}
header a, footer a, nav a {
color: white;
}
header .logout {
float: right;
text-align: right;
}
nav {
border-top: 2px solid #555;
}
nav ul, section .nav {
display: flex;
justify-content: space-around;
padding: 0 10%;
list-style: none;
}
section {
flex: 1;
margin: 20px;
height: 100%;
display: flex;
flex-direction: column;
}
section a {
text-decoration: underline;
text-decoration-style: dotted;
}
section p {
margin: 5px;
}
section a:hover {
text-decoration-style: dashed;
}
section table{
margin: 20px auto;
border-collapse: collapse;
}
section td, section th {
border: 1px solid black;
padding: 2px 5px;
}
section label {
display: inline-block;
min-width: 10%;
text-align: right;
padding: 2px;
}
section [type="submit"] {
padding: 2px 10px;
}
section .nav{
flex: 1;
display: flex;
flex-direction: column;
}
section .nav:before{
content: "";
flex: 1;
}