Monorepo for Tangled
0

Configure Feed

Select the types of activity you want to include in your feed.

appview/db: now-redundant indexed col in label ops

Lewis: May this revision serve well! <lewis@tangled.org>

authored by

Lewis and committed by
Tangled
(Jul 7, 2026, 2:27 PM +0300) 0991aacc 8cbc3db0

+109 -43
+5
appview/db/db.go
··· 2421 2421 return err 2422 2422 }) 2423 2423 2424 + orm.RunMigration(conn, logger, "drop-label-ops-indexed", func(tx *sql.Tx) error { 2425 + _, err := tx.Exec(`alter table label_ops drop column indexed`) 2426 + return err 2427 + }) 2428 + 2424 2429 return &DB{ 2425 2430 db, 2426 2431 logger,
+7 -19
appview/db/label.go
··· 186 186 } 187 187 188 188 func AddLabelOp(e Execer, l *models.LabelOp) (int64, error) { 189 - now := time.Now() 190 189 result, err := e.Exec( 191 190 `insert into label_ops ( 192 191 did, ··· 195 194 operation, 196 195 operand_key, 197 196 operand_value, 198 - performed, 199 - indexed 197 + performed 200 198 ) 201 - values (?, ?, ?, ?, ?, ?, ?, ?) 199 + values (?, ?, ?, ?, ?, ?, ?) 202 200 on conflict(did, rkey, subject, operand_key, operand_value) do update set 203 201 operation = excluded.operation, 204 202 operand_value = excluded.operand_value, 205 - performed = excluded.performed, 206 - indexed = excluded.indexed`, 203 + performed = excluded.performed`, 207 204 l.Did, 208 205 l.Rkey, 209 206 l.Subject.String(), ··· 211 208 l.OperandKey, 212 209 l.OperandValue, 213 210 l.PerformedAt.Format(time.RFC3339), 214 - now.Format(time.RFC3339), 215 211 ) 216 212 if err != nil { 217 213 return 0, err ··· 223 219 } 224 220 225 221 l.Id = id 226 - l.IndexedAt = now 227 222 228 223 return id, nil 229 224 } ··· 253 248 operation, 254 249 operand_key, 255 250 operand_value, 256 - performed, 257 - indexed 251 + performed 258 252 from label_ops 259 253 %s 260 - order by indexed 254 + order by id 261 255 `, 262 256 whereClause, 263 257 ) ··· 270 264 271 265 for rows.Next() { 272 266 var labelOp models.LabelOp 273 - var performedAt, indexedAt string 267 + var performedAt string 274 268 275 269 if err := rows.Scan( 276 270 &labelOp.Id, ··· 281 275 &labelOp.OperandKey, 282 276 &labelOp.OperandValue, 283 277 &performedAt, 284 - &indexedAt, 285 278 ); err != nil { 286 279 return nil, err 287 280 } 288 281 289 282 labelOp.PerformedAt, err = time.Parse(time.RFC3339, performedAt) 290 283 if err != nil { 291 - labelOp.PerformedAt = time.Now() 292 - } 293 - 294 - labelOp.IndexedAt, err = time.Parse(time.RFC3339, indexedAt) 295 - if err != nil { 296 - labelOp.IndexedAt = time.Now() 284 + labelOp.PerformedAt = time.Time{} 297 285 } 298 286 299 287 labelOps = append(labelOps, labelOp)
+92
appview/db/label_migration_test.go
··· 1 + package db 2 + 3 + import ( 4 + "database/sql" 5 + "path/filepath" 6 + "testing" 7 + ) 8 + 9 + func TestDropLabelOpsIndexedColumn(t *testing.T) { 10 + path := filepath.Join(t.TempDir(), "legacy.db") 11 + conn, err := sql.Open("sqlite3", path+"?_foreign_keys=1") 12 + if err != nil { 13 + t.Fatalf("open: %v", err) 14 + } 15 + defer conn.Close() 16 + 17 + legacy := ` 18 + create table label_definitions ( 19 + id integer primary key autoincrement, 20 + did text not null, 21 + rkey text not null, 22 + at_uri text generated always as ('at://' || did || '/' || 'sh.tangled.label.definition' || '/' || rkey) stored, 23 + name text not null, 24 + unique (at_uri) 25 + ); 26 + create table label_ops ( 27 + id integer primary key autoincrement, 28 + did text not null, 29 + rkey text not null, 30 + at_uri text generated always as ('at://' || did || '/' || 'sh.tangled.label.op' || '/' || rkey) stored, 31 + subject text not null, 32 + operation text not null check (operation in ("add", "del")), 33 + operand_key text not null, 34 + operand_value text not null, 35 + performed text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 36 + indexed text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 37 + foreign key (operand_key) references label_definitions (at_uri) on delete cascade, 38 + unique (did, rkey, subject, operand_key, operand_value) 39 + ); 40 + ` 41 + if _, err := conn.Exec(legacy); err != nil { 42 + t.Fatalf("legacy schema: %v", err) 43 + } 44 + 45 + defUri := "at://did:plc:boltless/sh.tangled.label.definition/prio" 46 + if _, err := conn.Exec( 47 + `insert into label_definitions (did, rkey, name) values (?, ?, ?)`, 48 + "did:plc:boltless", "prio", "priority", 49 + ); err != nil { 50 + t.Fatalf("seed def: %v", err) 51 + } 52 + if _, err := conn.Exec( 53 + `insert into label_ops (did, rkey, subject, operation, operand_key, operand_value) values (?, ?, ?, ?, ?, ?)`, 54 + "did:plc:boltless", "op1", "at://did:plc:boltless/sh.tangled.repo.issue/issue1", "add", defUri, "high", 55 + ); err != nil { 56 + t.Fatalf("seed op: %v", err) 57 + } 58 + 59 + indexedColumns := func() int { 60 + var count int 61 + if err := conn.QueryRow( 62 + `select count(*) from pragma_table_info('label_ops') where name = 'indexed'`, 63 + ).Scan(&count); err != nil { 64 + t.Fatalf("pragma_table_info: %v", err) 65 + } 66 + return count 67 + } 68 + 69 + if indexedColumns() != 1 { 70 + t.Fatalf("legacy table must carry the indexed column before the migration") 71 + } 72 + if _, err := conn.Exec(`alter table label_ops drop column indexed`); err != nil { 73 + t.Fatalf("drop column indexed must succeed against the real table shape: %v", err) 74 + } 75 + if indexedColumns() != 0 { 76 + t.Fatalf("indexed column must be gone after the drop") 77 + } 78 + 79 + var gotAtUri, gotVal string 80 + if err := conn.QueryRow( 81 + `select at_uri, operand_value from label_ops where did = ? and rkey = ?`, 82 + "did:plc:boltless", "op1", 83 + ).Scan(&gotAtUri, &gotVal); err != nil { 84 + t.Fatalf("row must survive the drop: %v", err) 85 + } 86 + if want := "at://did:plc:boltless/sh.tangled.label.op/op1"; gotAtUri != want { 87 + t.Fatalf("generated at_uri must survive the drop: got %q want %q", gotAtUri, want) 88 + } 89 + if gotVal != "high" { 90 + t.Fatalf("operand value must survive the drop: got %q", gotVal) 91 + } 92 + }
+1 -4
appview/labels/labels.go
··· 89 89 did := user.Did 90 90 rkey := tid.TID() 91 91 performedAt := time.Now() 92 - indexedAt := time.Now() 93 92 repoAt := r.Form.Get("repo") 94 93 subjectUri := r.Form.Get("subject") 95 94 ··· 140 139 OperandKey: key, 141 140 OperandValue: val, 142 141 PerformedAt: performedAt, 143 - IndexedAt: indexedAt, 144 142 }) 145 143 } 146 144 } ··· 160 158 OperandKey: key, 161 159 OperandValue: val, 162 160 PerformedAt: performedAt, 163 - IndexedAt: indexedAt, 164 161 }) 165 162 } 166 163 } ··· 263 260 defer rollback() 264 261 265 262 for _, o := range validLabelOps { 266 - if _, err := db.AddLabelOp(l.db, &o); err != nil { 263 + if _, err := db.AddLabelOp(tx, &o); err != nil { 267 264 fail("Failed to update labels. Try again later.", err) 268 265 return 269 266 }
+4 -20
appview/models/label.go
··· 307 307 func LabelDefinitionFromRecord(did, rkey string, record tangled.LabelDefinition) (*LabelDefinition, error) { 308 308 created, err := time.Parse(time.RFC3339, record.CreatedAt) 309 309 if err != nil { 310 - created = time.Now() 310 + created = time.Time{} 311 311 } 312 312 313 313 multiple := false ··· 342 342 OperandKey string 343 343 OperandValue string 344 344 PerformedAt time.Time 345 - IndexedAt time.Time 346 345 } 347 346 348 347 func (l LabelOp) SortAt() time.Time { 349 - createdAt := l.PerformedAt 350 - indexedAt := l.IndexedAt 351 - 352 - // if we don't have an indexedat, fall back to now 353 - if indexedAt.IsZero() { 354 - indexedAt = time.Now() 355 - } 356 - 357 348 // if createdat is invalid (before epoch), treat as null -> return zero time 358 - if createdAt.Before(time.UnixMicro(0)) { 349 + if l.PerformedAt.Before(time.UnixMicro(0)) { 359 350 return time.Time{} 360 351 } 361 - 362 - // if createdat is <= indexedat, use createdat 363 - if createdAt.Before(indexedAt) || createdAt.Equal(indexedAt) { 364 - return createdAt 365 - } 366 - 367 - // otherwise, createdat is in the future relative to indexedat -> use indexedat 368 - return indexedAt 352 + return l.PerformedAt 369 353 } 370 354 371 355 var _ Validator = new(LabelOp) ··· 395 379 func LabelOpsFromRecord(did, rkey string, record tangled.LabelOp) []LabelOp { 396 380 performed, err := time.Parse(time.RFC3339, record.PerformedAt) 397 381 if err != nil { 398 - performed = time.Now() 382 + performed = time.Time{} 399 383 } 400 384 401 385 mkOp := func(operand *tangled.LabelOp_Operand) LabelOp {