diff --git a/.rat-excludes b/.rat-excludes index 228cc2a39b0..135f96cb3ba 100644 --- a/.rat-excludes +++ b/.rat-excludes @@ -67,20 +67,8 @@ temporary_superset_ui/* # skip license checks for auto-generated test snapshots .*snap -# docs overrides for third party logos we don't have the rights to -google-big-query.svg -google-sheets.svg -ibm-db2.svg -netlify.png -postgresql.svg -snowflake.svg -ydb.svg -loading.svg -apache-solr.svg -azure.svg -superset.svg - -# docs third-party logos, i.e. docs/static/img/logos/* +# docs third-party logos (database logos, org logos, etc.) +databases/* logos/* # docs-related diff --git a/README.md b/README.md index d12a7d066cb..617852c1908 100644 --- a/README.md +++ b/README.md @@ -107,71 +107,67 @@ Here are some of the major database solutions that are supported:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Amazon Athena   + Amazon DynamoDB   + Amazon Redshift   + Apache Doris   + Apache Drill   + Apache Druid   + Apache Hive   + Apache Impala   + Apache Kylin   + Apache Pinot   + Apache Solr   + Apache Spark SQL   + Ascend   + Aurora MySQL (Data API)   + Aurora PostgreSQL (Data API)   + Azure Data Explorer   + Azure Synapse   + ClickHouse   + Cloudflare D1   + CockroachDB   + Couchbase   + CrateDB   + Databend   + Databricks   + Denodo   + Dremio   + DuckDB   + Elasticsearch   + Exasol   + Firebird   + Firebolt   + Google BigQuery   + Google Sheets   + Greenplum   + Hologres   + IBM Db2   + IBM Netezza Performance Server   + MariaDB   + Microsoft SQL Server   + MonetDB   + MongoDB   + MotherDuck   + OceanBase   + Oracle   + Presto   + RisingWave   + SAP HANA   + SAP Sybase   + Shillelagh   + SingleStore   + Snowflake   + SQLite   + StarRocks   + Superset meta database   + TDengine   + Teradata   + TimescaleDB   + Trino   + Vertica   + YDB   + YugabyteDB

diff --git a/docs/scripts/generate-database-docs.mjs b/docs/scripts/generate-database-docs.mjs index 04980f24f83..912569294ee 100644 --- a/docs/scripts/generate-database-docs.mjs +++ b/docs/scripts/generate-database-docs.mjs @@ -30,9 +30,12 @@ import { spawnSync } from 'child_process'; import fs from 'fs'; +import { createRequire } from 'module'; import path from 'path'; import { fileURLToPath } from 'url'; +const require = createRequire(import.meta.url); + const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const ROOT_DIR = path.resolve(__dirname, '../..'); @@ -41,6 +44,7 @@ const DATA_OUTPUT_DIR = path.join(DOCS_DIR, 'src/data'); const DATA_OUTPUT_FILE = path.join(DATA_OUTPUT_DIR, 'databases.json'); const MDX_OUTPUT_DIR = path.join(DOCS_DIR, 'docs/databases'); const MDX_SUPPORTED_DIR = path.join(MDX_OUTPUT_DIR, 'supported'); +const IMAGES_DIR = path.join(DOCS_DIR, 'static/img/databases'); /** * Try to run the full lib.py script with Flask context @@ -607,30 +611,109 @@ const README_PATH = path.join(ROOT_DIR, 'README.md'); const README_START_MARKER = ''; const README_END_MARKER = ''; +/** + * Read image dimensions, with fallback SVG viewBox parsing for cases where + * image-size can't handle SVG width/height attributes (e.g., scientific notation). + */ +function getImageDimensions(imgPath) { + const sizeOf = require('image-size'); + try { + const dims = sizeOf(imgPath); + // image-size may misparse SVG attributes (e.g. width="1e3" → 1). + // Fall back to viewBox parsing if a dimension looks wrong. + if (dims.type === 'svg' && (dims.width < 2 || dims.height < 2)) { + const content = fs.readFileSync(imgPath, 'utf-8'); + const vbMatch = content.match(/viewBox=["']([^"']+)["']/); + if (vbMatch) { + const parts = vbMatch[1].trim().split(/[\s,]+/).map(Number); + if (parts.length >= 4 && parts[2] > 0 && parts[3] > 0) { + return { width: parts[2], height: parts[3] }; + } + } + } + if (dims.width > 0 && dims.height > 0) { + return { width: dims.width, height: dims.height }; + } + } catch { /* fall through */ } + return null; +} + +/** + * Compute display dimensions that fit within a bounding box while preserving + * the image's aspect ratio. Enforces a minimum height so very wide logos + * remain legible. + */ +function fitToBoundingBox(imgWidth, imgHeight, maxWidth, maxHeight, minHeight) { + const ratio = imgWidth / imgHeight; + // Start at max height, compute width + let h = maxHeight; + let w = h * ratio; + // If too wide, cap width and reduce height + if (w > maxWidth) { + w = maxWidth; + h = w / ratio; + } + // If height fell below minimum, enforce minimum (allow width to exceed max) + if (h < minHeight) { + h = minHeight; + w = h * ratio; + } + return { width: Math.round(w), height: Math.round(h) }; +} + /** * Generate the database logos HTML for README.md - * Only includes databases that have logos defined + * Only includes databases that have logos and homepage URLs. + * Deduplicates by logo filename to match the docs homepage behavior. + * Reads actual image dimensions to preserve aspect ratios. */ function generateReadmeLogos(databases) { - // Get databases with logos, sorted alphabetically + // Get databases with logos and homepage URLs, sorted alphabetically, + // deduplicated by logo filename (matches docs homepage logic in index.tsx) + const seenLogos = new Set(); const dbsWithLogos = Object.entries(databases) - .filter(([, db]) => db.documentation?.logo) - .sort(([a], [b]) => a.localeCompare(b)); + .filter(([, db]) => db.documentation?.logo && db.documentation?.homepage_url) + .sort(([a], [b]) => a.localeCompare(b)) + .filter(([, db]) => { + const logo = db.documentation.logo; + if (seenLogos.has(logo)) return false; + seenLogos.add(logo); + return true; + }); if (dbsWithLogos.length === 0) { return ''; } - // Generate HTML img tags + const MAX_WIDTH = 150; + const MAX_HEIGHT = 40; + const MIN_HEIGHT = 24; + + const DOCS_BASE = 'https://superset.apache.org/docs/databases/supported'; + + // Generate linked logo tags with aspect-ratio-preserving dimensions const logoTags = dbsWithLogos.map(([name, db]) => { const logo = db.documentation.logo; - const alt = name.toLowerCase().replace(/\s+/g, '-'); - // Use docs site URL for logos - return ` `; + const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); + const imgPath = path.join(IMAGES_DIR, logo); + + const dims = getImageDimensions(imgPath); + let sizeAttrs; + if (dims) { + const { width, height } = fitToBoundingBox(dims.width, dims.height, MAX_WIDTH, MAX_HEIGHT, MIN_HEIGHT); + sizeAttrs = `width="${width}" height="${height}"`; + } else { + console.warn(` Could not read dimensions for ${logo}, using height-only fallback`); + sizeAttrs = `height="${MAX_HEIGHT}"`; + } + + const img = `${name}`; + return ` ${img}`; }); + // Use   between logos for spacing (GitHub strips style/class attributes) return `

-${logoTags.join('\n')} +${logoTags.join('  \n')}

`; } diff --git a/docs/src/data/databases.json b/docs/src/data/databases.json index d8aaa76e6c9..4a64d016760 100644 --- a/docs/src/data/databases.json +++ b/docs/src/data/databases.json @@ -1,5 +1,5 @@ { - "generated": "2026-01-27T06:14:03.276Z", + "generated": "2026-01-27T23:17:43.310Z", "statistics": { "totalDatabases": 68, "withDocumentation": 68, @@ -876,7 +876,7 @@ "module": "crate", "documentation": { "description": "CrateDB is a distributed SQL database for machine data and IoT workloads.", - "logo": "cratedb.png", + "logo": "cratedb.svg", "homepage_url": "https://crate.io/", "categories": [ "TIME_SERIES", @@ -4577,7 +4577,7 @@ "module": "risingwave", "documentation": { "description": "RisingWave is a distributed streaming database.", - "logo": "risingwave.png", + "logo": "risingwave.svg", "homepage_url": "https://risingwave.com/", "pypi_packages": [ "psycopg2", diff --git a/docs/static/img/databases/cratedb.png b/docs/static/img/databases/cratedb.png deleted file mode 100644 index dedef2414c8..00000000000 Binary files a/docs/static/img/databases/cratedb.png and /dev/null differ diff --git a/docs/static/img/databases/cratedb.svg b/docs/static/img/databases/cratedb.svg new file mode 100644 index 00000000000..c34a58e0d11 --- /dev/null +++ b/docs/static/img/databases/cratedb.svg @@ -0,0 +1 @@ +CrateDB diff --git a/docs/static/img/databases/risingwave.png b/docs/static/img/databases/risingwave.png deleted file mode 100644 index a5db27a527f..00000000000 Binary files a/docs/static/img/databases/risingwave.png and /dev/null differ diff --git a/docs/static/img/databases/risingwave.svg b/docs/static/img/databases/risingwave.svg new file mode 100644 index 00000000000..5614269b8e4 --- /dev/null +++ b/docs/static/img/databases/risingwave.svg @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/superset-frontend/packages/superset-ui-core/src/components/assets/images/loading.svg b/superset-frontend/packages/superset-ui-core/src/components/assets/images/loading.svg index ffb9eb0ce73..e588d699082 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/assets/images/loading.svg +++ b/superset-frontend/packages/superset-ui-core/src/components/assets/images/loading.svg @@ -1,3 +1,21 @@ + diff --git a/superset/db_engine_specs/crate.py b/superset/db_engine_specs/crate.py index f23ccb4dcee..916f98f3f01 100644 --- a/superset/db_engine_specs/crate.py +++ b/superset/db_engine_specs/crate.py @@ -36,7 +36,7 @@ class CrateEngineSpec(BaseEngineSpec): "description": ( "CrateDB is a distributed SQL database for machine data and IoT workloads." ), - "logo": "cratedb.png", + "logo": "cratedb.svg", "homepage_url": "https://crate.io/", "categories": [DatabaseCategory.TIME_SERIES, DatabaseCategory.OPEN_SOURCE], "pypi_packages": ["crate", "sqlalchemy-cratedb"], diff --git a/superset/db_engine_specs/risingwave.py b/superset/db_engine_specs/risingwave.py index 1a4c9cfe91b..899a9f4c834 100644 --- a/superset/db_engine_specs/risingwave.py +++ b/superset/db_engine_specs/risingwave.py @@ -28,7 +28,7 @@ class RisingWaveDbEngineSpec(PostgresEngineSpec): metadata = { "description": "RisingWave is a distributed streaming database.", - "logo": "risingwave.png", + "logo": "risingwave.svg", "homepage_url": "https://risingwave.com/", "categories": [ DatabaseCategory.ANALYTICAL_DATABASES,