Fix ClickHouse Cloud migration failures caused by multi-table RENAME statements. Use when: (1) Migration fails with "Database X is Shared, it does not support renaming of multiple tables in single query", (2) golang-migrate or other migration tools show dirty database version after a table-swap migration on ClickHouse Cloud, (3) Schema migration works on self-hosted ClickHouse but fails on ClickHouse Cloud. ClickHouse Cloud uses SharedMergeTree engine which has restrictions not present in reg...
Scanned 5/27/2026
Install via CLI
openskills install divinevideo/divine-mobile---
name: clickhouse-cloud-multi-table-rename
description: |
Fix ClickHouse Cloud migration failures caused by multi-table RENAME statements.
Use when: (1) Migration fails with "Database X is Shared, it does not support renaming
of multiple tables in single query", (2) golang-migrate or other migration tools show
dirty database version after a table-swap migration on ClickHouse Cloud,
(3) Schema migration works on self-hosted ClickHouse but fails on ClickHouse Cloud.
ClickHouse Cloud uses SharedMergeTree engine which has restrictions not present in
regular MergeTree.
author: Claude Code
version: 1.0.0
date: 2026-02-22
---
# ClickHouse Cloud Multi-Table RENAME Limitation
## Problem
ClickHouse Cloud (SharedMergeTree engine) does not support renaming multiple tables
in a single `RENAME TABLE` statement, which is a common pattern for atomic table swaps
in schema migrations. Self-hosted ClickHouse supports this, so migrations that work
locally or on self-hosted instances will fail on ClickHouse Cloud.
## Context / Trigger Conditions
- Error message: `"Database X is Shared, it does not support renaming of multiple tables in single query"`
- Error code: 48
- Using golang-migrate (or similar) with ClickHouse Cloud
- Migration SQL contains a pattern like:
```sql
RENAME TABLE db.original TO db.original_old,
db.new_version TO db.original;
```
- Migration works in staging (self-hosted ClickHouse) but fails in production (ClickHouse Cloud)
## Solution
### Prevention: Write ClickHouse Cloud-compatible migrations
Instead of multi-table RENAME:
```sql
-- BAD: This fails on ClickHouse Cloud
RENAME TABLE nostr.my_table TO nostr.my_table_old,
nostr.my_table_v2 TO nostr.my_table;
```
Use separate RENAME statements:
```sql
-- GOOD: Split into individual operations
RENAME TABLE nostr.my_table TO nostr.my_table_old;
RENAME TABLE nostr.my_table_v2 TO nostr.my_table;
```
Note: This loses atomicity, but ClickHouse Cloud doesn't support the atomic version anyway.
### Recovery: Fix a dirty migration that already failed
1. **Check the current state** — identify which tables exist and what state they're in:
```sql
SHOW TABLES LIKE '%my_table%';
DESCRIBE TABLE nostr.my_table; -- Check if it has old or new schema
DESCRIBE TABLE nostr.my_table_v2; -- Check if the new table was created
```
2. **Complete the migration manually** with separate renames:
```sql
-- If both original and v2 exist (RENAME never executed):
RENAME TABLE nostr.my_table TO nostr.my_table_old;
RENAME TABLE nostr.my_table_v2 TO nostr.my_table;
DROP TABLE IF EXISTS nostr.my_table_old;
-- Recreate any views that were dropped
```
3. **Force the migration version** to mark it as completed:
```bash
# Using golang-migrate
migrate -path=/migrations -database "clickhouse://..." force VERSION
```
4. **If using K8s jobs**, recreate the job with `force VERSION` args:
```yaml
containers:
- name: migrate
image: my-migrate-image:tag
args: ["force", "65"] # The migration number that was applied manually
```
## Verification
After manual migration, verify:
```sql
-- Check table has new schema
DESCRIBE TABLE nostr.my_table;
-- Check migration version is clean (not dirty)
SELECT version, dirty FROM schema_migrations ORDER BY version DESC LIMIT 5;
-- Check old/temp tables are cleaned up
SHOW TABLES LIKE '%my_table%';
```
## Example
Migration 65 for funnelcake needed to change `view_traffic_sources.source` from
`Enum8` to `String`. The migration:
1. Dropped a dependent view
2. Created `view_traffic_sources_v2` with new schema
3. Copied data
4. Tried `RENAME TABLE original TO old, v2 TO original` — FAILED on ClickHouse Cloud
Recovery:
```bash
# Via HTTP API from a curl pod in the cluster:
curl -s "$CH_URL/?database=nostr&user=$USER&password=$PASS" \
--data-binary 'RENAME TABLE nostr.view_traffic_sources TO nostr.view_traffic_sources_old'
curl -s "$CH_URL/?database=nostr&user=$USER&password=$PASS" \
--data-binary 'RENAME TABLE nostr.view_traffic_sources_v2 TO nostr.view_traffic_sources'
curl -s "$CH_URL/?database=nostr&user=$USER&password=$PASS" \
--data-binary 'DROP TABLE IF EXISTS nostr.view_traffic_sources_old'
# Recreate the summary view...
# Then force migration version to 65
```
## Notes
- SharedMergeTree is the default engine on ClickHouse Cloud — you cannot switch to regular MergeTree
- Other SharedMergeTree limitations exist (e.g., some ALTER operations behave differently)
- When writing migrations for dual self-hosted/cloud environments, always use separate RENAME statements
- The golang-migrate ClickHouse driver uses `x-multi-statement=true` which splits statements on `;`, but the RENAME with commas is still a single statement
- If a failed migration left a `_v2` table behind, you must `DROP TABLE IF EXISTS` it before re-running the migration, or `CREATE TABLE IF NOT EXISTS` will silently skip creation and the INSERT will duplicate data into the existing v2 table
- Use `SET alter_sync = 2; SET mutations_sync = 2;` in migrations to ensure synchronous execution on ClickHouse Cloud
## References
- ClickHouse Cloud SharedMergeTree differences: SharedMergeTree engine has restrictions on operations that require cross-shard coordination
- golang-migrate ClickHouse driver: github.com/golang-migrate/migrate with clickhouse driver
No comments yet. Be the first to comment!