Skip to content

ClickHouse: Import as Update and Insert or Update fail on the first matched row #3765

Closed

Description

@datlechin

What happened?

Left open by #3743 ("Not in this PR"). Importing rows into a ClickHouse table as Update or Insert or Update stops at the first row whose key is already in the table. ClickHouse refuses the plain UPDATE ... SET the import sends, and the import shows the server's syntax error.

What the code does today:

  • The sheet offers every mode for every engine (apple/TablePro/Views/Import/RowImportSheet.swift:329). The only engine check is whether the engine has a SQL dialect (apple/TablePro/Core/Plugins/ImportDataSinkAdapter.swift:75).
  • Match on offers ClickHouse's primary key, since the plugin flags those columns as primary key (apple/Plugins/ClickHouseDriverPlugin/ClickHousePluginDriver+Schema.swift:97, :157).
  • Per run of rows, one SELECT <pos>, COUNT(*) ... UNION ALL counts the matches (apple/TablePro/Core/ChangeTracking/SQLStatementGenerator.swift:277). A count of 1 sends UPDATE <table> SET <mapped> WHERE <key> (SQLStatementGenerator.swift:293, sent at ImportDataSinkAdapter.swift:424).
  • ClickHouse changes rows with ALTER TABLE ... UPDATE, a mutation. The plugin already writes that form for grid edits (apple/Plugins/ClickHouseDriverPlugin/ClickHousePlugin.swift:467).
  • Lightweight UPDATE ... SET works only on a MergeTree-family table with the enable_block_number_column and enable_block_offset_column table settings (ClickHouse docs, UPDATE statement). Any other table, or a server without lightweight updates, refuses it.
  • Data Compare sync writes the same UPDATE ... SET (apple/TablePro/Core/Compare/DataSyncScriptBuilder.swift:151), and ClickHouse declares .dataCompare (ClickHousePlugin.swift:211).

Steps to reproduce

  1. On a ClickHouse connection, create CREATE TABLE t (id UInt32, name String) ENGINE = MergeTree ORDER BY id and insert (1, 'a').
  2. Import a CSV with the header id,name and the row 1,b into t.
  3. Set Import as to Update, Match on to the primary key, and import.

Expected behavior

One of these, not the server's error:

  • Hide the modes. Update and Insert or Update are not offered for an engine that cannot run a keyed UPDATE. The engine says so through a new capability in apple/Plugins/TableProPluginKit/PluginCapabilities.swift (the last bit used is 18). Data Compare sync reads the same capability before it writes an UPDATE script for that target. A new bit is a PluginKit ABI change (.claude/rules/plugin-system.md, ABI).
  • Write ClickHouse's own form. The plugin builds the keyed update as ALTER TABLE ... UPDATE ... WHERE <key>. A mutation applies in the background unless asked to wait, so the import must wait for it before the next count. The iOS app already adds SETTINGS mutations_sync = 1 to its ClickHouse row writes (apple/TableProMobile/TableProMobile/Platform/IOSDriverFactory.swift:190).

Done when: on ClickHouse, Update and Insert or Update either are not offered or update the matched rows, and a test covers that choice for import and for Data Compare sync.

Database type

ClickHouse

TablePro version

Not released. #3743 merged to main on 2026-10-21, after 0.83.0-beta.1.

Platform

macOS

OS version & hardware

No response

Screenshots / Logs

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingplatform-macosAbout the macOS app

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions