Bulk insert csv data using pgx.CopyFrom into a postgres database

Issue

I’m once again trying to push lots of csv data into a postgres database.

In the past I’ve created a struct to hold the data and unpacked each column into the struct before bumping the lot into the database table, and that is working fine, however, I’ve just found pgx.CopyFrom* and it would seem as though I should be able to make it work better.

So far I’ve got the column headings for the table into a slice of strings and the csv data into another slice of strings but I can’t work out the syntax to push this into the database.

I’ve found this post which sort of does what I want but uses a [][]interface{} rather than []strings.

The code I have so far is

// loop over the lines and find the first one with a timestamp
for {                
        line, err := csvReader.Read()                   
        if err == io.EOF { 
           break
        } else if err != nil {
           log.Error("Error reading csv data", "Loading Loop", err)
        }

       // see if we have a string starting with a timestamp
       _, err := time.Parse(timeFormat, line[0])
       if err == nil {
          // we have a data line
          _, err := db.CopyFrom(context.Background(), pgx.Identifier{"emms.scada_crwf.pwr_active"}, col_headings, pgx.CopyFromRows(line))    
      }
   }

}

But pgx.CopyFromRows expects [][]interface{} not []string.

What should the syntax be? Am I barking up the wrong tree?

Solution

I recommend reading your CSV and creating a []interface{} for each record you read, appending the []interface{} to a collection of rows ([][]interface{}), then passing rows on to pgx.

var rows [][]interface{}

// read header outside of CSV "body" loop
header, _ := reader.Read()

// inside your CSV reader "body" loop...
    row := make([]interface{}, len(record))

    // use your logic/gate-keeping from here

    row[0] = record[0] // timestamp

    // convert the floats
    for i := 1; i < len(record); i++ {
        val, _ := strconv.ParseFloat(record[i], 10)
        row[i] = val
    }

    rows = append(rows, row)

...

copyCount, err := conn.CopyFrom(
    pgx.Identifier{"floaty-things"},
    header,
    pgx.CopyFromRows(rows),
)

I can’t mock up the entire program, but here’s a full demo of converting the CSV to [][]interface{}, https://go.dev/play/p/efbiFN2FJMi.

And check in with the documentation, https://pkg.go.dev/github.com/jackc/pgx/v4.

Answered By – Zach Young

Answer Checked By – Candace Johnson (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.