1
0
mirror of https://gitlab.com/depesz/explain.depesz.com.git synced 2024-11-28 08:58:52 +02:00

Add support for storing queries

This commit is contained in:
Hubert depesz Lubaczewski 2020-07-27 15:16:43 +02:00
parent cbe239d9da
commit 55be9d33bf
7 changed files with 91 additions and 10 deletions

View File

@ -210,6 +210,12 @@ sub index {
$title = '' unless defined $title;
$title = '' if 'Optional title' eq $title;
my $query = $self->req->param( 'query' );
if ( defined $query ) {
$query = undef if $query =~ m{\A\s*\z};
$query = undef if $query =~ m{\A\s*for\s+example:\s+select\s+a,\s+b\s+from\s+c\s+where\s+d\s+>\s+now\(\)\s+-\s+'5\s+minutes'::interval\s*;\s*\z}ism;
}
# try
eval {
@ -222,7 +228,12 @@ sub index {
# Anonymize plan, when requested.
if ( $is_anon ) {
$explain->anonymize();
if ( $query ) {
( $query ) = $explain->anonymize( $query );
}
else {
$explain->anonymize();
}
$plan = $explain->as_text();
}
@ -239,7 +250,7 @@ sub index {
}
# save to database
my ( $id, $delete_key ) = $self->database->save_with_random_name( $title, $plan, $is_public, $is_anon, $self->session->{ 'user' }, $parent_id, );
my ( $id, $delete_key ) = $self->database->save_with_random_name( $title, $plan, $is_public, $is_anon, $self->session->{ 'user' }, $parent_id, $query );
# redirect to /show/:id
$self->flash( delete_key => $delete_key );
@ -273,19 +284,19 @@ sub show {
my $self = shift;
# value of "/:id" param
my $id = defined $self->stash->{ id } ? $self->stash->{ id } : '';
my $id = defined $self->stash->{ id } ? $self->stash->{ id } : '';
# missing or invalid
return $self->redirect_to( 'new-explain' ) unless $id =~ m{\A[a-zA-Z0-9]+\z};
# get plan source from database
my ( $plan, $title ) = $self->database->get_plan( $id );
my $data = $self->database->get_plan_data( $id );
# not found in database
return $self->redirect_to( 'new-explain', status => 404 ) unless $plan;
return $self->redirect_to( 'new-explain', status => 404 ) unless $data;
# make explanation
my $explain = eval { Pg::Explain->new( source => $plan ); };
my $explain = eval { Pg::Explain->new( source => $data->{ 'plan' } ); };
# plans are validated before save, so this should never happen
if ( $EVAL_ERROR ) {
@ -328,7 +339,8 @@ sub show {
# put explain and title to stash
$self->stash->{ explain } = $explain;
$self->stash->{ title } = $title;
$self->stash->{ title } = $data->{ 'title' };
$self->stash->{ query } = $data->{ 'query' };
$self->stash->{ stats } = $stats;
# Fetch path of optimizations

View File

@ -215,12 +215,12 @@ sub update_plan {
sub save_with_random_name {
my $self = shift;
my ( $title, $content, $is_public, $is_anon, $username, $optimization_for ) = @_;
my ( $title, $content, $is_public, $is_anon, $username, $optimization_for, $query ) = @_;
my @row = $self->dbh->selectrow_array(
'SELECT id, delete_key FROM register_plan(?, ?, ?, ?, ?, ?)',
'SELECT id, delete_key FROM register_plan(?, ?, ?, ?, ?, ?, ?)',
undef,
$title, $content, $is_public, $is_anon, $username, $optimization_for,
$title, $content, $is_public, $is_anon, $username, $optimization_for, $query
);
# return id and delete_key

View File

@ -108,6 +108,8 @@ section p a:hover {background:#234;color:#fff;border:1px solid #123}
.result {margin:0;padding:0}
.result-source {background:#222 url('../img/code-bg.gif');color:#fff}
.result-source pre {margin:0;padding:1em}
.result-query {background:#222 url('../img/code-bg.gif');color:#fff}
.result-query pre {margin:0;padding:1em}
.result-text {background:#222 url('../img/code-bg.gif');color:#fff}
.result-text pre {margin:0;padding:1em}

View File

@ -195,10 +195,19 @@
var result = $( link.parents( 'div.result' ).get( 0 ) );
if ( 'query' == view.toLowerCase( ) ) {
result.find( 'div.result-html' ).hide( );
result.find( 'div.result-stats' ).hide( );
result.find( 'div.result-source' ).hide( );
result.find( 'div.result-query' ).show( );
result.find( 'div.result-text' ).hide( );
return;
}
if ( 'text' == view.toLowerCase( ) ) {
result.find( 'div.result-html' ).hide( );
result.find( 'div.result-stats' ).hide( );
result.find( 'div.result-source' ).hide( );
result.find( 'div.result-query' ).hide( );
result.find( 'div.result-text' ).show( );
return;
}
@ -207,6 +216,7 @@
result.find( 'div.result-html' ).show( );
result.find( 'div.result-stats' ).hide( );
result.find( 'div.result-source' ).hide( );
result.find( 'div.result-query' ).hide( );
result.find( 'div.result-text' ).hide( );
return;
}
@ -215,6 +225,7 @@
result.find( 'div.result-html' ).hide( );
result.find( 'div.result-stats' ).hide( );
result.find( 'div.result-source' ).show( );
result.find( 'div.result-query' ).hide( );
result.find( 'div.result-text' ).hide( );
return;
}
@ -222,6 +233,7 @@
result.find( 'div.result-html' ).hide( );
result.find( 'div.result-source' ).hide( );
result.find( 'div.result-stats' ).show( );
result.find( 'div.result-query' ).hide( );
result.find( 'div.result-text' ).hide( );
},

33
sql/patch-012.sql Normal file
View File

@ -0,0 +1,33 @@
-- Added support for queries for plans
BEGIN;
ALTER TABLE public.plans add column query TEXT;
CREATE OR REPLACE FUNCTION public.register_plan(in_title text, in_plan text, in_is_public boolean, in_is_anonymized boolean, in_username text, in_optimization_for text, in_query TEXT)
RETURNS register_plan_return
LANGUAGE plpgsql
AS $function$
DECLARE
use_hash_length int4 := 2;
reply register_plan_return;
insert_sql TEXT;
BEGIN
insert_sql := 'INSERT INTO public.plans (id, title, plan, is_public, entered_on, is_anonymized, delete_key, added_by, optimization_for, query) VALUES ($1, $2, $3, $4, now(), $5, $6, $7, $8, $9 )';
reply.delete_key := get_random_string( 50 );
LOOP
reply.id := get_random_string(use_hash_length);
BEGIN
execute insert_sql using reply.id, in_title, in_plan, in_is_public, in_is_anonymized, reply.delete_key, in_username, in_optimization_for, in_query;
RETURN reply;
EXCEPTION WHEN unique_violation THEN
-- do nothing
END;
use_hash_length := use_hash_length + 1;
IF use_hash_length >= 30 THEN
raise exception 'Random string of length == 30 requested. something''s wrong.';
END IF;
END LOOP;
END;
$function$;
commit;

View File

@ -51,6 +51,14 @@
Buckets: 1024 Batches: 1 Memory Usage: 1kB
-> Seq Scan on pg_namespace n (cost=0.00..1.09 rows=4 width=68) (actual time=0.005..0.007 rows=4 loops=1)
Filter: ((nspname <> 'pg_catalog'::name) AND (nspname <> 'information_schema'::name))"></textarea>
<label for="query">Optionally paste your query here:</label>
<textarea id="query" name="query" class="auto-hint" title="For example:
SELECT a, b
FROM c
WHERE d > now() - '5 minutes'::interval;"></textarea>
select\s+a,\s+b\s+from\s+c\s+where\s+d\s+>\s+now()\s+-\s+'5\s+minutes'::interval;
</div>
<div class="fe fe_is_public">

View File

@ -475,6 +475,14 @@
onclick="$( this ).explain( 'toggleView', 'text', this ); return false;"
onkeypress="return this.onclick( );">TEXT</a>
</li>
% }
% if ( stash('query') ) {
<li class="query">
<a href="#query"
title="view query"
onclick="$( this ).explain( 'toggleView', 'query', this ); return false;"
onkeypress="return this.onclick( );">QUERY</a>
</li>
% }
<li class="stats">
<a href="#stats"
@ -592,6 +600,12 @@
</div>
% }
% if ( stash('query') ) {
<div class="result-query hidden">
<pre id="query"><code class="sql"><%= stash('query') %></code></pre>
</div>
% }
<div class="result-stats hidden">
<h1>Per node type stats</h1>
<table class="stats">