Import: choose source format (SQL/JSON) and batch JSON inserts#1548
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6198afe92c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| batch.removeAll(keepingCapacity: true) | ||
|
|
||
| do { | ||
| try await sink.insertRows(entries.map(\.row)) |
There was a problem hiding this comment.
Avoid replaying rows already inserted by a partial batch
In skipAndContinue mode this assumes sink.insertRows(...) is all-or-nothing, but ImportDataSinkAdapter.insertRows can execute several statements for one batch (different column sets or bind-limit chunks) before a later statement throws. When that happens, the catch path replays every entry row-by-row, so any rows from earlier successful statements in the same batch are inserted a second time. This shows up with JSON rows that do not all have the same mapped columns and one later group has a bad value; either make the sink batch atomic for this path or only replay the rows that were not already committed.
Useful? React with 👍 / 👎.
6198afe to
fbafddc
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fbafddc337
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let sql = | ||
| "INSERT INTO \(quoteIdentifierFn(tableName)) (\(columnList)) VALUES \(rowTuples)" |
There was a problem hiding this comment.
Preserve single-row insert syntax for Oracle
On Oracle connections, JSON row imports now batch same-shaped rows into INSERT ... VALUES (...), (...), but the Oracle dialect supported by this app does not use that multi-row VALUES form; previously insertRow emitted one INSERT ... VALUES (...) per row, which works. Because Oracle has supportsImport: true and importFormatOptions will expose JSON there, any JSON file with two consecutive rows mapping to the same columns will fail at execution instead of importing. Keep Oracle on single-row inserts or generate an Oracle-specific multi-row form.
Useful? React with 👍 / 👎.
What
Two changes to the import flow, on one branch.
1. Import source picker
Importbecomes a submenu so you choose the source up front instead of the app guessing from the file extension.Import ▸ From SQL… / From JSON…in the app menu, the sidebar context menu, and the toolbar (native pull-down + submenu menu-form representation).Import {Format}…item when only one format applies.openImportDialog(formatId:); routing keys off the chosen plugin'srequiresTargetTableinstead of inferring from the picked file. The hardcoded "SQL" panel/error text is gone.Cmd+Shift+Imoves to the SQL leaf (a submenu parent can't own a shortcut, per HIG).2. JSON import: accurate progress + batched inserts
fileSize / 256estimate, so it no longer overshoots (e.g. "3000 of 2657" while the real count was 3503). Streaming.jsonl/.ndjsonkeeps an estimate, since a true count there needs a second file pass.INSERT … VALUES (…),(…),…(500/batch, clamped by each driver's bind-parameter limit: Postgres 65535, SQLite 32766, MSSQL 2100). Importing 3503 rows to a remote Postgres drops from ~113s to a few seconds (~7 round-trips instead of 3503).PluginImportDataSinkis implemented only in-app and called only by the bundled JSON plugin.Tests
insertStatement(Postgres$N+ MySQL?, arity guards) andmaxBindParametersper engine.requiresTargetTable→ row-mapping vs statement) and format-option labels.swiftlint --strictclean on all changed files. Not built locally (Xcode build is done separately); CI will compile.Note
Unrelated in-progress toolbar work in the tree (layout
.v2reorder,Localizable.xcstrings) was intentionally left out of this branch.