public async addResultData()

in gui/frontend/src/script-execution/PresentationInterface.tsx [438:704]


    public async addResultData(data: IExecutionResult, dataOptions: IResponseDataOptions,
        presentationOptions?: IPresentationOptions, queryType?: QueryType): Promise<void> {

        // If this is the first result we receive, switch to the loading state.
        const isBusy = this.loadingState === LoadingState.Loading || this.loadingState === LoadingState.Waiting;
        if (this.waitTimer || isBusy) {
            if (this.waitTimer) {
                clearTimeout(this.waitTimer);
                this.waitTimer = null;
            }

            // Execution is finished if either we have execution info (and no result data so far) or we got
            // a chat result with an answer.
            if (!this.resultData && "executionInfo" in data) {
                this.changeLoadingState(LoadingState.Idle);
            } else if (data.type === "chat" && "answer" in data && data.answer !== "") {
                this.changeLoadingState(LoadingState.Idle);
            } else if (data.type !== "chat") {
                this.changeLoadingState(LoadingState.Loading);
            }
        }

        switch (data.type) {
            case "text": {
                if (!this.resultData) {
                    // No data yet. Take the given one unchanged.
                    this.resultData = data;
                } else if (this.resultData.type === "resultSets") {
                    // There's result set data shown currently. In this case add the text to the output tab.
                    // If no output tab exists already, create it.
                    if (!this.resultData.output) {
                        this.resultData.output = [];
                    }

                    if (data.text) {
                        this.resultData.output.push(...data.text);
                    }
                } else if (this.resultData.type === "graphData") {
                    // TODO: If graph data is visible, add the text as another entry to the graph page.
                } else if (this.resultData.type === "chat") {
                    // TODO: If a chat result is visible, add the text as another entry to the page.
                } else if (this.resultData.type === "about") {
                    // TODO: If a about result is visible, add the text as another entry to the page.
                } else {
                    // Text data to render.
                    if (!this.resultData.text) {
                        this.resultData.text = data.text;
                    } else if (data.text) {
                        // If the last entry has the same language as the new data, then merge them.
                        const lastEntry = this.resultData.text[this.resultData.text.length - 1];
                        while (data.text.length > 0 && data.text[0].index === lastEntry.index
                            && data.text[0].language === lastEntry?.language) {
                            lastEntry.content += data.text[0].content;
                            data.text.shift();
                        }

                        // Add the remaining text data.
                        this.resultData.text.push(...data.text);
                        if (data.executionInfo) {
                            this.resultData.executionInfo = data.executionInfo;

                            // We got execution info, so we can return into idle state.
                            this.changeLoadingState(LoadingState.Idle);
                        }
                    }
                }

                break;
            }

            case "resultSetRows": {
                if (!this.resultData) {
                    if (data.rows.length === 0 && queryType !== QueryType.Select) {
                        this.addEmptyResultSetAsText(data, dataOptions);
                    } else {
                        this.resultData = {
                            type: "resultSets",
                            sets: [{
                                type: "resultSet",
                                index: dataOptions.index,
                                subIndex: dataOptions.subIndex,
                                resultId: dataOptions?.resultId,
                                sql: dataOptions.sql ?? "",
                                columns: data.columns ?? [],
                                data,
                                updatable: dataOptions.updatable ?? false,
                                fullTableName: dataOptions.fullTableName ?? "",
                            }],
                        };
                    }
                } else {
                    let needUpdate = true;
                    switch (this.resultData.type) {
                        case "resultSets": {
                            // Find the target set (tab) to add the data to or create a new one.
                            const resultSets = this.resultData.sets;

                            // Add the data to our internal storage, to support switching tabs
                            // for multiple result sets.
                            let index = -1;
                            const resultSet = resultSets.find((candidate, candidateIndex) => {
                                if (candidate.resultId === dataOptions.resultId) {
                                    index = candidateIndex;

                                    return true;
                                }

                                return false;
                            });

                            if (resultSet) {
                                // No need for re-rendering if the result set already exists and
                                // we only add new rows.
                                if (data.executionInfo) {
                                    resultSet.data.executionInfo = data.executionInfo;
                                } else {
                                    needUpdate = false;
                                }

                                // Internal storage to support result tab switching.
                                if (dataOptions.replaceData) {
                                    resultSet.data.rows = data.rows;
                                } else {
                                    resultSet.data.rows.push(...data.rows);
                                }

                                // Also update columns if they were given.
                                if (data.columns && data.columns.length > 0) {
                                    resultSet.columns = data.columns;
                                }

                                resultSet.data.currentPage = data.currentPage;
                                resultSet.data.hasMoreRows = data.hasMoreRows;

                                if (this.currentSet === index + 1) {
                                    // Tab is visible, so send it the new data.
                                    if (data.columns && data.columns.length > 0) {
                                        await this.resultRef.current?.updateColumns(dataOptions.resultId,
                                            data.columns);
                                    }
                                    await this.resultRef.current?.addData(data, dataOptions.resultId,
                                        dataOptions.replaceData);
                                }
                            } else {
                                if (data.rows.length === 0 && queryType !== QueryType.Select) {
                                    this.addEmptyResultSetAsText(data, dataOptions);
                                } else {
                                    // No existing result set tab found - create it.
                                    this.resultData.sets.push({
                                        type: "resultSet",
                                        index: dataOptions.index,
                                        subIndex: dataOptions.subIndex,
                                        resultId: dataOptions?.resultId,
                                        sql: dataOptions.sql ?? "",
                                        columns: data.columns ?? [],
                                        data,
                                        updatable: dataOptions.updatable ?? false,
                                        fullTableName: dataOptions.fullTableName ?? "",
                                    });
                                }
                            }

                            break;
                        }

                        case "text": {
                            if (data.rows.length === 0) {
                                this.addEmptyResultSetAsText(data, dataOptions);
                            } else {
                                // Move the text over to the output tab of the new result set data.
                                this.resultData = {
                                    type: "resultSets",
                                    sets: [{
                                        type: "resultSet",
                                        index: dataOptions.index,
                                        resultId: dataOptions?.resultId,
                                        sql: dataOptions.sql ?? "",
                                        columns: data.columns ?? [],
                                        data,
                                        updatable: dataOptions.updatable ?? false,
                                        fullTableName: dataOptions.fullTableName ?? "",
                                    }],
                                    output: this.resultData.text,
                                };
                            }

                            break;
                        }

                        default: {
                            // Replace the existing data entirely.
                            this.resultData = {
                                type: "resultSets",
                                sets: [{
                                    type: "resultSet",
                                    index: dataOptions.index,
                                    resultId: dataOptions?.resultId,
                                    sql: dataOptions.sql ?? "",
                                    columns: data.columns ?? [],
                                    data,
                                    updatable: dataOptions.updatable ?? false,
                                    fullTableName: dataOptions.fullTableName ?? "",
                                }],
                            };

                            break;
                        }
                    }

                    // Everything but result sets is finished implicitly when the result comes in.
                    let allFinished = true;
                    if (this.resultData.type === "resultSets") {
                        // Result sets may still be waiting for more row data.
                        allFinished = this.resultData.sets.every((value) => {
                            return value.data.executionInfo != null;
                        });

                    }

                    if (allFinished && this.loadingState !== LoadingState.Idle) {
                        this.changeLoadingState(LoadingState.Idle);
                    }

                    if (!needUpdate) {
                        return;
                    }
                }

                break;
            }

            case "graphData": {
                // New graph always replaces what was there before.
                this.resultData = data;
                this.minHeight = 200;

                break;
            }

            case "chat": {
                // Chat results display
                this.resultData = data;
                this.minHeight = 200;
                if (data.answer !== "") {
                    // We got the whole answer, so we can stop waiting.
                    this.changeLoadingState(LoadingState.Idle);
                }

                break;
            }

            case "about": {
                // About results display
                this.resultData = data;
                this.minHeight = 220;

                break;
            }

            default:
        }

        // The backend is only set when the presentation is active.
        if (this.#backend) {
            this.renderResults(presentationOptions);
        }
    }