Skip to content

PriorityQueue cannot reopen an existing DB: 'duplicate column name: priority' #5

Description

@fahimfaisaal

Summary

PriorityQueue fails to open against an existing database because initPriorityColumn unconditionally runs ALTER TABLE ... ADD COLUMN priority, producing duplicate column name: priority on the second open.

Root cause

priority_queue.go:36-56 checks for the column with:

var name string
err := pq.client.QueryRow(fmt.Sprintf("PRAGMA table_info(%s)", quoteIdent(pq.tableName))).
    Scan(nil, &name, nil, nil, nil, nil)
if err != nil || name != "priority" {
    // ALTER TABLE ... ADD COLUMN priority ...
}

PRAGMA table_info returns one row per column. QueryRow reads only the first row (the id column), so name is always "id" and the guard is always true. Every open runs ADD COLUMN, which has no IF NOT EXISTS in SQLite, so the second open of a persisted DB errors and NewPriorityQueue returns:

failed to initialize priority column: duplicate column name: priority

Reproduction

m1 := sqliteq.New("pq.db")
m1.NewPriorityQueue("pq")   // ok
m1.Close()

m2 := sqliteq.New("pq.db")
_, err := m2.NewPriorityQueue("pq") // err: duplicate column name: priority

Confirmed via test — priority queues cannot survive a process restart, defeating the persistence guarantee.

Impact

High. Any priority queue on a persisted DB is broken after the first run.

Possible fix

Scan all pragma rows and only alter when the column is truly absent:

func (pq *PriorityQueue) initPriorityColumn() error {
    rows, err := pq.client.Query(fmt.Sprintf("PRAGMA table_info(%s)", quoteIdent(pq.tableName)))
    if err != nil {
        return err
    }
    defer rows.Close()

    hasPriority := false
    for rows.Next() {
        var cid int
        var name, ctype string
        var notnull, pk int
        var dflt sql.NullString
        if err := rows.Scan(&cid, &name, &ctype, &notnull, &dflt, &pk); err != nil {
            return err
        }
        if name == "priority" {
            hasPriority = true
        }
    }
    if err := rows.Err(); err != nil {
        return err
    }
    if hasPriority {
        return nil
    }
    // ADD COLUMN + CREATE INDEX (index already uses IF NOT EXISTS)
    ...
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions