Files
doormile_backend/scratch/check_handover_schema.go

137 lines
4.2 KiB
Go

//go:build ignore
// Read-only verification for the base-handover work. Reads information_schema
// and pg_constraint only — no writes, no DDL, no migrations.
//
// go run scratch/check_handover_schema.go
package main
import (
"fmt"
"log"
"doormile/config"
"doormile/db"
"github.com/joho/godotenv"
)
func main() {
_ = godotenv.Load()
cfg := config.Load()
db.Connect(cfg)
if db.DB == nil {
log.Fatal("no DB connection")
}
fmt.Println("=== 1. New columns (expected ABSENT until AutoMigrate runs) ===")
type col struct {
Table string
Name string
}
for _, want := range []col{
{"pickupbookings", "pickupsourcetype"},
{"pickupbookings", "pickuphubid"},
{"consignments", "inwardedat"},
} {
var n int64
db.DB.Raw(`SELECT count(*) FROM information_schema.columns
WHERE table_name = ? AND column_name = ?`, want.Table, want.Name).Scan(&n)
fmt.Printf(" %-16s %-18s present=%v\n", want.Table, want.Name, n > 0)
}
fmt.Println("\n=== 2. CHECK constraints on the tables we write ===")
type chk struct {
Conname string
Def string
}
var checks []chk
db.DB.Raw(`SELECT c.conname, pg_get_constraintdef(c.oid) AS def
FROM pg_constraint c JOIN pg_class t ON t.oid = c.conrelid
WHERE c.contype = 'c'
AND t.relname IN ('consignments','consignmentexceptions','consignmenthistory',
'pickupbookings','bookingassignments','milerprofiles')
ORDER BY t.relname, c.conname`).Scan(&checks)
for _, c := range checks {
fmt.Printf(" %s\n %s\n", c.Conname, c.Def)
}
fmt.Println("\n=== 3. Foreign keys on the user/id columns we write ===")
type fk struct {
Table string
Conname string
Def string
}
var fks []fk
db.DB.Raw(`SELECT t.relname AS table, c.conname, pg_get_constraintdef(c.oid) AS def
FROM pg_constraint c JOIN pg_class t ON t.oid = c.conrelid
WHERE c.contype = 'f'
AND t.relname IN ('consignmentexceptions','consignmenthistory','consignments',
'tripsheets','bookingassignments','pickupbookings')
ORDER BY t.relname, c.conname`).Scan(&fks)
if len(fks) == 0 {
fmt.Println(" (none)")
}
for _, f := range fks {
fmt.Printf(" %-24s %s\n", f.Table, f.Def)
}
fmt.Println("\n=== 4. NOT NULL columns on the tables we insert into ===")
type nn struct {
Table string
Column string
Def *string
}
var nns []nn
db.DB.Raw(`SELECT table_name AS table, column_name AS column, column_default AS def
FROM information_schema.columns
WHERE table_name IN ('consignmentexceptions','consignmenthistory')
AND is_nullable = 'NO'
ORDER BY table_name, ordinal_position`).Scan(&nns)
for _, c := range nns {
d := "(no default)"
if c.Def != nil {
d = *c.Def
}
fmt.Printf(" %-24s %-20s %s\n", c.Table, c.Column, d)
}
fmt.Println("\n=== 5. Hub master data completeness (request 29) ===")
type hubRow struct {
Total int64
NoAddress int64
NoPincode int64
NoCoords int64
ActiveTotal int64
}
var h hubRow
db.DB.Raw(`SELECT count(*) AS total,
count(*) FILTER (WHERE address IS NULL OR address = '') AS no_address,
count(*) FILTER (WHERE pincode IS NULL OR pincode = '') AS no_pincode,
count(*) FILTER (WHERE latitude IS NULL OR latitude = 0 OR longitude IS NULL OR longitude = 0) AS no_coords,
count(*) FILTER (WHERE status = 'Active') AS active_total
FROM hubs WHERE deletedat IS NULL`).Scan(&h)
fmt.Printf(" hubs=%d active=%d missing_address=%d missing_pincode=%d missing_coords=%d\n",
h.Total, h.ActiveTotal, h.NoAddress, h.NoPincode, h.NoCoords)
fmt.Println("\n=== 6. Consignment status distribution (what is live now) ===")
type sc struct {
Status string
N int64
}
var scs []sc
db.DB.Raw(`SELECT status, count(*) AS n FROM consignments
WHERE deletedat IS NULL GROUP BY status ORDER BY n DESC`).Scan(&scs)
for _, s := range scs {
fmt.Printf(" %-22s %d\n", s.Status, s.N)
}
fmt.Println("\n=== 7. Open assignments on already-converted bookings (the off-duty bug) ===")
var stuck int64
db.DB.Raw(`SELECT count(*) FROM bookingassignments a
JOIN pickupbookings b ON b.bookingid = a.bookingid
WHERE a.assignmentstatus IN ('Assigned','Accepted')
AND b.status = 'Converted_To_Consignment'`).Scan(&stuck)
fmt.Printf(" assignments still open on a converted booking: %d\n", stuck)
}