Fixed an issue introduced due to reloading of the tree due to changes in particular preferences (#7942)

* Fix an issue - when the object browser tree is being recreated due to reloading for changes in some of the preferences. Tree object returns object from the previous instance as 'selected', but - it does not have the 'treeNodeInfo' available. In this special case - we would consider that there is no node selected at that particular moment, and pass information accordingly.

* Fixed 'New Connection' dialog issue after connecting a disconnected server.

* Disable the 'Add' button in the GridHeader with form, when 'canAdd'
flag is set to false.

* Convert the access path to string array for correct comparision.

* Check the access path type before comparison.
When language is 'c', set the 'code' block read-only.

* Enabled 'Strict' control for EPAS >= 95
This commit is contained in:
Ashesh Vashi 2024-09-18 20:55:01 +05:30 committed by GitHub
parent 100f59f78b
commit b0cd028ff8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 78 additions and 56 deletions

View File

@ -16,14 +16,14 @@ class TokenHeaderSchema extends BaseUISchema {
constructor(tokenOptions) {
super({
token: undefined,
isNew: false,
isNew: true,
});
this.tokenOptions = tokenOptions;
}
set isNewFTSConf(flag) {
if (!this.state) return;
if (this.state)
this.state.data = {...this.state.data, isNew: flag};
}
@ -35,16 +35,17 @@ class TokenHeaderSchema extends BaseUISchema {
}
get baseFields() {
let obj = this;
return [{
id: 'token', label: gettext('Tokens'), deps: ['isNew'],
type: () => ({
type: 'select',
options: this.tokenOptions,
}),
disabled: function() { return obj.isNewFTSConf; }
disabled: function() {
return this.state ? this.state.data.isNew : true;
}
},{
id: 'isNew', visible: false, type: 'text',
id: 'isNew', visible: false, type: 'text', exclude: true,
}];
}
}
@ -160,12 +161,13 @@ export default class FTSConfigurationSchema extends BaseUISchema {
}, {
id: 'tokens', label: '', type: 'collection',
group: gettext('Tokens'), mode: ['create','edit'],
editable: false, schema: this.tokColumnSchema,
schema: this.tokColumnSchema,
headerSchema: this.tokHeaderSchema,
headerFormVisible: true,
GridHeader: DataGridFormHeader,
uniqueCol : ['token'],
canAdd: true, canEdit: false, canDelete: true,
canAdd: (state, helpderProps) => (helpderProps.mode !== 'create'),
canDelete: true,
}
];
}

View File

@ -159,33 +159,21 @@ export default class FunctionSchema extends BaseUISchema {
}
}
isGreaterThan95(state){
if (
isLessThan95ORNonSPL(state) {
return (
this.inCatalog() ||
this.node_info['node_info'].server.version < 90500 ||
this.node_info['node_info']['server'].server_type != 'ppas' ||
state.lanname != 'edbspl'
) {
state.provolatile = null;
state.proisstrict = false;
state.procost = null;
state.proleakproof = false;
return true;
} else {
return false;
}
);
}
isGreaterThan96(state){
if (
isLessThan96ORNonSPL(state){
return (
this.node_info['node_info'].server.version < 90600 ||
this.node_info['node_info']['server'].server_type != 'ppas' ||
state.lanname != 'edbspl'
) {
state.proparallel = null;
return true;
} else {
return false;
}
);
}
@ -210,7 +198,7 @@ export default class FunctionSchema extends BaseUISchema {
if (this.type !== 'procedure') {
obj.inCatalog(state);
} else {
obj.isGreaterThan95(state);
obj.isLessThan95ORNonSPL(state);
}
},
noEmpty: true,
@ -328,7 +316,7 @@ export default class FunctionSchema extends BaseUISchema {
{'label': 'VOLATILE', 'value': 'v'},
{'label': 'STABLE', 'value': 's'},
{'label': 'IMMUTABLE', 'value': 'i'},
], disabled: (this.type !== 'procedure') ? obj.inCatalog() : obj.isGreaterThan95,
], disabled: (this.type !== 'procedure') ? obj.inCatalog() : obj.isLessThan95ORNonSPL,
controlProps: {allowClear: false},
},{
id: 'proretset', label: gettext('Returns a set?'), type: 'switch',
@ -336,8 +324,22 @@ export default class FunctionSchema extends BaseUISchema {
visible: obj.isVisible, readonly: obj.isReadonly,
},{
id: 'proisstrict', label: gettext('Strict?'), type: 'switch',
group: gettext('Options'), disabled: obj.inCatalog(),
group: gettext('Options'),
disabled: obj.inCatalog() ? true : obj.isLessThan95ORNonSPL,
deps: ['lanname'],
depChange: (state, source) => (
(source[source.length - 1] !== 'lanname') ? undefined : (
obj.isLessThan95ORNonSPL(state)
) ? {
provolatile: null,
proisstrict: false,
procost: null,
proleakproof: false,
proparallel: null,
} : (
obj.isLessThan95ORNonSPL(state) ? { proparallel: null } : undefined
)
),
},{
id: 'prosecdef', label: gettext('Security of definer?'),
group: gettext('Options'), type: 'switch',
@ -357,13 +359,13 @@ export default class FunctionSchema extends BaseUISchema {
{'label': 'RESTRICTED', 'value': 'r'},
{'label': 'SAFE', 'value': 's'},
],
disabled: (this.type !== 'procedure') ? obj.inCatalog(): obj.isGreaterThan96,
disabled: (this.type !== 'procedure') ? obj.inCatalog(): obj.isLessThan96ORNonSPL,
min_version: 90600,
controlProps: {allowClear: false},
},{
id: 'procost', label: gettext('Estimated cost'), group: gettext('Options'),
cell:'string', type: 'text', deps: ['lanname'],
disabled: (this.type !== 'procedure') ? obj.isDisabled: obj.isGreaterThan95,
disabled: (this.type !== 'procedure') ? obj.isDisabled : obj.isLessThan95ORNonSPL,
},{
id: 'prorows', label: gettext('Estimated rows'), type: 'text',
deps: ['proretset'], visible: obj.isVisible,
@ -378,7 +380,7 @@ export default class FunctionSchema extends BaseUISchema {
},{
id: 'proleakproof', label: gettext('Leak proof?'),
group: gettext('Options'), cell:'boolean', type: 'switch', min_version: 90200,
disabled: (this.type !== 'procedure') ? obj.inCatalog(): obj.isGreaterThan95,
disabled: (this.type !== 'procedure') ? obj.inCatalog() : obj.isLessThan95ORNonSPL,
deps: ['lanname'],
},{
id: 'prosupportfunc', label: gettext('Support function'),

View File

@ -161,10 +161,7 @@ export default class TriggerFunctionSchema extends BaseUISchema {
type: 'sql', isFullTab: true,
mode: ['properties', 'create', 'edit'],
group: gettext('Code'), deps: ['lanname'],
visible: (state) => {
return state.lanname !== 'c';
},
disabled: obj.isDisabled, readonly: obj.isReadonly,
readonly: (state) => (obj.isDisabled() || state.lanname === 'c'),
},{
id: 'probin', label: gettext('Object file'), cell: 'string',
type: 'text', group: gettext('Definition'), deps: ['lanname'],
@ -261,7 +258,7 @@ export default class TriggerFunctionSchema extends BaseUISchema {
if (isEmptyString(state.service)) {
/* code validation */
if (isEmptyString(state.prosrc)) {
if (isEmptyString(state.prosrc) && state.lanname !== 'c') {
errmsg = gettext('Code cannot be empty.');
setError('prosrc', errmsg);
return true;

View File

@ -77,7 +77,7 @@ export function DataGridFormHeader({tableEleRef}) {
viewHelperProps,
} = useContext(DataGridContext);
const {
addOnTop, canAddRow, canEdit, expandEditOnAdd, headerFormVisible
canAdd, addOnTop, canAddRow, canEdit, expandEditOnAdd, headerFormVisible
} = options;
const rows = table.getRowModel().rows;
@ -85,8 +85,12 @@ export function DataGridFormHeader({tableEleRef}) {
const newRowIndex = useRef(-1);
const schemaState = useContext(SchemaStateContext);
const headerFormData = useRef({});
const [addDisabled, setAddDisabled] = useState(canAddRow);
const [addDisabled, setAddDisabled] = useState(!canAdd || !canAddRow);
const {headerSchema} = field;
const disableAddButton = (flag) => {
if (!canAdd || !canAddRow) return;
setAddDisabled(flag);
};
const onAddClick = useCallback(() => {
@ -154,8 +158,8 @@ export function DataGridFormHeader({tableEleRef}) {
showFooter={false}
onDataChange={(isDataChanged, dataChanged)=>{
headerFormData.current = dataChanged;
setAddDisabled(
canAddRow && headerSchema.addDisabled(headerFormData.current)
disableAddButton(
headerSchema.addDisabled(headerFormData.current)
);
}}
hasSQL={false}

View File

@ -114,7 +114,7 @@ export const sessDataReducer = (state, action) => {
return data;
case SCHEMA_STATE_ACTIONS.DEFERRED_DEPCHANGE:
data = getDepChange(action.path, data, state, action);
data = getDepChange(action.path, _.cloneDeep(data), state, action);
break;
}

View File

@ -9,11 +9,14 @@
import { useEffect } from 'react';
const convertKeysToString = (arr) => (arr||[]).map((key) => String(key));
const isPathEqual = (path1, path2) => (
JSON.stringify(path1) === JSON.stringify(path2)
Array.isArray(path1) &&
Array.isArray(path2) &&
JSON.stringify(convertKeysToString(path1)) ===
JSON.stringify(convertKeysToString(path2))
);
export const useFieldError = (path, schemaState, subscriberManager) => {
useEffect(() => {
@ -38,7 +41,9 @@ export const useFieldError = (path, schemaState, subscriberManager) => {
});
const errors = schemaState?.errors || {};
const error = isPathEqual(errors.name, path) ? errors.message : null;
const error = (
Array.isArray(errors.name) && isPathEqual(errors.name, path)
) ? errors.message : null;
return {hasError: !_.isNull(error), error};
};

View File

@ -67,13 +67,23 @@ export default function withStandardTabInfo(Component, tabId) {
};
}, []);
////////
// Special case:
//
// When the tree is being recreated during reloading on changes of some
// preferences, it is possible that the tree returns 'selected' node, but -
// it does not have the 'treeNodeInfo' as it was actually part of the
// previous instance of the tree.
//
// In that case - we consider that there is no node selected in the tree.
///////
return (
<ErrorBoundary>
<Component
{...props}
nodeItem={nodeItem}
nodeData={nodeData}
node={node}
nodeItem={treeNodeInfo ? nodeItem : undefined}
nodeData={treeNodeInfo ? nodeData : undefined}
node={treeNodeInfo ? node : undefined}
treeNodeInfo={treeNodeInfo}
isActive={isActive}
isStale={isStale}

View File

@ -124,12 +124,13 @@ class NewConnectionSchema extends BaseUISchema {
let selectedServer = _.find(
self.flatServers, (s) => s.value == state.sid
);
return {
server_name: selectedServer?.label,
did: null,
user: null,
role: null,
sid: null,
sid: state.sid,
fgcolor: selectedServer?.fgcolor,
bgcolor: selectedServer?.bgcolor,
connected: selectedServer?.connected,
@ -138,6 +139,7 @@ class NewConnectionSchema extends BaseUISchema {
deferredDepChange: (state, source, topState, actionObj) => {
return new Promise((resolve) => {
let sid = actionObj.value;
if(!_.find(self.flatServers, (s) => s.value == sid)?.connected) {
this.connectServer(sid, state.user, null, (data) => {
self.setServerConnected(sid, data.icon);