Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for restoring plain SQL database dumps. #5871 #8493

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions web/pgadmin/static/js/SchemaView/FormView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from './hooks';
import { registerView, View } from './registry';
import { createFieldControls, listenDepChanges } from './utils';
import FormViewTab from './FormViewTab';

const ErrorMessageBox = () => {
const [key, setKey] = useState(0);
Expand Down Expand Up @@ -181,14 +182,8 @@ export default function FormView({
action={(ref) => ref?.updateIndicator()}
>{
finalGroups.map((tabGroup, idx) =>
<Tab
key={tabGroup.id}
label={tabGroup.label}
data-test={tabGroup.id}
className={
tabGroup.hasError &&
tabValue != idx ? 'tab-with-error' : ''
}
<FormViewTab
key={tabGroup.id} tabGroup={tabGroup} idx={idx} tabValue={tabValue}
/>
)
}{hasSQLTab &&
Expand Down
45 changes: 45 additions & 0 deletions web/pgadmin/static/js/SchemaView/FormViewTab.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2025, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////

import { Tab } from '@mui/material';
import React, { useContext, useState } from 'react';
import { useFieldOptions, useSchemaStateSubscriber } from './hooks';
import { SchemaStateContext } from './SchemaState';
import PropTypes from 'prop-types';

export default function FormViewTab({tabGroup, idx, tabValue, ...props}) {
const accessPath = [tabGroup.id];
const [, setRefreshKey] = useState(0);
const subscriberManager = useSchemaStateSubscriber(setRefreshKey);
const schemaState = useContext(SchemaStateContext);
const options = useFieldOptions(accessPath, schemaState, subscriberManager);

return (
<Tab
key={tabGroup.id}
label={tabGroup.label}
data-test={tabGroup.id}
iconPosition='start'
disabled={options.disabled}
className={
tabGroup.hasError &&
tabValue != idx ? 'tab-with-error' : ''
}
{...props}
/>
);
};

FormViewTab.muiName = Tab.muiName;

FormViewTab.propTypes = {
tabGroup: PropTypes.object,
idx: PropTypes.number,
tabValue: PropTypes.number,
};
8 changes: 6 additions & 2 deletions web/pgadmin/static/js/Theme/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -788,13 +788,17 @@ function getFinalTheme(baseTheme) {
MuiTab: {
styleOverrides: {
root: {
'&.MuiTab-textColorPrimary':{
'&:not(.Mui-disabled).MuiTab-textColorPrimary':{
color: baseTheme.palette.text.primary,
},
'&.Mui-selected': {
color: baseTheme.otherVars.activeColor,
},
}
},
icon: {
fontSize: '1rem',
marginRight: '2px',
},
}
},
MuiBackdrop: {
Expand Down
78 changes: 64 additions & 14 deletions web/pgadmin/tools/restore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,18 @@ def _get_create_req_data():
data = json.loads(request.data)

try:
_file = filename_with_file_manager_path(data['file'])
filepath = filename_with_file_manager_path(data['file'])
except Exception as e:
return True, internal_server_error(errormsg=str(e)), data, None

if _file is None:
if filepath is None:
return True, make_json_response(
status=410,
success=0,
errormsg=_("File could not be found.")
), data, _file
), data, filepath

return False, '', data, _file
return False, '', data, filepath


def _connect_server(sid):
Expand Down Expand Up @@ -263,15 +263,15 @@ def set_multiple(key, param, data, args, driver, conn, with_schema=True):
return False


def _set_args_param_values(data, manager, server, driver, conn, _file):
def get_restore_util_args(data, manager, server, driver, conn, filepath):
"""
add args to the list.
:param data: Data.
:param manager: Manager.
:param server: Server.
:param driver: Driver.
:param conn: Connection.
:param _file: File.
:param filepath: File.
:return: args list.
"""
args = []
Expand Down Expand Up @@ -347,11 +347,58 @@ def _set_args_param_values(data, manager, server, driver, conn, _file):
False)
set_multiple('indexes', '--index', data, args, driver, conn, False)

args.append(fs_short_path(_file))
args.append(fs_short_path(filepath))

return args


def get_sql_util_args(data, manager, server, filepath):
"""
add args to the list.
:param data: Data.
:param manager: Manager.
:param server: Server.
:param driver: Driver.
:param conn: Connection.
:param filepath: File.
:return: args list.
"""
args = [
'--host',
manager.local_bind_host if manager.use_ssh_tunnel else server.host,
'--port',
str(manager.local_bind_port) if manager.use_ssh_tunnel
else str(server.port),
'--username', server.username, '--dbname',
data['database'],
'--file', fs_short_path(filepath)
]

return args


def use_restore_utility(data, manager, server, driver, conn, filepath):
utility = manager.utility('restore')
ret_val = does_utility_exist(utility)
if ret_val:
return ret_val, None, None

args = get_restore_util_args(data, manager, server, driver, conn, filepath)

return None, utility, args


def use_sql_utility(data, manager, server, filepath):
utility = manager.utility('sql')
ret_val = does_utility_exist(utility)
if ret_val:
return ret_val, None, None

args = get_sql_util_args(data, manager, server, filepath)

return None, utility, args


@blueprint.route('/job/<int:sid>', methods=['POST'], endpoint='create_job')
@pga_login_required
def create_restore_job(sid):
Expand All @@ -364,24 +411,27 @@ def create_restore_job(sid):
Returns:
None
"""
is_error, errmsg, data, _file = _get_create_req_data()
is_error, errmsg, data, filepath = _get_create_req_data()
if is_error:
return errmsg

is_error, errmsg, driver, manager, conn, _, server = _connect_server(sid)
if is_error:
return errmsg

utility = manager.utility('restore')
ret_val = does_utility_exist(utility)
if ret_val:
if data['format'] == 'plain':
error_msg, utility, args = use_sql_utility(
data, manager, server, filepath)
else:
error_msg, utility, args = use_restore_utility(
data, manager, server, driver, conn, filepath)

if error_msg is not None:
return make_json_response(
success=0,
errormsg=ret_val
errormsg=error_msg
)

args = _set_args_param_values(data, manager, server, driver, conn, _file)

try:
p = BatchProcess(
desc=RestoreMessage(
Expand Down
3 changes: 2 additions & 1 deletion web/pgadmin/tools/restore/static/js/restore.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ define('tools.restore', [
()=>getRestoreDisableOptionSchema({nodeInfo: treeNodeInfo}),
()=>getRestoreMiscellaneousSchema({nodeInfo: treeNodeInfo}),
{
role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData)
role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData),
nodeType: itemNodeData._type,
},
treeNodeInfo,
pgBrowser
Expand Down
Loading
Loading