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

rusackas pushed a commit to branch feat/db-engine-docs
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 1e39af16862fbb5116a39305eb18049571ca90fe
Author: Evan Rusackas <[email protected]>
AuthorDate: Sun Dec 21 12:05:53 2025 -0800

    feat(docs): show compatible databases in overview table
    
    Add compatible databases (YugabyteDB, TimescaleDB, Hologres) to the
    overview table with a link to their parent database's documentation.
    
    Compatible DBs show a "PostgreSQL compatible" tag and inherit feature
    scores from their parent.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude Opus 4.5 <[email protected]>
---
 docs/src/components/databases/DatabaseIndex.tsx | 148 ++++++++++++++++++------
 1 file changed, 114 insertions(+), 34 deletions(-)

diff --git a/docs/src/components/databases/DatabaseIndex.tsx 
b/docs/src/components/databases/DatabaseIndex.tsx
index b0fa7ffc9b..bdebc4e3f5 100644
--- a/docs/src/components/databases/DatabaseIndex.tsx
+++ b/docs/src/components/databases/DatabaseIndex.tsx
@@ -25,13 +25,36 @@ import {
   ApiOutlined,
   KeyOutlined,
   SearchOutlined,
+  LinkOutlined,
 } from '@ant-design/icons';
-import type { DatabaseData, DatabaseInfo, SortField } from './types';
+import type { DatabaseData, DatabaseInfo, CompatibleDatabase } from './types';
 
 interface DatabaseIndexProps {
   data: DatabaseData;
 }
 
+// Type for table entries (includes both regular DBs and compatible DBs)
+interface TableEntry {
+  name: string;
+  category: string;
+  score: number;
+  max_score: number;
+  timeGrainCount: number;
+  hasDrivers: boolean;
+  hasAuthMethods: boolean;
+  hasConnectionString: boolean;
+  joins?: boolean;
+  subqueries?: boolean;
+  supports_dynamic_schema?: boolean;
+  supports_catalog?: boolean;
+  ssh_tunneling?: boolean;
+  documentation?: DatabaseInfo['documentation'];
+  // For compatible databases
+  isCompatible?: boolean;
+  compatibleWith?: string;
+  compatibleDescription?: string;
+}
+
 // Category colors for visual distinction
 const CATEGORY_COLORS: Record<string, string> = {
   'Cloud - AWS': 'orange',
@@ -102,20 +125,64 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ 
data }) => {
 
   const { statistics, databases } = data;
 
-  // Convert databases object to array and add category
+  // Convert databases object to array, including compatible databases
   const databaseList = useMemo(() => {
-    return Object.entries(databases).map(([name, db]) => ({
-      ...db,
-      name,
-      category: getCategory(name),
-      timeGrainCount: countTimeGrains(db),
-      hasDrivers: (db.documentation?.drivers?.length ?? 0) > 0,
-      hasAuthMethods: (db.documentation?.authentication_methods?.length ?? 0) 
> 0,
-      hasConnectionString: Boolean(
-        db.documentation?.connection_string ||
-          (db.documentation?.drivers?.length ?? 0) > 0
-      ),
-    }));
+    const entries: TableEntry[] = [];
+
+    Object.entries(databases).forEach(([name, db]) => {
+      // Add the main database
+      entries.push({
+        ...db,
+        name,
+        category: getCategory(name),
+        timeGrainCount: countTimeGrains(db),
+        hasDrivers: (db.documentation?.drivers?.length ?? 0) > 0,
+        hasAuthMethods: (db.documentation?.authentication_methods?.length ?? 
0) > 0,
+        hasConnectionString: Boolean(
+          db.documentation?.connection_string ||
+            (db.documentation?.drivers?.length ?? 0) > 0
+        ),
+        isCompatible: false,
+      });
+
+      // Add compatible databases from this database's documentation
+      const compatibleDbs = db.documentation?.compatible_databases ?? [];
+      compatibleDbs.forEach((compat) => {
+        // Check if this compatible DB already exists as a main entry
+        const existsAsMain = Object.keys(databases).some(
+          (dbName) => dbName.toLowerCase() === compat.name.toLowerCase()
+        );
+
+        if (!existsAsMain) {
+          entries.push({
+            name: compat.name,
+            category: getCategory(compat.name),
+            // Compatible DBs inherit scores from parent
+            score: db.score,
+            max_score: db.max_score,
+            timeGrainCount: countTimeGrains(db),
+            hasDrivers: false,
+            hasAuthMethods: false,
+            hasConnectionString: Boolean(compat.connection_string),
+            joins: db.joins,
+            subqueries: db.subqueries,
+            supports_dynamic_schema: db.supports_dynamic_schema,
+            supports_catalog: db.supports_catalog,
+            ssh_tunneling: db.ssh_tunneling,
+            documentation: {
+              description: compat.description,
+              connection_string: compat.connection_string,
+              pypi_packages: compat.pypi_packages,
+            },
+            isCompatible: true,
+            compatibleWith: name,
+            compatibleDescription: `Uses ${name} driver`,
+          });
+        }
+      });
+    });
+
+    return entries;
   }, [databases]);
 
   // Filter and sort databases
@@ -146,19 +213,34 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ 
data }) => {
       title: 'Database',
       dataIndex: 'name',
       key: 'name',
-      sorter: (a: typeof filteredDatabases[0], b: typeof filteredDatabases[0]) 
=>
-        a.name.localeCompare(b.name),
-      render: (name: string, record: typeof filteredDatabases[0]) => (
-        <div>
-          <a href={`#${name.toLowerCase().replace(/\s+/g, '-')}`}>
-            <strong>{name}</strong>
-          </a>
-          <div style={{ fontSize: '12px', color: '#666' }}>
-            {record.documentation?.description?.slice(0, 80)}
-            {(record.documentation?.description?.length ?? 0) > 80 ? '...' : 
''}
+      sorter: (a: TableEntry, b: TableEntry) => a.name.localeCompare(b.name),
+      render: (name: string, record: TableEntry) => {
+        // Link to parent for compatible DBs, otherwise to own section
+        const linkTarget = record.isCompatible && record.compatibleWith
+          ? `#${record.compatibleWith.toLowerCase().replace(/\s+/g, '-')}`
+          : `#${name.toLowerCase().replace(/\s+/g, '-')}`;
+
+        return (
+          <div>
+            <a href={linkTarget}>
+              <strong>{name}</strong>
+            </a>
+            {record.isCompatible && record.compatibleWith && (
+              <Tag
+                icon={<LinkOutlined />}
+                color="geekblue"
+                style={{ marginLeft: 8, fontSize: '11px' }}
+              >
+                {record.compatibleWith} compatible
+              </Tag>
+            )}
+            <div style={{ fontSize: '12px', color: '#666' }}>
+              {record.documentation?.description?.slice(0, 80)}
+              {(record.documentation?.description?.length ?? 0) > 80 ? '...' : 
''}
+            </div>
           </div>
-        </div>
-      ),
+        );
+      },
     },
     {
       title: 'Category',
@@ -166,7 +248,7 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data 
}) => {
       key: 'category',
       width: 160,
       filters: categories.map((cat) => ({ text: cat, value: cat })),
-      onFilter: (value: React.Key | boolean, record: typeof 
filteredDatabases[0]) =>
+      onFilter: (value: React.Key | boolean, record: TableEntry) =>
         record.category === value,
       render: (category: string) => (
         <Tag color={CATEGORY_COLORS[category] || 'default'}>{category}</Tag>
@@ -177,10 +259,9 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ 
data }) => {
       dataIndex: 'score',
       key: 'score',
       width: 80,
-      sorter: (a: typeof filteredDatabases[0], b: typeof filteredDatabases[0]) 
=>
-        a.score - b.score,
+      sorter: (a: TableEntry, b: TableEntry) => a.score - b.score,
       defaultSortOrder: 'descend' as const,
-      render: (score: number, record: typeof filteredDatabases[0]) => (
+      render: (score: number, record: TableEntry) => (
         <span
           style={{
             color: score > 150 ? '#52c41a' : score > 100 ? '#1890ff' : '#666',
@@ -196,8 +277,7 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data 
}) => {
       dataIndex: 'timeGrainCount',
       key: 'timeGrainCount',
       width: 100,
-      sorter: (a: typeof filteredDatabases[0], b: typeof filteredDatabases[0]) 
=>
-        a.timeGrainCount - b.timeGrainCount,
+      sorter: (a: TableEntry, b: TableEntry) => a.timeGrainCount - 
b.timeGrainCount,
       render: (count: number) => (
         <span>{count > 0 ? `${count} grains` : '-'}</span>
       ),
@@ -206,7 +286,7 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data 
}) => {
       title: 'Features',
       key: 'features',
       width: 200,
-      render: (_: unknown, record: typeof filteredDatabases[0]) => (
+      render: (_: unknown, record: TableEntry) => (
         <div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>
           {record.joins && <Tag color="green">JOINs</Tag>}
           {record.subqueries && <Tag color="green">Subqueries</Tag>}
@@ -220,7 +300,7 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data 
}) => {
       title: 'Documentation',
       key: 'docs',
       width: 150,
-      render: (_: unknown, record: typeof filteredDatabases[0]) => (
+      render: (_: unknown, record: TableEntry) => (
         <div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>
           {record.hasConnectionString && (
             <Tag icon={<ApiOutlined />} color="default">

Reply via email to