You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/08/27 03:51:15 UTC

(superset) branch master updated: fix(sqllab): stop a database with no extra from breaking SET_DATABASES (#43216)

This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new b71293acde0 fix(sqllab): stop a database with no extra from breaking SET_DATABASES (#43216)
b71293acde0 is described below

commit b71293acde004b4e82758675a23d2cf2207f67c8
Author: Bikash <bi...@gmail.com>
AuthorDate: Thu Aug 27 09:21:08 2026 +0530

    fix(sqllab): stop a database with no extra from breaking SET_DATABASES (#43216)
    
    Co-authored-by: bikashJMV <bi...@jmv.co.in>
    Co-authored-by: Claude Opus 5 (1M context) <no...@anthropic.com>
    Co-authored-by: Evan Rusackas <ev...@rusackas.com>
---
 .../src/SqlLab/reducers/sqlLab.test.ts             | 47 ++++++++++++++++++++++
 superset-frontend/src/SqlLab/reducers/sqlLab.ts    | 23 ++++++++++-
 2 files changed, 69 insertions(+), 1 deletion(-)

diff --git a/superset-frontend/src/SqlLab/reducers/sqlLab.test.ts b/superset-frontend/src/SqlLab/reducers/sqlLab.test.ts
index 3166a902e84..11e6c199678 100644
--- a/superset-frontend/src/SqlLab/reducers/sqlLab.test.ts
+++ b/superset-frontend/src/SqlLab/reducers/sqlLab.test.ts
@@ -61,6 +61,53 @@ describe('sqlLabReducer', () => {
     });
   });
 
+  test('should default extra_json to an empty object when extra is unset', () => {
+    // `extra` is nullable in the metadata database, and JSON.parse(extra || '')
+    // is guaranteed to throw because '' is never valid JSON, so one such row
+    // took down the whole reducer.
+    const incomingDb = {
+      ...databases.result[0],
+      extra: null,
+    };
+    const incomingDbId = Number(incomingDb.id);
+
+    const action = actions.setDatabases([incomingDb] as any);
+
+    const newState = sqlLabReducer(initialState, action);
+
+    expect(newState.databases[incomingDbId]).toEqual({
+      ...incomingDb,
+      extra_json: {},
+    });
+  });
+
+  test('defaults extra_json when a database has malformed extra', () => {
+    const incomingDb = { ...databases.result[0], extra: '{not json' };
+
+    const newState = sqlLabReducer(
+      initialState,
+      actions.setDatabases([incomingDb] as any),
+    );
+
+    expect(newState.databases[Number(incomingDb.id)].extra_json).toEqual({});
+  });
+
+  test('keeps a valid extra payload', () => {
+    const incomingDb = {
+      ...databases.result[0],
+      extra: '{"engine_params": {"pool_size": 5}}',
+    };
+
+    const newState = sqlLabReducer(
+      initialState,
+      actions.setDatabases([incomingDb] as any),
+    );
+
+    expect(newState.databases[Number(incomingDb.id)].extra_json).toEqual({
+      engine_params: { pool_size: 5 },
+    });
+  });
+
   // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks
   describe('Query editors actions', () => {
     let newState: SqlLabState;
diff --git a/superset-frontend/src/SqlLab/reducers/sqlLab.ts b/superset-frontend/src/SqlLab/reducers/sqlLab.ts
index ac3b7a96d6f..874ff94e6ef 100644
--- a/superset-frontend/src/SqlLab/reducers/sqlLab.ts
+++ b/superset-frontend/src/SqlLab/reducers/sqlLab.ts
@@ -36,6 +36,27 @@ import {
 
 type SqlLabState = SqlLabRootState['sqlLab'];
 
+/**
+ * A database's `extra` column is free-form and frequently empty: it is nullable
+ * in the metadata database and the API returns it verbatim. `JSON.parse` cannot
+ * represent that, and `JSON.parse(extra || '')` is guaranteed to throw, since
+ * the empty string is never valid JSON — so a single database row with no
+ * `extra` took down the whole SET_DATABASES reducer and with it SQL Lab.
+ * Malformed JSON is treated the same way: one bad row must not cost the user
+ * every other database.
+ */
+function parseDatabaseExtra(extra: unknown): Record<string, unknown> {
+  if (typeof extra !== 'string' || extra.trim() === '') {
+    return {};
+  }
+  try {
+    const parsed = JSON.parse(extra);
+    return parsed && typeof parsed === 'object' ? parsed : {};
+  } catch {
+    return {};
+  }
+}
+
 function alterUnsavedQueryEditorState(
   state: SqlLabState,
   updatedState: Partial<QueryEditor>,
@@ -727,7 +748,7 @@ export default function sqlLabReducer(
       (action.databases as any[])!.forEach((db: any) => {
         databases[db.id] = {
           ...db,
-          extra_json: JSON.parse(db.extra || ''),
+          extra_json: parseDatabaseExtra(db.extra),
         };
       });
       return {