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:
parent
cbe239d9da
commit
55be9d33bf
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
||||
|
@ -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
33
sql/patch-012.sql
Normal 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;
|
@ -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">
|
||||
|
@ -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">
|
||||
|
Loading…
Reference in New Issue
Block a user