func FetchColumns(conn *pgx.Conn, keys []ColumnKey) ([]Column, error) {
	if len(keys) == 0 {
		return nil, nil
	}
	uncachedKeys := make([]ColumnKey, 0, len(keys))
	columnsMu.Lock()
	for _, key := range keys {
		if _, ok := columnsCache[key]; !ok && key.TableOID > 0 {
			uncachedKeys = append(uncachedKeys, key)
		}
	}
	columnsMu.Unlock()
	if len(uncachedKeys) == 0 {
		return fetchCachedColumns(keys)
	}
	predicate := &strings.Builder{}
	predicate.Grow(len(uncachedKeys) * 40)
	for i, key := range uncachedKeys {
		predicate.WriteString("(cls.oid = ")
		predicate.WriteString(strconv.Itoa(int(key.TableOID)))
		predicate.WriteString(" AND attr.attnum = ")
		predicate.WriteString(strconv.Itoa(int(key.Number)))
		predicate.WriteString(")")
		if i < len(uncachedKeys)-1 {
			predicate.WriteString("\n    OR ")
		}
	}
	q := texts.Dedent(`
		SELECT cls.oid         AS table_oid,
					 cls.relname     AS table_name,
					 attr.attname    AS col_name,
					 attr.attnum     AS col_num,
					 attr.attnotnull AS col_null
		FROM pg_class cls
					 JOIN pg_attribute attr ON (attr.attrelid = cls.oid)
	`) + "\nWHERE " + predicate.String()
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()
	rows, err := conn.Query(ctx, q)
	if err != nil {
		return nil, fmt.Errorf("fetch column metadata: %w", err)
	}
	defer rows.Close()
	for rows.Next() {
		col := Column{}
		notNull := false
		if err := rows.Scan(&col.TableOID, &col.TableName, &col.Name, &col.Number, &notNull); err != nil {
			return nil, fmt.Errorf("scan fetch column row: %w", err)
		}
		col.Null = !notNull
		columnsCache[ColumnKey{col.TableOID, col.Number}] = col
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("close fetch column rows: %w", err)
	}
	return fetchCachedColumns(keys)
}
FetchColumns fetches meta information about a Postgres column from the
pg_class and pg_attribute catalog tables.
package pg
import (
	"context"
	"fmt"
	"github.com/jackc/pgtype"
	"github.com/jackc/pgx/v4"
	"github.com/jschaf/pggen/internal/texts"
	"strconv"
	"strings"
	"sync"
	"time"
)
type Column struct {
	Name      string      
	TableOID  pgtype.OID  
	TableName string      
	Number    uint16      
	Type      Type        
	Null      bool        
}
type ColumnKey struct {
	TableOID pgtype.OID
	Number   uint16  
}
var (
	columnsMu    = &sync.Mutex{}
	columnsCache = make(map[ColumnKey]Column, 32)
)