Skip to content

dbeaver/pro#7562 [CB] keep data editor filter state after reconnect#4238

Closed
SychevAndrey wants to merge 25 commits intodevelfrom
7562-cb-keep-data-editor-filter-state-after-reconnect
Closed

dbeaver/pro#7562 [CB] keep data editor filter state after reconnect#4238
SychevAndrey wants to merge 25 commits intodevelfrom
7562-cb-keep-data-editor-filter-state-after-reconnect

Conversation

@SychevAndrey
Copy link
Copy Markdown
Contributor

No description provided.

replaced private restorePendingState()  with public restoreViewState(pinnedColumnNames, columnOrderNames?) that takes data directly as arguments
bug with skipping constraints if any without a position.
if (!prevColumn) {
        return;
      }
@SychevAndrey SychevAndrey marked this pull request as ready for review March 26, 2026 11:12
@SychevAndrey SychevAndrey changed the title 7562 cb keep data editor filter state after reconnect dbeaver/pro#7562 [CB] keep data editor filter state after reconnect Mar 26, 2026
@SychevAndrey SychevAndrey self-assigned this Mar 26, 2026
if (!usedIndices.has(key)) {
newOrder.push(key);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this check stand for?
What columns didn't we see in the previous cycle above?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The answer is the same as for indecies. Someone could edit our table and add some columns. So we will not have them in our orderColumns array. Here we put them in the end (I am open to other solutions)

export interface IDataViewerPersistedState {
constraints: IPersistedConstraint[];
whereFilter: string;
pinnedColumns: string[];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused that we use column names as unique identifiers. This feels tricky and has a bug potential
In most of DBs it is forbidden to name 2 columns with the same name it is true. But is it in all DBs? I have a little doubt here

Also we can have this kind of case:
SELECT a AS col, b AS col FROM table;

We execute this in sql editor. See the table. What would happen? Is there a chance one of the columns disappears? Or any other related bug happened?

My suggestion is to add unique index for each column and it will resolve a lot of potential issues

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what will be the index then? How would you resolve it if someone added a column or two while you was working on something else and then reopen the table?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what will be the index then?

The backend sends the id for each column

How would you resolve it if someone added a column or two while you were working on something else and then reopened the table?

Maybe append it to the end or the beginning of the current view?

console.warn('[DataViewerTableState] Auto-save failed', exception);
}
},
{ delay: 1000, fireImmediately: false, equals: comparer.structural },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can not throttle it after any changes, but debounce? like save the state once in 5 seconds after any change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it spam the table with requests every second if you're actively working with it? if so debounce would reduce amount of requests to save the whole state

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok I see. I don't think it's a big problem with comparing on each action, but I guess the deep equal is overhead here, so I just simplify it a bit and let MobX compare by pointer. You should make 1000 operation/sec to make it lag :) effect is already delayed, I guess 5 sec is too much, localstorage is fast enough and the size of saved object is relatively small until someone has thousands of columns and pinned half of them

Comment on lines +154 to +182
private validateState(data: IDataViewerPersistedState): boolean {
if (!data || typeof data !== 'object') {
return false;
}

if (!Array.isArray(data.constraints)) {
return false;
}

for (const c of data.constraints) {
if (!c || typeof c.attributeName !== 'string' || c.attributeName.length === 0) {
return false;
}
}

if (typeof data.whereFilter !== 'string') {
return false;
}

if (!Array.isArray(data.pinnedColumns) || !data.pinnedColumns.every(c => typeof c === 'string')) {
return false;
}

if (data.columnOrder !== undefined && (!Array.isArray(data.columnOrder) || !data.columnOrder.every(c => typeof c === 'string'))) {
return false;
}

return true;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like zod is gonna be quite handy here

Nexus6v2
Nexus6v2 previously approved these changes Mar 26, 2026
@SychevAndrey SychevAndrey requested a review from Wroud March 27, 2026 11:49
return this.columnKeys
.filter(key => this.isColumnPinned(key))
.map(key => this.getColumnName(key))
.filter((name): name is string => name !== undefined);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use isDefined for that

const name = this.getColumnName(key);

if (name) {
nameToIndex.set(name, key.index);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like we need to add new function, something like getColumnIndexByName

if (pinnedColumnNames.length > 0) {
const keys = pinnedColumnNames
.map(name => this.columnKeys.find(k => this.getColumnName(k) === name))
.filter((k): k is IGridColumnKey => k !== undefined);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isDefined?

updateConstraintsForResult(this.source, result);
}

private getColumnNameByPosition(attributePosition: number): string | undefined {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably it should be implemented on the GridViewAction level as we might need it in different places, we have similar logic there, in general, logic that find data column by certain criteria should be gathered in one place and be accessible from different places. Also we have similar logic inside useTableData

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it's better to implement on the GridDataResultAction side, but there is problem GridDataResultAction is abstraction over grid structures, but this function is requires specific ResultSet data type, so it should be implemented in the ResultSetDataAction and can be abstract in the GridDataResultAction

}
}

restoreViewState(pinnedColumnNames: string[], columnOrderNames?: string[]): void {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it's better to pass an interface as a single argument to this function, so it can be easily extended if needed

updateConstraintsForResult(this.source, result);
}

private getColumnNameByPosition(attributePosition: number): string | undefined {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it's better to implement on the GridDataResultAction side, but there is problem GridDataResultAction is abstraction over grid structures, but this function is requires specific ResultSet data type, so it should be implemented in the ResultSetDataAction and can be abstract in the GridDataResultAction

let viewStateRestored = false;

const cancelViewRestore = hasViewState
? reaction(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since you using reaction here, it will be much easier to have some state interface, and read-mutate it directly in the database data actions, so it will be automatically synced and restored

i think you can introduce the new database data action for persisted state and re-use it in the actions

@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Apr 2, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity

Metric Results
Complexity 0

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

@SychevAndrey SychevAndrey requested a review from Wroud April 3, 2026 12:43
@SychevAndrey SychevAndrey deleted the 7562-cb-keep-data-editor-filter-state-after-reconnect branch April 3, 2026 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants