Disable the PSQL tool for Windows, the 'fcntl' module is not working on Windows.

pull/51/head
Nikhil Mohite 2021-06-01 20:04:43 +05:30 committed by Akshay Joshi
parent 9ac08c263b
commit e0eac875b6
18 changed files with 175 additions and 101 deletions

View File

@ -851,7 +851,8 @@ def utils():
app_version_int=config.APP_VERSION_INT,
pg_libpq_version=pg_libpq_version,
support_ssh_tunnel=config.SUPPORT_SSH_TUNNEL,
logout_url=_get_logout_url()
logout_url=_get_logout_url(),
platform=sys.platform
),
200, {'Content-Type': MIMETYPE_APP_JS})

View File

@ -6,6 +6,7 @@
# This software is released under the PostgreSQL Licence
#
##########################################################################
import sys
from flask_babelex import gettext
from pgadmin.utils.constants import PREF_LABEL_DISPLAY,\
PREF_LABEL_KEYBOARD_SHORTCUTS, PREF_LABEL_TABS_SETTINGS, \
@ -505,37 +506,61 @@ def register_browser_preferences(self):
)
)
self.open_in_new_tab = self.preference.register(
'tab_settings', 'new_browser_tab_open',
gettext("Open in new browser tab"), 'select2', None,
category_label=PREF_LABEL_OPTIONS,
options=[{'label': gettext('Query Tool'), 'value': 'qt'},
{'label': gettext('Debugger'), 'value': 'debugger'},
{'label': gettext('Schema Diff'), 'value': 'schema_diff'},
{'label': gettext('ERD Tool'), 'value': 'erd_tool'},
{'label': gettext('PSQL Tool'), 'value': 'psql_tool'}],
help_str=gettext('Select Query Tool, Debugger, Schema Diff, ERD Tool '
'or PSQL Tool from the drop-down to set '
'open in new browser tab for that particular module.'
),
select2={
'multiple': True, 'allowClear': False,
'tags': True, 'first_empty': False,
'selectOnClose': False, 'emptyOptions': True,
'tokenSeparators': [','],
'placeholder': gettext('Select open new tab...')
}
)
self.psql_tab_title = self.preference.register(
'tab_settings', 'psql_tab_title_placeholder',
gettext("PSQL tool tab title"),
'text', '%DATABASE%/%USERNAME%@%SERVER%',
category_label=PREF_LABEL_DISPLAY,
help_str=gettext(
'Supported placeholders are %DATABASE%, %USERNAME%, and %SERVER%. '
'Users can provide any string with or without placeholders of'
' their choice. The blank title will be revert back to the'
' default title with placeholders.'
if sys.platform != 'win32':
self.open_in_new_tab = self.preference.register(
'tab_settings', 'new_browser_tab_open',
gettext("Open in new browser tab"), 'select2', None,
category_label=PREF_LABEL_OPTIONS,
options=[{'label': gettext('Query Tool'), 'value': 'qt'},
{'label': gettext('Debugger'), 'value': 'debugger'},
{'label': gettext('Schema Diff'), 'value': 'schema_diff'},
{'label': gettext('ERD Tool'), 'value': 'erd_tool'},
{'label': gettext('PSQL Tool'), 'value': 'psql_tool'}],
help_str=gettext(
'Select Query Tool, Debugger, Schema Diff, ERD Tool '
'or PSQL Tool from the drop-down to set '
'open in new browser tab for that particular module.'
),
select2={
'multiple': True, 'allowClear': False,
'tags': True, 'first_empty': False,
'selectOnClose': False, 'emptyOptions': True,
'tokenSeparators': [','],
'placeholder': gettext('Select open new tab...')
}
)
self.psql_tab_title = self.preference.register(
'tab_settings', 'psql_tab_title_placeholder',
gettext("PSQL tool tab title"),
'text', '%DATABASE%/%USERNAME%@%SERVER%',
category_label=PREF_LABEL_DISPLAY,
help_str=gettext(
'Supported placeholders are %DATABASE%, %USERNAME%, '
'and %SERVER%. Users can provide any string with or without'
' placeholders of their choice. The blank title will be revert'
' back to the default title with placeholders.'
)
)
else:
self.open_in_new_tab = self.preference.register(
'tab_settings', 'new_browser_tab_open',
gettext("Open in new browser tab"), 'select2', None,
category_label=PREF_LABEL_OPTIONS,
options=[{'label': gettext('Query Tool'), 'value': 'qt'},
{'label': gettext('Debugger'), 'value': 'debugger'},
{'label': gettext('Schema Diff'), 'value': 'schema_diff'},
{'label': gettext('ERD Tool'), 'value': 'erd_tool'}],
help_str=gettext(
'Select Query Tool, Debugger, Schema Diff, ERD Tool '
'or PSQL Tool from the drop-down to set '
'open in new browser tab for that particular module.'
),
select2={
'multiple': True, 'allowClear': False,
'tags': True, 'first_empty': False,
'selectOnClose': False, 'emptyOptions': True,
'tokenSeparators': [','],
'placeholder': gettext('Select open new tab...')
}
)
)

View File

@ -66,12 +66,14 @@ define([
}]);
// show psql tool same as query tool.
pgAdmin.Browser.add_menus([{
name: 'show_psql_tool', node: this.type, module: this,
applies: ['context'], callback: 'show_psql_tool',
priority: 998, label: gettext('PSQL Tool (Beta)'),
icon: 'fas fa-terminal',
}]);
if(pgAdmin['enable_psql'] && pgAdmin['platform'] != 'win32') {
pgAdmin.Browser.add_menus([{
name: 'show_psql_tool', node: this.type, module: this,
applies: ['context'], callback: 'show_psql_tool',
priority: 998, label: gettext('PSQL Tool (Beta)'),
icon: 'fas fa-terminal',
}]);
}
}
}
},

View File

@ -210,7 +210,7 @@ define('pgadmin.browser.node', [
icon: 'fa fa-search', enable: enable,
}]);
if(pgAdmin['enable_psql']) {
if(pgAdmin['enable_psql'] && pgAdmin['platform'] != 'win32') {
// show psql tool same as query tool.
pgAdmin.Browser.add_menus([{
name: 'show_psql_tool', node: this.type, module: this,

View File

@ -59,7 +59,7 @@ let _defaultToolBarButtons = [
}
];
if(pgAdmin['enable_psql']) {
if(pgAdmin['enable_psql'] && pgAdmin['platform'] != 'win32') {
_defaultToolBarButtons.unshift({
label: gettext('PSQL Tool'),
ariaLabel: gettext('PSQL Tool'),
@ -119,7 +119,7 @@ export function initializeToolbar(panel, wcDocker) {
pgAdmin.DataGrid.show_filtered_row({mnuid: 4}, pgAdmin.Browser.tree.selected());
else if ('name' in data && data.name === gettext('Search objects'))
pgAdmin.SearchObjects.show_search_objects('', pgAdmin.Browser.tree.selected());
else if ('name' in data && data.name === gettext('PSQL Tool')){
else if ('name' in data && data.name === gettext('PSQL Tool') && pgAdmin['platform'] != 'win32'){
var input = {},
t = pgAdmin.Browser.tree,
i = input.item || t.selected(),

View File

@ -54,6 +54,8 @@ define('pgadmin.browser.utils',
/* GET PSQL Tool related config */
pgAdmin['enable_psql'] = '{{ current_app.config.get('ENABLE_PSQL') }}' == 'True';
pgAdmin['allow_psql_shell_commands'] = '{{ current_app.config.get('ALLOW_PSQL_SHELL_COMMANDS') }}' == 'True';
pgAdmin['platform'] = '{{platform}}';
// Define list of nodes on which Query tool option doesn't appears
var unsupported_nodes = pgAdmin.unsupported_nodes = [

View File

@ -1,35 +1,35 @@
#!/usr/bin/env python3
import fcntl
import os
import pty
import re
import select
import struct
import termios
import config
from sys import platform as _platform
import eventlet.green.subprocess as subprocess
from config import PG_DEFAULT_DRIVER
from flask import Response, url_for, request
from flask import render_template, copy_current_request_context, \
current_app as app
from flask_babelex import gettext
from flask_security import login_required, current_user
from pgadmin.browser.utils import underscore_unescape
from pgadmin.browser.utils import underscore_unescape, underscore_escape
from pgadmin.utils import PgAdminModule
from pgadmin.utils.constants import MIMETYPE_APP_JS
from pgadmin.utils.driver import get_driver
from ... import socketio as sio
from pgadmin.utils import get_complete_file_path
from pgadmin.utils.ajax import internal_server_error
if _platform != 'win32':
import fcntl
import termios
import pty
session_input = dict()
session_input_cursor = dict()
session_last_cmd = dict()
pdata = dict()
cdata = dict()
_NODES_SQL = 'nodes.sql'
class PSQLModule(PgAdminModule):
@ -225,27 +225,35 @@ def start_process(data):
"""
@copy_current_request_context
def read_and_forward_pty_output(sid, data):
max_read_bytes = 1024 * 20
p, parent, fd = create_pty_terminal(connection_data)
if _platform != 'win32':
p, parent, fd = create_pty_terminal(connection_data)
while p and p.poll() is None:
if request.sid in app.config['sessions']:
# This code is added to make this unit testable.
if "is_test" not in data:
sio.sleep(0.01)
else:
data['count'] += 1
if data['count'] == 5:
break
while p and p.poll() is None:
if request.sid in app.config['sessions']:
# This code is added to make this unit testable.
if "is_test" not in data:
sio.sleep(0.01)
else:
data['count'] += 1
if data['count'] == 5:
break
timeout = 0
# module provides access to platform-specific I/O
# monitoring functions
(data_ready, _, _) = select.select([parent, fd], [], [],
timeout)
timeout = 0
# module provides access to platform-specific I/O
# monitoring functions
(data_ready, _, _) = select.select([parent, fd], [], [],
timeout)
read_terminal_data(parent, data_ready, max_read_bytes, sid)
read_terminal_data(parent, data_ready, max_read_bytes, sid)
else:
sio.emit(
'conn_error',
{
'error': 'PSQL tool not supported.',
}, namespace='/pty', room=request.sid)
# Check user is authenticated and PSQL is enabled in config.
if current_user.is_authenticated and config.ENABLE_PSQL:
@ -255,6 +263,8 @@ def start_process(data):
if data['db']:
db = underscore_unescape(data['db']).replace('\\', "\\\\")
data['db'] = db
conn, manager = _get_connection(int(data['sid']), data)
psql_utility = manager.utility('sql')
connection_data = get_connection_str(psql_utility, db,
@ -348,15 +358,15 @@ def get_conn_str(manager, db):
"""
manager.export_password_env('PGPASSWORD')
conn_attr =\
"host={0} port={1} dbname={2} user={3} sslmode={4} " \
"sslcompression={5} " \
"".format(
'host={0} port={1} dbname={2} user={3} sslmode={4} ' \
'sslcompression={5} ' \
''.format(
manager.local_bind_host if manager.use_ssh_tunnel else
manager.host,
manager.local_bind_port if manager.use_ssh_tunnel else
manager.port,
db if db != '' else 'postgres',
manager.user if manager.user else 'postgres',
underscore_unescape(db) if db != '' else 'postgres',
underscore_unescape(manager.user) if manager.user else 'postgres',
manager.ssl_mode,
True if manager.sslcompression else False,
)
@ -423,7 +433,6 @@ def invalid_cmd():
:rtype:
"""
session_last_cmd[request.sid]['invalid_cmd'] = True
for i in range(len(session_input[request.sid])):
os.write(app.config['sessions'][request.sid],
'\b \b'.encode())
@ -455,6 +464,7 @@ def check_valid_cmd(user_input):
if stop_execution:
session_last_cmd[request.sid]['invalid_cmd'] = True
# Remove already added command from terminal.
for i in range(len(user_input)):
os.write(app.config['sessions'][request.sid],
@ -482,6 +492,7 @@ def enter_key_press(data):
# contains \! in input.
is_new_connection = session_last_cmd[request.sid][
'is_new_connection']
if user_input.startswith('\\!') and re.match("^\\\!$", user_input) and len(
user_input) == 2 and not config.ALLOW_PSQL_SHELL_COMMANDS \
and not is_new_connection:
@ -490,7 +501,8 @@ def enter_key_press(data):
not config.ALLOW_PSQL_SHELL_COMMANDS and\
not session_last_cmd[request.sid]['is_new_connection']:
check_valid_cmd(user_input)
elif user_input == '\q' or user_input == 'q\\q':
elif user_input == '\q' or user_input == 'q\\q' or \
user_input in ['exit', 'exit;']:
# If user enter \q to terminate the PSQL, emit the msg to
# notify user connection is terminated.
sio.emit('pty-output',
@ -501,6 +513,7 @@ def enter_key_press(data):
' tool.'),
'error': True},
namespace='/pty', room=request.sid)
os.write(app.config['sessions'][request.sid],
'\n'.encode())

View File

@ -54,7 +54,7 @@ export function initialize(gettext, url_for, $, _, pgAdmin, csrfToken, Browser)
}];
this.enable_psql_tool = pgAdmin['enable_psql'];
if(pgAdmin['enable_psql']) {
if(pgAdmin['enable_psql'] && pgAdmin['platform'] != 'win32') {
pgBrowser.add_menus(menus);
}

View File

@ -23,36 +23,40 @@
require(
['sources/generated/psql_tool'],
function(pgBrowser) {
const term = self.pgAdmin.Browser.psql.psql_terminal();
<!--Addon for fitAddon, webLinkAddon, SearchAddon -->
const fitAddon = self.pgAdmin.Browser.psql.psql_Addon(term);
<!-- Update the theme for terminal as per pgAdmin 4 theme.-->
self.pgAdmin.Browser.psql.set_theme(term);
<!-- Open the terminal -->
term.open(document.getElementById('psql-terminal'));
<!-- Socket-->
const socket = self.pgAdmin.Browser.psql.psql_socket();
self.pgAdmin.Browser.psql.psql_socket_io(socket, '{{is_enable}}', '{{sid}}', '{{db}}', '{{server_type}}', fitAddon, term);
self.pgAdmin.Browser.psql.psql_terminal_io(term, socket);
self.pgAdmin.Browser.psql.check_db_name_change('{{db}}', '{{o_db_name}}');
if (self.pgAdmin['platform'] != 'win32') {
const term = self.pgAdmin.Browser.psql.psql_terminal();
<!--Addon for fitAddon, webLinkAddon, SearchAddon -->
const fitAddon = self.pgAdmin.Browser.psql.psql_Addon(term);
<!-- Update the theme for terminal as per pgAdmin 4 theme.-->
self.pgAdmin.Browser.psql.set_theme(term);
<!-- Open the terminal -->
term.open(document.getElementById('psql-terminal'));
<!-- Socket-->
const socket = self.pgAdmin.Browser.psql.psql_socket();
self.pgAdmin.Browser.psql.psql_socket_io(socket, '{{is_enable}}', '{{sid}}', '{{db}}', '{{server_type}}', fitAddon, term);
self.pgAdmin.Browser.psql.psql_terminal_io(term, socket);
self.pgAdmin.Browser.psql.check_db_name_change('{{db}}', '{{o_db_name}}');
<!-- Resize the terminal -->
function fitToscreen(){
fitAddon.fit()
socket.emit("resize", {"cols": term.cols, "rows": term.rows})
}
function debounce(func, wait_ms) {
let timeout
return function(...args) {
const context = this
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(context, args), wait_ms)
<!-- Resize the terminal -->
function fitToscreen(){
fitAddon.fit()
socket.emit("resize", {"cols": term.cols, "rows": term.rows})
}
}
const wait_ms = 50;;
window.onresize = debounce(fitToscreen, wait_ms)
function debounce(func, wait_ms) {
let timeout
return function(...args) {
const context = this
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(context, args), wait_ms)
}
}
const wait_ms = 50;;
window.onresize = debounce(fitToscreen, wait_ms)
} else {
document.getElementById('psql-terminal').innterHTML = 'PSQL not supported'
}
});
{% endblock %}

View File

@ -1,5 +1,6 @@
import uuid
import config
import sys
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
@ -24,6 +25,8 @@ class PSQLBackend(BaseTestGenerator):
self.server_con = server_utils.connect_server(self, self.sid)
def runTest(self):
if sys.platform == 'win32':
self.skipTest('PSQL disabled for windows')
# Fetch flask client to access current user and other cookies.
flask_client = app.test_client()
flask_client.get('/')

View File

@ -1,5 +1,6 @@
import uuid
import random
import sys
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
@ -16,6 +17,8 @@ class PSQLPanel(BaseTestGenerator):
self.theme = 'standard'
def runTest(self):
if sys.platform == 'win32':
self.skipTest('PSQL disabled for windows')
trans_id = random.randint(1, 9999999)
url = '/psql/panel/{trans_id}?sgid={sgid}&sid={sid}&did={did}' \
'&server_type=pg&db={db_name}&theme={theme}'.\

View File

@ -1,5 +1,6 @@
import uuid
import config
import sys
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
@ -17,6 +18,8 @@ class PSQLSocketDisabled(BaseTestGenerator):
config.ENABLE_PSQL = False
def runTest(self):
if sys.platform == 'win32':
self.skipTest('PSQL disabled for windows')
self.test_client = socketio.test_client(app, namespace='/pty')
self.assertTrue(self.test_client.is_connected('/pty'))
received = self.test_client.get_received('/pty')

View File

@ -1,5 +1,6 @@
import uuid
import config
import sys
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
@ -24,6 +25,8 @@ class PSQLInput(BaseTestGenerator):
self.server_con = server_utils.connect_server(self, self.sid)
def runTest(self):
if sys.platform == 'win32':
self.skipTest('PSQL disabled for windows')
# Fetch flask client to access current user and other cookies.
flask_client = app.test_client()
flask_client.get('/')

View File

@ -1,5 +1,6 @@
import uuid
import config
import sys
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
@ -24,6 +25,8 @@ class PSQLResizeTerminal(BaseTestGenerator):
self.server_con = server_utils.connect_server(self, self.sid)
def runTest(self):
if sys.platform == 'win32':
self.skipTest('PSQL disabled for windows')
# Fetch flask client to access current user and other cookies.
flask_client = app.test_client()
flask_client.get('/')

View File

@ -1,5 +1,6 @@
import uuid
import config
import sys
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
@ -17,6 +18,8 @@ class PSQLSocketConnect(BaseTestGenerator):
config.ENABLE_PSQL = True
def runTest(self):
if sys.platform == 'win32':
self.skipTest('PSQL disabled for windows')
self.test_client = socketio.test_client(app, namespace='/pty')
self.assertTrue(self.test_client.is_connected('/pty'))
received = self.test_client.get_received('/pty')

View File

@ -1,5 +1,6 @@
import uuid
import config
import sys
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
@ -17,6 +18,8 @@ class PSQLSocketDisconnect(BaseTestGenerator):
config.ENABLE_PSQL = True
def runTest(self):
if sys.platform == 'win32':
self.skipTest('PSQL disabled for windows')
# Fetch flask client to access current user and other cookies.
flask_test_client = app.test_client()
flask_test_client.get('/')

View File

@ -1,5 +1,6 @@
import uuid
import config
import sys
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
@ -21,6 +22,8 @@ class PSQLStartProcess(BaseTestGenerator):
self.server_con = server_utils.connect_server(self, self.sid)
def runTest(self):
if sys.platform == 'win32':
self.skipTest('PSQL disabled for windows')
# Fetch flask client to access current user and other cookies.
flask_client = app.test_client()
flask_client.get('/')

View File

@ -1,5 +1,6 @@
import uuid
import config
import sys
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
@ -17,6 +18,8 @@ class PSQLStartProcessFail(BaseTestGenerator):
config.ENABLE_PSQL = True
def runTest(self):
if sys.platform == 'win32':
self.skipTest('PSQL disabled for windows')
self.test_client = socketio.test_client(app, namespace='/pty')
self.assertTrue(self.test_client.is_connected('/pty'))
received = self.test_client.get_received('/pty')