jackc / pgx

PostgreSQL driver and toolkit for Go
MIT License
10.91k stars 849 forks source link

CopyFrom with null schema gives zero-length delimited identifier error #1872

Open joescharf opened 10 months ago

joescharf commented 10 months ago

Describe the bug Turns out it's a minor thing but tripped me up for a bit. If you send a null schema into pgx.Identifier like so:

copyCount, err := dstConn.CopyFrom(ctx, pgx.Identifier{"", "dstTable"}, columnNames, pgx.CopyFromRows(filteredRows[0:1]))

then Identifier.Sanitize() will return "\"\".\"dst_table\""

and you'll end up with the error: "zero-length delimited identifier at or near \"\"\"\""

Identifier.Sanitize() should probably check for parts[0] == ""

Maybe it should also ensure len(parts) <= 2

To Reproduce Runnable example:

package main

import (
    "context"
    "log"
    "os"

    "github.com/jackc/pgx/v5"
)

// Sample code to demonstrate the bug
//
// Returns:
// (base) ➜  copyfrombug gor .
// 2024/01/11 12:00:29 copyCount 1
// 2024/01/11 12:00:29 copyfrom errstatement description failed: ERROR: zero-length delimited identifier at or near """" (SQLSTATE 42601)
// exit status 1

func main() {
    conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close(context.Background())

    _, err = conn.Exec(context.Background(), "DROP TABLE IF EXISTS copyfrombug")
    if err != nil {
        log.Fatal("drop table", err)
    }
    _, err = conn.Exec(context.Background(), "CREATE TABLE copyfrombug(id serial primary key, name text)")
    if err != nil {
        log.Fatal(err)
    }

    // WORKS
    inputRows := [][]any{
        {int16(1), "abc"},
    }
    // IDENT IS JUST THE TABLE NAME
    ident := pgx.Identifier{"copyfrombug"}
    copyCount, err := conn.CopyFrom(context.Background(), ident, []string{"id", "name"}, pgx.CopyFromRows(inputRows))
    if err != nil {
        log.Fatal("copyfrom err", err)
    }
    log.Println("copyCount", copyCount)

    // DOES NOT WORK
    inputRows = [][]any{
        {int16(2), "def"},
    }
    // IDENT IS THE SCHEMA(null) AND TABLE NAME
    // statement description failed: ERROR: zero-length delimited identifier at or near """" (SQLSTATE 42601)
    ident = pgx.Identifier{"", "copyfrombug"}
    copyCount, err = conn.CopyFrom(context.Background(), ident, []string{"id", "name"}, pgx.CopyFromRows(inputRows))
    if err != nil {
        log.Fatal("copyfrom err", err)
    }
    log.Println("copyCount", copyCount)
}

Version go 1.21.6 pgx 5.4.3

Additional Thanks for the great library!

jackc commented 10 months ago

Identifier.Sanitize() should probably check for parts[0] == ""

Maybe it should also ensure len(parts) <= 2

Sanitize doesn't return an error so there is really nothing that can be done about length. As far as checking for "", again there is no error return. I suppose "" could be filtered out. Not sure if that would just be hiding an application bug though.

joescharf commented 10 months ago

Maybe just a note in the docs could suffice.