Repository: ClickHouse/ClickHouse
Pull request #78119: Added `index_length` column to `information_schema.tables`
Author: login: KrzaQ
Labels: name: pr-improvement
description: Pull request with some product improvements
name: can be tested
description: Allows running workflows for external contributors
name: pr-synced-to-cloud
description: The PR is synced to the cloud repo
Created: 2025-03-22T11:58:16Z | Merged: 2025-03-31T15:46:46Z

Resolves #57590

### Changelog category (leave one):
- Improvement

### Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):
Added column `index_length_column` to `information_schema.tables` for better compatibility with MySQL.

Linked issue #57590: Add `information_schema.tables.index_length`
**Use case**

MySQL compatibility.

**Describe the solution you'd like**

The sum of the compressed size of the primary key, secondary indices, and all marks.
Linked issue comments: user: login: KL-16
created_at: 2023-12-07T18:45:22Z
body: Hi @alexey-milovidov I would like to work on this issue
author_association: NONE
user: login: alexey-milovidov
created_at: 2023-12-07T19:08:13Z
body: @KL-16, thank you!
author_association: MEMBER

Diff:
diff --git a/docs/en/operations/system-tables/information_schema.md b/docs/en/operations/system-tables/information_schema.md
index 4b7fe5b14648..c48ebb3ffbe5 100644
--- a/docs/en/operations/system-tables/information_schema.md
+++ b/docs/en/operations/system-tables/information_schema.md
@@ -205,10 +205,9 @@ Columns:
     - `FOREIGN TABLE`
     - `LOCAL TEMPORARY`
     - `SYSTEM VIEW`
-- `table_rows` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) — The total
-  number of rows. NULL if it could not be determined.
-- `data_length` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) — The size of
-  the data on-disk. NULL if it could not be determined.
+- `table_rows` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) — The total number of rows. NULL if it could not be determined.
+- `data_length` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) — The size of the data on-disk. NULL if it could not be determined.
+- `index_length` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) — The total size of the primary key, secondary indexes, and all marks.
 - `table_collation` ([Nullable](../../sql-reference/data-types/nullable.md)([String](../../sql-reference/data-types/string.md))) — The table default collation. Always `utf8mb4_0900_ai_ci`.
 - `table_comment` ([Nullable](../../sql-reference/data-types/nullable.md)([String](../../sql-reference/data-types/string.md))) — The comment used when creating the table.
 
diff --git a/src/Storages/System/attachInformationSchemaTables.cpp b/src/Storages/System/attachInformationSchemaTables.cpp
index 701e5c05fc12..330601d9f395 100644
--- a/src/Storages/System/attachInformationSchemaTables.cpp
+++ b/src/Storages/System/attachInformationSchemaTables.cpp
@@ -64,6 +64,7 @@ static constexpr std::string_view tables = R"(
         `table_type` String,
         `table_rows` Nullable(UInt64),
         `data_length` Nullable(UInt64),
+        `index_length` Nullable(UInt64),
         `table_collation` Nullable(String),
         `table_comment` Nullable(String),
         `TABLE_CATALOG` String,
@@ -88,6 +89,9 @@ static constexpr std::string_view tables = R"(
                 )            AS table_type,
         total_rows AS table_rows,
         total_bytes AS data_length,
+        sum(p.primary_key_size + p.marks_bytes
+            + p.secondary_indices_compressed_bytes + p.secondary_indices_marks_bytes
+        ) AS index_length,
         'utf8mb4_0900_ai_ci' AS table_collation,
         comment              AS table_comment,
         table_catalog        AS TABLE_CATALOG,
@@ -98,7 +102,17 @@ static constexpr std::string_view tables = R"(
         data_length          AS DATA_LENGTH,
         table_collation      AS TABLE_COLLATION,
         table_comment        AS TABLE_COMMENT
-    FROM system.tables
+    FROM system.tables t
+    LEFT JOIN system.parts p ON (t.database = p.database AND t.name = p.table)
+    GROUP BY
+        t.database,
+        t.name,
+        t.is_temporary,
+        t.engine,
+        t.has_own_data,
+        t.total_rows,
+        t.total_bytes,
+        t.comment
 )";
 
 static constexpr std::string_view views = R"(
diff --git a/tests/integration/test_select_access_rights/test_from_system_tables.py b/tests/integration/test_select_access_rights/test_from_system_tables.py
index 5fb69d266258..9d5d509d65a6 100644
--- a/tests/integration/test_select_access_rights/test_from_system_tables.py
+++ b/tests/integration/test_select_access_rights/test_from_system_tables.py
@@ -162,6 +162,19 @@ def test_information_schema():
     )
 
     node.query("GRANT SELECT ON information_schema.* TO sqluser")
+    expected_error = (
+        "necessary to have the grant SELECT(database, `table`) ON system.parts"
+    )
+    assert expected_error in node.query_and_get_error(
+        "SELECT count() FROM information_schema.tables WHERE table_name='table1'",
+        user="sqluser",
+    )
+    assert expected_error in node.query_and_get_error(
+        "SELECT count() FROM information_schema.tables WHERE table_name='table2'",
+        user="sqluser",
+    )
+
+    node.query("GRANT SELECT ON system.parts TO sqluser")
     assert (
         node.query(
             "SELECT count() FROM information_schema.tables WHERE table_name='table1'",
diff --git a/tests/queries/0_stateless/01161_information_schema.reference b/tests/queries/0_stateless/01161_information_schema.reference
index 6fbcce3ba940..0a69b764d1e7 100644
--- a/tests/queries/0_stateless/01161_information_schema.reference
+++ b/tests/queries/0_stateless/01161_information_schema.reference
@@ -42,11 +42,11 @@ views
 INFORMATION_SCHEMA	INFORMATION_SCHEMA	default	\N	\N	\N	\N	INFORMATION_SCHEMA	INFORMATION_SCHEMA	default	\N	\N	\N	\N
 information_schema	information_schema	default	\N	\N	\N	\N	information_schema	information_schema	default	\N	\N	\N	\N
 -- information_schema.tables
-default	default	kcu1	BASE TABLE	0	0	utf8mb4_0900_ai_ci		default	default	kcu1	BASE TABLE	0	0	utf8mb4_0900_ai_ci	
-default	default	kcu2	BASE TABLE	0	0	utf8mb4_0900_ai_ci		default	default	kcu2	BASE TABLE	0	0	utf8mb4_0900_ai_ci	
-default	default	mv	VIEW	0	0	utf8mb4_0900_ai_ci		default	default	mv	VIEW	0	0	utf8mb4_0900_ai_ci	
-default	default	t	FOREIGN TABLE	0	0	utf8mb4_0900_ai_ci		default	default	t	FOREIGN TABLE	0	0	utf8mb4_0900_ai_ci	
-default	default	v	VIEW	\N	\N	utf8mb4_0900_ai_ci		default	default	v	VIEW	\N	\N	utf8mb4_0900_ai_ci	
+default	default	kcu1	BASE TABLE	0	0	0	utf8mb4_0900_ai_ci		default	default	kcu1	BASE TABLE	0	0	utf8mb4_0900_ai_ci	
+default	default	kcu2	BASE TABLE	0	0	0	utf8mb4_0900_ai_ci		default	default	kcu2	BASE TABLE	0	0	utf8mb4_0900_ai_ci	
+default	default	mv	VIEW	0	0	0	utf8mb4_0900_ai_ci		default	default	mv	VIEW	0	0	utf8mb4_0900_ai_ci	
+default	default	t	FOREIGN TABLE	0	0	0	utf8mb4_0900_ai_ci		default	default	t	FOREIGN TABLE	0	0	utf8mb4_0900_ai_ci	
+default	default	v	VIEW	\N	\N	0	utf8mb4_0900_ai_ci		default	default	v	VIEW	\N	\N	utf8mb4_0900_ai_ci	
 -- information_schema.views
 default	default	mv	SELECT * FROM system.one	NONE	NO	YES	NO	NO	NO	default	default	mv	SELECT * FROM system.one	NONE	NO	YES	NO	NO	NO
 default	default	v	SELECT n, f FROM default.t	NONE	NO	NO	NO	NO	NO	default	default	v	SELECT n, f FROM default.t	NONE	NO	NO	NO	NO	NO
diff --git a/tests/queries/0_stateless/02206_information_schema_show_database.reference b/tests/queries/0_stateless/02206_information_schema_show_database.reference
index 2cc93c56e82a..36c5273a9412 100644
--- a/tests/queries/0_stateless/02206_information_schema_show_database.reference
+++ b/tests/queries/0_stateless/02206_information_schema_show_database.reference
@@ -1,6 +1,6 @@
 CREATE DATABASE INFORMATION_SCHEMA\nENGINE = Memory
 CREATE VIEW INFORMATION_SCHEMA.COLUMNS\n(\n    `table_catalog` String,\n    `table_schema` String,\n    `table_name` String,\n    `column_name` String,\n    `ordinal_position` UInt64,\n    `column_default` String,\n    `is_nullable` String,\n    `data_type` String,\n    `character_maximum_length` Nullable(UInt64),\n    `character_octet_length` Nullable(UInt64),\n    `numeric_precision` Nullable(UInt64),\n    `numeric_precision_radix` Nullable(UInt64),\n    `numeric_scale` Nullable(UInt64),\n    `datetime_precision` Nullable(UInt64),\n    `character_set_catalog` Nullable(String),\n    `character_set_schema` Nullable(String),\n    `character_set_name` Nullable(String),\n    `collation_catalog` Nullable(String),\n    `collation_schema` Nullable(String),\n    `collation_name` Nullable(String),\n    `domain_catalog` Nullable(String),\n    `domain_schema` Nullable(String),\n    `domain_name` Nullable(String),\n    `extra` Nullable(String),\n    `column_comment` String,\n    `column_type` String,\n    `TABLE_CATALOG` String,\n    `TABLE_SCHEMA` String,\n    `TABLE_NAME` String,\n    `COLUMN_NAME` String,\n    `ORDINAL_POSITION` UInt64,\n    `COLUMN_DEFAULT` String,\n    `IS_NULLABLE` String,\n    `DATA_TYPE` String,\n    `CHARACTER_MAXIMUM_LENGTH` Nullable(UInt64),\n    `CHARACTER_OCTET_LENGTH` Nullable(UInt64),\n    `NUMERIC_PRECISION` Nullable(UInt64),\n    `NUMERIC_PRECISION_RADIX` Nullable(UInt64),\n    `NUMERIC_SCALE` Nullable(UInt64),\n    `DATETIME_PRECISION` Nullable(UInt64),\n    `CHARACTER_SET_CATALOG` Nullable(String),\n    `CHARACTER_SET_SCHEMA` Nullable(String),\n    `CHARACTER_SET_NAME` Nullable(String),\n    `COLLATION_CATALOG` Nullable(String),\n    `COLLATION_SCHEMA` Nullable(String),\n    `COLLATION_NAME` Nullable(String),\n    `DOMAIN_CATALOG` Nullable(String),\n    `DOMAIN_SCHEMA` Nullable(String),\n    `DOMAIN_NAME` Nullable(String),\n    `EXTRA` Nullable(String),\n    `COLUMN_COMMENT` String,\n    `COLUMN_TYPE` String\n)\nSQL SECURITY INVOKER\nAS SELECT\n    database AS table_catalog,\n    database AS table_schema,\n    `table` AS table_name,\n    name AS column_name,\n    position AS ordinal_position,\n    default_expression AS column_default,\n    type LIKE \'Nullable(%)\' AS is_nullable,\n    type AS data_type,\n    character_octet_length AS character_maximum_length,\n    character_octet_length,\n    numeric_precision,\n    numeric_precision_radix,\n    numeric_scale,\n    datetime_precision,\n    NULL AS character_set_catalog,\n    NULL AS character_set_schema,\n    NULL AS character_set_name,\n    NULL AS collation_catalog,\n    NULL AS collation_schema,\n    NULL AS collation_name,\n    NULL AS domain_catalog,\n    NULL AS domain_schema,\n    NULL AS domain_name,\n    multiIf(default_kind = \'DEFAULT\', \'DEFAULT_GENERATED\', default_kind = \'MATERIALIZED\', \'STORED GENERATED\', default_kind = \'ALIAS\', \'VIRTUAL GENERATED\', \'\') AS extra,\n    comment AS column_comment,\n    type AS column_type,\n    table_catalog AS TABLE_CATALOG,\n    table_schema AS TABLE_SCHEMA,\n    table_name AS TABLE_NAME,\n    column_name AS COLUMN_NAME,\n    ordinal_position AS ORDINAL_POSITION,\n    column_default AS COLUMN_DEFAULT,\n    is_nullable AS IS_NULLABLE,\n    data_type AS DATA_TYPE,\n    character_maximum_length AS CHARACTER_MAXIMUM_LENGTH,\n    character_octet_length AS CHARACTER_OCTET_LENGTH,\n    numeric_precision AS NUMERIC_PRECISION,\n    numeric_precision_radix AS NUMERIC_PRECISION_RADIX,\n    numeric_scale AS NUMERIC_SCALE,\n    datetime_precision AS DATETIME_PRECISION,\n    character_set_catalog AS CHARACTER_SET_CATALOG,\n    character_set_schema AS CHARACTER_SET_SCHEMA,\n    character_set_name AS CHARACTER_SET_NAME,\n    collation_catalog AS COLLATION_CATALOG,\n    collation_schema AS COLLATION_SCHEMA,\n    collation_name AS COLLATION_NAME,\n    domain_catalog AS DOMAIN_CATALOG,\n    domain_schema AS DOMAIN_SCHEMA,\n    domain_name AS DOMAIN_NAME,\n    extra AS EXTRA,\n    column_comment AS COLUMN_COMMENT,\n    column_type AS COLUMN_TYPE\nFROM system.columns
-CREATE VIEW INFORMATION_SCHEMA.TABLES (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
-CREATE VIEW INFORMATION_SCHEMA.tables (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
-CREATE VIEW information_schema.TABLES (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
-CREATE VIEW information_schema.tables (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
+CREATE VIEW INFORMATION_SCHEMA.TABLES (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `index_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, sum(((p.primary_key_size + p.marks_bytes) + p.secondary_indices_compressed_bytes) + p.secondary_indices_marks_bytes) AS index_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables AS t LEFT JOIN system.parts AS p ON (t.database = p.database) AND (t.name = p.`table`) GROUP BY t.database, t.name, t.is_temporary, t.engine, t.has_own_data, t.total_rows, t.total_bytes, t.comment
+CREATE VIEW INFORMATION_SCHEMA.tables (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `index_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, sum(((p.primary_key_size + p.marks_bytes) + p.secondary_indices_compressed_bytes) + p.secondary_indices_marks_bytes) AS index_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables AS t LEFT JOIN system.parts AS p ON (t.database = p.database) AND (t.name = p.`table`) GROUP BY t.database, t.name, t.is_temporary, t.engine, t.has_own_data, t.total_rows, t.total_bytes, t.comment
+CREATE VIEW information_schema.TABLES (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `index_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, sum(((p.primary_key_size + p.marks_bytes) + p.secondary_indices_compressed_bytes) + p.secondary_indices_marks_bytes) AS index_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables AS t LEFT JOIN system.parts AS p ON (t.database = p.database) AND (t.name = p.`table`) GROUP BY t.database, t.name, t.is_temporary, t.engine, t.has_own_data, t.total_rows, t.total_bytes, t.comment
+CREATE VIEW information_schema.tables (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `index_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, sum(((p.primary_key_size + p.marks_bytes) + p.secondary_indices_compressed_bytes) + p.secondary_indices_marks_bytes) AS index_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables AS t LEFT JOIN system.parts AS p ON (t.database = p.database) AND (t.name = p.`table`) GROUP BY t.database, t.name, t.is_temporary, t.engine, t.has_own_data, t.total_rows, t.total_bytes, t.comment
diff --git a/tests/queries/0_stateless/03397_information_schema_tables_index_length.reference b/tests/queries/0_stateless/03397_information_schema_tables_index_length.reference
new file mode 100644
index 000000000000..872fd820314f
--- /dev/null
+++ b/tests/queries/0_stateless/03397_information_schema_tables_index_length.reference
@@ -0,0 +1,4 @@
+OK
+OK
+2
+OK
diff --git a/tests/queries/0_stateless/03397_information_schema_tables_index_length.sql b/tests/queries/0_stateless/03397_information_schema_tables_index_length.sql
new file mode 100644
index 000000000000..4ec3d4a1832e
--- /dev/null
+++ b/tests/queries/0_stateless/03397_information_schema_tables_index_length.sql
@@ -0,0 +1,29 @@
+-- https://github.com/ClickHouse/ClickHouse/issues/57590
+
+DROP TABLE IF EXISTS tab_with_primary_key_index;
+CREATE TABLE tab_with_primary_key_index (id UInt32, a UInt32) ENGINE = MergeTree ORDER BY id;
+INSERT INTO tab_with_primary_key_index SELECT number, number % 2 ? 1 : number FROM numbers(10);
+
+DROP TABLE IF EXISTS tab_with_primary_key_index_and_skipping_index;
+CREATE TABLE tab_with_primary_key_index_and_skipping_index (id UInt32, a UInt32, INDEX idx a TYPE set(0)) ENGINE = MergeTree ORDER BY id;
+INSERT INTO tab_with_primary_key_index_and_skipping_index SELECT number, number % 2 ? 1 : number FROM numbers(10);
+
+-- Check that information_schema.tables.index_length is larger than 0 for both tables
+SELECT if(index_length > 0, 'OK', 'FAIL')
+FROM information_schema.tables
+WHERE table_name LIKE 'tab_with_primary_key_index%'
+    AND table_schema = currentDatabase();
+
+-- A very crude check that information_schema.tables.index_length is different for both tables
+SELECT count(*)
+FROM information_schema.tables
+WHERE table_name LIKE 'tab_with_primary_key_index%'
+    AND table_schema = currentDatabase();
+
+DROP TABLE tab_with_primary_key_index;
+
+-- Check that information_schema.tables.index_length is 0 for non-MergeTree tables
+SELECT if(index_length = 0, 'OK', 'FAIL')
+FROM information_schema.tables
+WHERE table_name = 'tables'
+    AND table_schema = 'system'; -- table engine is 'SystemTables'


Reviews:
user: login: rschu1ze
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-24T12:31:59Z
user: login: KrzaQ
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T22:48:12Z
user: login: jsc0218
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T21:19:38Z
user: login: KrzaQ
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T21:31:16Z
user: login: rschu1ze
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-27T11:55:15Z

Review comments:
diff_hunk: @@ -84,6 +84,8 @@ Columns:
 
 - `loading_dependent_table` ([Array](../../sql-reference/data-types/array.md)([String](../../sql-reference/data-types/string.md))) - Dependent loading table.
 
+- `index_size` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) - Total size of all indexes in the table.
path: docs/en/operations/system-tables/tables.md
user: login: rschu1ze
body: This new field would duplicate what is already shown in `system.parts`. Let's _only_ change `information_schema.tables` (which is defined by the SQL standard and generally not very usable, it only exists for compatibility with 3rd party tools).
created_at: 2025-03-24T12:30:53Z
author_association: MEMBER
diff_hunk: @@ -178,11 +178,21 @@ class IStorage : public std::enable_shared_from_this<IStorage>, public TypePromo
     using ColumnSizeByName = std::unordered_map<std::string, ColumnSize>;
     virtual ColumnSizeByName getColumnSizes() const { return {}; }
 
+    /// Get the total length of all indexes in the table.
path: src/Storages/IStorage.h
user: login: rschu1ze
body: No need to change anything in C++, all existing data can be gathered from fields

```
primary_key_size
marks_bytes
secondary_indices_compressed_bytes
secondary_indices_uncompressed_bytes
secondary_indices_marks_bytes
```

in `system.parts`.
created_at: 2025-03-24T12:31:38Z
author_association: MEMBER
diff_hunk: @@ -178,11 +178,21 @@ class IStorage : public std::enable_shared_from_this<IStorage>, public TypePromo
     using ColumnSizeByName = std::unordered_map<std::string, ColumnSize>;
     virtual ColumnSizeByName getColumnSizes() const { return {}; }
 
+    /// Get the total length of all indexes in the table.
path: src/Storages/IStorage.h
user: login: KrzaQ
body: Fair enough. I looked at how the information_schema views are defined, and they all are simple select queries, so I thought that was the desired effect.
created_at: 2025-03-24T22:48:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -209,6 +209,7 @@ Columns:
   number of rows. NULL if it could not be determined.
 - `data_length` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) — The size of
   the data on-disk. NULL if it could not be determined.
+- `index_length` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) — The total size of all the compressed indexes, marks, compressed secondary indexes, and their marks. This column is computed from the contents of the [system.parts](../../operations/system-tables/parts.md) table.
path: docs/en/operations/system-tables/information_schema.md
user: login: jsc0218
body: Maybe more clear stating the total size of primary key, secondary indexes and their marks?
created_at: 2025-03-26T21:19:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -209,6 +209,7 @@ Columns:
   number of rows. NULL if it could not be determined.
 - `data_length` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) — The size of
   the data on-disk. NULL if it could not be determined.
+- `index_length` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt64](../../sql-reference/data-types/int-uint.md))) — The total size of all the compressed indexes, marks, compressed secondary indexes, and their marks. This column is computed from the contents of the [system.parts](../../operations/system-tables/parts.md) table.
path: docs/en/operations/system-tables/information_schema.md
user: login: KrzaQ
body: What do you think of "The total size of the primary key, secondary indexes, and all marks. This value is derived from [system.parts](https://github.com/ClickHouse/operations/system-tables/parts.md)."?
created_at: 2025-03-26T21:31:15Z
author_association: CONTRIBUTOR

PR comments:
user: login: CLAassistant
created_at: 2025-03-22T11:58:23Z
body: [![CLA assistant check](https://cla-assistant.io/pull/badge/signed)](https://cla-assistant.io/ClickHouse/ClickHouse?pullRequest=78119) <br/>All committers have signed the CLA.
author_association: NONE
user: login: clickhouse-gh[bot]
created_at: 2025-03-22T15:47:24Z
body: Workflow [[PR](https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=78119&sha=latest&name_0=PR)], commit [21c7dc17]
author_association: CONTRIBUTOR
user: login: KrzaQ
created_at: 2025-03-25T18:33:43Z
body: I _think_ the fuzzer failures are unrelated to my change, and the rest of the tests pass.
author_association: CONTRIBUTOR
user: login: KrzaQ
created_at: 2025-03-27T08:12:48Z
body: > I _think_ the fuzzer failures are unrelated to my change, and the rest of the tests pass.

Again. I only changed the docs, and a different set of tests has failed. Looking at them, I don't think they're related to my change at all.
author_association: CONTRIBUTOR
user: login: rschu1ze
created_at: 2025-03-27T11:55:37Z
body: Let's see what CI says (it is unfortunately quite unstable at the moment).
author_association: MEMBER
user: login: jsc0218
created_at: 2025-03-27T16:01:23Z
body: [flaky](https://github.com/ClickHouse/ClickHouse/issues/78367) 
author_association: CONTRIBUTOR
user: login: clickgapai
created_at: 2026-04-03T10:06:47Z
body: Hi @KrzaQ @rschu1ze — while reviewing this PR I found the following:

- **Bug (P1):** index_length sums inactive parts because JOIN lacks active=1 filter — https://github.com/ClickHouse/ClickHouse/issues/101695
- **Bug (P2):** INDEX_LENGTH uppercase alias missing from information_schema.tables view — https://github.com/ClickHouse/ClickHouse/issues/101696

Happy to discuss — close anything that's wrong or already addressed.
author_association: CONTRIBUTOR