(ctx context.Context, name string, msgs message.WriteInserts)
| 135 | } |
| 136 | |
| 137 | func (c *Client) WriteTableBatch(ctx context.Context, name string, msgs message.WriteInserts) error { |
| 138 | if len(msgs) == 0 { |
| 139 | return nil |
| 140 | } |
| 141 | |
| 142 | table := msgs[0].GetTable() |
| 143 | |
| 144 | writeStart := time.Now() |
| 145 | tmpFile, err := writeTMPFile(table, msgs) |
| 146 | if err != nil { |
| 147 | return err |
| 148 | } |
| 149 | c.logger.Debug().Str("table", table.Name).Str("duration", time.Since(writeStart).String()).Msg("write tmp file") |
| 150 | defer os.Remove(tmpFile) |
| 151 | |
| 152 | if len(table.PrimaryKeys()) == 0 { |
| 153 | copyStart := time.Now() |
| 154 | defer func() { |
| 155 | c.logger.Debug().Str("table", table.Name).Str("duration", time.Since(copyStart).String()).Msg("copy file to table") |
| 156 | }() |
| 157 | return c.copyFromFile(ctx, name, tmpFile, table) |
| 158 | } |
| 159 | |
| 160 | tmpTableName := name + strings.ReplaceAll(uuid.New().String(), "-", "_") |
| 161 | // we skip constraints here, as copy doesn't allow us to handle conflicts, we handle those during upsert |
| 162 | if err := c.createTableIfNotExist(ctx, tmpTableName, table, true); err != nil { |
| 163 | return fmt.Errorf("failed to create table %s: %w", tmpTableName, err) |
| 164 | } |
| 165 | defer func() { |
| 166 | e := c.exec(ctx, "drop table "+tmpTableName) |
| 167 | if err == nil { |
| 168 | // we preserve original error, so update only on nil err |
| 169 | err = e |
| 170 | } |
| 171 | }() |
| 172 | |
| 173 | if err := c.copyFromFile(ctx, tmpTableName, tmpFile, table); err != nil { |
| 174 | return fmt.Errorf("failed to copy from file %s: %w", tmpFile, err) |
| 175 | } |
| 176 | |
| 177 | // At time of writing (March 2023), duckdb does not support updating list columns. |
| 178 | // As a workaround, we delete the row and insert it again. This makes it non-atomic, unfortunately, |
| 179 | // but this is unavoidable until support is added to duckdb itself. |
| 180 | // See https://github.com/duckdb/duckdb/blob/c5d9afb97bbf0be12216f3b89ae3131afbbc3156/src/storage/table/list_column_data.cpp#L243-L251 |
| 181 | if containsList(table) { |
| 182 | return c.deleteInsert(ctx, tmpTableName, table) |
| 183 | } |
| 184 | |
| 185 | return c.upsert(ctx, tmpTableName, table) |
| 186 | } |
| 187 | |
| 188 | func writeTMPFile(table *schema.Table, msgs []*message.WriteInsert) (fileName string, err error) { |
| 189 | sc := transformSchemaForWriting(table) |
nothing calls this directly
no test coverage detected