Support EXPLAIN on Greenplum. Fixes #3097

- Extract SQLEditor.execute and SQLEditor._poll into their own files and add test around them
 - Extract SQLEditor backend functions that start executing query to their own files and add tests around it
 - Move the Explain SQL from the front-end and now pass the Explain plan parameters as a JSON object in the start query call.
 - Extract the compile_template_name into a function that can be used by the different places that try to select the version of the template and the server type
This commit is contained in:
Joao Pedro De Almeida Pereira
2018-02-09 11:54:42 +00:00
committed by Dave Page
parent e60a84c44f
commit e16a952753
30 changed files with 3673 additions and 582 deletions

View File

@@ -8,5 +8,9 @@
//////////////////////////////////////////////////////////////////////////
define(function () {
return {'static': '/base/pgadmin/static/<path:filename>'};
return {
'static': '/base/pgadmin/static/<path:filename>',
'sqleditor.poll': '/sqleditor/query_tool/poll/<path:trans_id>',
'sqleditor.query_tool_start': '/sqleditor/query_tool/start/<path:trans_id>'
};
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////////////////
import {is_new_transaction_required} from '../../../pgadmin/static/js/sqleditor/is_new_transaction_required';
describe('#is_new_transaction_required', () => {
describe('when status is not 404', () => {
it('should return false', () => {
expect(is_new_transaction_required({
status: 300,
})).toBe(false);
});
});
describe('when status is 404', () => {
describe('when responseJSON is not present', () => {
it('should return false', () => {
expect(is_new_transaction_required({
status: 404,
})).toBeFalsy();
});
});
describe('when responseJSON is present', () => {
describe('when info is not present inside responseJSON', () => {
it('should return false', () => {
expect(is_new_transaction_required({
status: 404,
responseJSON: {},
})).toBeFalsy();
});
});
describe('when info is present inside responseJSON', () => {
describe('when info value is not "DATAGRID_TRANSACTION_REQUIRED"', () => {
it('should return false', () => {
expect(is_new_transaction_required({
status: 404,
responseJSON: {
info: 'some information',
},
})).toBe(false);
});
});
describe('when info value is "DATAGRID_TRANSACTION_REQUIRED"', () => {
it('should return false', () => {
expect(is_new_transaction_required({
status: 404,
responseJSON: {
info: 'DATAGRID_TRANSACTION_REQUIRED',
},
})).toBe(true);
});
});
});
});
});
});

View File

@@ -43,7 +43,6 @@ describe('queryToolActions', () => {
expect(sqlEditorController.execute_data_query).toHaveBeenCalled();
});
});
});
@@ -51,60 +50,100 @@ describe('queryToolActions', () => {
describe('when verbose and costs are not selected and buffers and timing are not selected', () => {
beforeEach(() => {
setUpSpies('', '');
spyOn(queryToolActions, '_verbose').and.returnValue('OFF');
spyOn(queryToolActions, '_costsEnabled').and.returnValue('OFF');
spyOn(queryToolActions, '_buffers').and.returnValue('OFF');
spyOn(queryToolActions, '_timing').and.returnValue('OFF');
spyOn(queryToolActions, '_verbose').and.returnValue(false);
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
spyOn(queryToolActions, '_buffers').and.returnValue(false);
spyOn(queryToolActions, '_timing').and.returnValue(false);
});
it('calls the execute function', () => {
queryToolActions.explainAnalyze(sqlEditorController);
let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE OFF, COSTS OFF, BUFFERS OFF, TIMING OFF) ';
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainAnalyzeQuery);
// let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE OFF, COSTS OFF, BUFFERS OFF, TIMING OFF) ';
const explainObject = {
format: 'json',
analyze: true,
verbose: false,
costs: false,
buffers: false,
timing: false,
summary: false,
};
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
});
});
describe('when verbose and costs and buffers and timing are all selected', () => {
beforeEach(() => {
setUpSpies('', '');
spyOn(queryToolActions, '_verbose').and.returnValue('ON');
spyOn(queryToolActions, '_costsEnabled').and.returnValue('ON');
spyOn(queryToolActions, '_buffers').and.returnValue('ON');
spyOn(queryToolActions, '_timing').and.returnValue('ON');
spyOn(queryToolActions, '_verbose').and.returnValue(true);
spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
spyOn(queryToolActions, '_buffers').and.returnValue(true);
spyOn(queryToolActions, '_timing').and.returnValue(true);
});
it('calls the execute function', () => {
queryToolActions.explainAnalyze(sqlEditorController);
let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE ON, COSTS ON, BUFFERS ON, TIMING ON) ';
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainAnalyzeQuery);
const explainObject = {
format: 'json',
analyze: true,
verbose: true,
costs: true,
buffers: true,
timing: true,
summary: false,
};
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
});
});
describe('when verbose is selected and costs is not selected and buffer is selected and timing is not selected', () => {
beforeEach(() => {
setUpSpies('', '');
spyOn(queryToolActions, '_verbose').and.returnValue('ON');
spyOn(queryToolActions, '_costsEnabled').and.returnValue('OFF');
spyOn(queryToolActions, '_buffers').and.returnValue('ON');
spyOn(queryToolActions, '_timing').and.returnValue('OFF');
spyOn(queryToolActions, '_verbose').and.returnValue(true);
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
spyOn(queryToolActions, '_buffers').and.returnValue(true);
spyOn(queryToolActions, '_timing').and.returnValue(false);
});
it('calls the execute function', () => {
queryToolActions.explainAnalyze(sqlEditorController);
let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE ON, COSTS OFF, BUFFERS ON, TIMING OFF) ';
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainAnalyzeQuery);
const explainObject = {
format: 'json',
analyze: true,
verbose: true,
costs: false,
buffers: true,
timing: false,
summary: false,
};
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
});
});
describe('when verbose is not selected and costs is selected and buffer is not selected and timing is selected', () => {
beforeEach(() => {
setUpSpies('', '');
spyOn(queryToolActions, '_verbose').and.returnValue('OFF');
spyOn(queryToolActions, '_costsEnabled').and.returnValue('ON');
spyOn(queryToolActions, '_buffers').and.returnValue('OFF');
spyOn(queryToolActions, '_timing').and.returnValue('ON');
spyOn(queryToolActions, '_verbose').and.returnValue(false);
spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
spyOn(queryToolActions, '_buffers').and.returnValue(false);
spyOn(queryToolActions, '_timing').and.returnValue(true);
});
it('calls the execute function', () => {
queryToolActions.explainAnalyze(sqlEditorController);
let explainAnalyzeQuery = 'EXPLAIN (FORMAT JSON, ANALYZE ON, VERBOSE OFF, COSTS ON, BUFFERS OFF, TIMING ON) ';
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainAnalyzeQuery);
const explainObject = {
format: 'json',
analyze: true,
verbose: false,
costs: true,
buffers: false,
timing: true,
summary: false,
};
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
});
});
});
@@ -113,39 +152,67 @@ describe('queryToolActions', () => {
describe('when verbose and costs are selected', () => {
beforeEach(() => {
setUpSpies('', '');
spyOn(queryToolActions, '_verbose').and.returnValue('ON');
spyOn(queryToolActions, '_costsEnabled').and.returnValue('ON');
spyOn(queryToolActions, '_verbose').and.returnValue(true);
spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
});
it('calls the execute function', () => {
queryToolActions.explain(sqlEditorController);
let explainQuery = 'EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE ON, COSTS ON, BUFFERS OFF, TIMING OFF) ';
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainQuery);
const explainObject = {
format: 'json',
analyze: false,
verbose: true,
costs: true,
buffers: false,
timing: false,
summary: false,
};
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
});
});
describe('when verbose and costs are not selected', () => {
beforeEach(() => {
setUpSpies('', '');
spyOn(queryToolActions, '_verbose').and.returnValue('OFF');
spyOn(queryToolActions, '_costsEnabled').and.returnValue('OFF');
spyOn(queryToolActions, '_verbose').and.returnValue(false);
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
});
it('calls the execute function', () => {
queryToolActions.explain(sqlEditorController);
let explainQuery = 'EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE OFF, COSTS OFF, BUFFERS OFF, TIMING OFF) ';
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainQuery);
const explainObject = {
format: 'json',
analyze: false,
verbose: false,
costs: false,
buffers: false,
timing: false,
summary: false,
};
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
});
});
describe('when verbose is selected and costs is not selected', () => {
beforeEach(() => {
setUpSpies('', '');
spyOn(queryToolActions, '_verbose').and.returnValue('ON');
spyOn(queryToolActions, '_costsEnabled').and.returnValue('OFF');
spyOn(queryToolActions, '_verbose').and.returnValue(true);
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
});
it('calls the execute function', () => {
queryToolActions.explain(sqlEditorController);
let explainQuery = 'EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE ON, COSTS OFF, BUFFERS OFF, TIMING OFF) ';
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainQuery);
const explainObject = {
format: 'json',
analyze: false,
verbose: true,
costs: false,
buffers: false,
timing: false,
summary: false,
};
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
});
});
});