github.com/simpleiot/simpleiot@v0.18.3/store/sqlite.go (about) 1 package store 2 3 import ( 4 "crypto/rand" 5 "database/sql" 6 "errors" 7 "fmt" 8 "log" 9 "math" 10 "strings" 11 "sync" 12 "time" 13 14 "github.com/google/uuid" 15 "github.com/simpleiot/simpleiot/data" 16 17 // tell sql to use sqlite 18 _ "modernc.org/sqlite" 19 ) 20 21 // DbSqlite represents a SQLite data store 22 type DbSqlite struct { 23 db *sql.DB 24 meta Meta 25 writeLock sync.Mutex 26 } 27 28 // Meta contains metadata about the database 29 type Meta struct { 30 ID int `json:"id"` 31 Version int `json:"version"` 32 RootID string `json:"rootID"` 33 JWTKey []byte `json:"jwtKey"` 34 } 35 36 // NewSqliteDb creates a new Sqlite data store 37 func NewSqliteDb(dbFile string, rootID string) (*DbSqlite, error) { 38 ret := &DbSqlite{} 39 40 pragmas := "_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)&_pragma=busy_timeout(8000)&_pragma=journal_size_limit(100000000)" 41 42 dbFileOptions := fmt.Sprintf("%s?%s", dbFile, pragmas) 43 44 log.Println("Open store:", dbFileOptions) 45 46 db, err := sql.Open("sqlite", dbFileOptions) 47 if err != nil { 48 return nil, err 49 } 50 51 // Note, code should run with the following set, which ensures we don't have any 52 // nested db operations. Ideally, all DB operations should exit before the next one 53 // starts. Cache rows in memory if necessary to make this happen. 54 // db.SetMaxOpenConns(1) 55 56 ret.db = db 57 58 _, err = db.Exec(`CREATE TABLE IF NOT EXISTS meta (id INT NOT NULL PRIMARY KEY, 59 version INT, 60 root_id TEXT, 61 jwt_key BLOB)`) 62 if err != nil { 63 return nil, fmt.Errorf("Error creating meta table: %v", err) 64 } 65 66 // check if jwt_key column exists 67 row := db.QueryRow(`SELECT COUNT(*) AS CNTREC FROM pragma_table_info('meta') WHERE name='jwt_key'`) 68 var count int 69 err = row.Scan(&count) 70 if err != nil { 71 return nil, err 72 } 73 74 if count <= 0 { 75 _, err := db.Exec(`ALTER TABLE meta ADD COLUMN jwt_key BLOB`) 76 if err != nil { 77 return nil, err 78 } 79 } 80 81 _, err = db.Exec(`CREATE TABLE IF NOT EXISTS edges (id TEXT NOT NULL PRIMARY KEY, 82 up TEXT, 83 down TEXT, 84 hash INT, 85 type TEXT)`) 86 87 if err != nil { 88 return nil, fmt.Errorf("Error creating edges table: %v", err) 89 } 90 91 _, err = db.Exec(`CREATE TABLE IF NOT EXISTS node_points (id TEXT NOT NULL PRIMARY KEY, 92 node_id TEXT, 93 type TEXT, 94 key TEXT, 95 time INT, 96 idx REAL, 97 value REAL, 98 text TEXT, 99 data BLOB, 100 tombstone INT, 101 origin TEXT)`) 102 103 if err != nil { 104 return nil, fmt.Errorf("Error creating node_points table: %v", err) 105 } 106 107 _, err = db.Exec(`CREATE TABLE IF NOT EXISTS edge_points (id TEXT NOT NULL PRIMARY KEY, 108 edge_id TEXT, 109 type TEXT, 110 key TEXT, 111 time INT, 112 idx REAL, 113 value REAL, 114 text TEXT, 115 data BLOB, 116 tombstone INT, 117 origin TEXT)`) 118 119 if err != nil { 120 return nil, fmt.Errorf("Error creating edge_points table: %v", err) 121 } 122 123 _, err = db.Exec(`CREATE INDEX IF NOT EXISTS edgeUp ON edges(up)`) 124 if err != nil { 125 return nil, err 126 } 127 128 _, err = db.Exec(`CREATE INDEX IF NOT EXISTS edgeDown ON edges(down)`) 129 if err != nil { 130 return nil, err 131 } 132 133 _, err = db.Exec(`CREATE INDEX IF NOT EXISTS edgeType ON edges(type)`) 134 if err != nil { 135 return nil, err 136 } 137 138 err = ret.initMeta() 139 if err != nil { 140 return nil, fmt.Errorf("Error initializing db meta: %v", err) 141 } 142 143 err = ret.runMigrations() 144 if err != nil { 145 return nil, fmt.Errorf("Error running migrations: %v", err) 146 } 147 148 if ret.meta.RootID == "" { 149 // we need to initialize root node and user 150 ret.meta.RootID, err = ret.initRoot(rootID) 151 if err != nil { 152 return nil, fmt.Errorf("Error initializing root node: %v", err) 153 } 154 } 155 156 if len(ret.meta.JWTKey) <= 0 { 157 err := ret.initJwtKey() 158 if err != nil { 159 return nil, fmt.Errorf("Error initializing JWT Key: %v", err) 160 } 161 } 162 163 // make sure we find root ID 164 nodes, err := ret.getNodes(nil, "all", ret.meta.RootID, "", false) 165 if err != nil { 166 return nil, fmt.Errorf("error fetching root node: %v", err) 167 } 168 169 if len(nodes) < 1 { 170 return nil, fmt.Errorf("root node not found") 171 } 172 173 return ret, nil 174 } 175 176 func (sdb *DbSqlite) initMeta() error { 177 // should be one row in the meta database 178 rows, err := sdb.db.Query("SELECT id, version, root_id, jwt_key FROM meta") 179 if err != nil { 180 return err 181 } 182 defer rows.Close() 183 184 var count int 185 186 for rows.Next() { 187 count++ 188 err = rows.Scan(&sdb.meta.ID, &sdb.meta.Version, &sdb.meta.RootID, &sdb.meta.JWTKey) 189 if err != nil { 190 return fmt.Errorf("Error scanning meta row: %v", err) 191 } 192 } 193 194 if count < 1 { 195 _, err := sdb.db.Exec("INSERT INTO meta(id, version, root_id) VALUES(?, ?, ?)", 0, 0, "") 196 if err != nil { 197 return err 198 } 199 } 200 201 return nil 202 } 203 204 func (sdb *DbSqlite) runMigrations() error { 205 if sdb.meta.Version < 4 { 206 _, err := sdb.db.Exec(`UPDATE node_points SET key = '0' WHERE key = ''`) 207 if err != nil { 208 return err 209 } 210 211 _, err = sdb.db.Exec(`UPDATE edge_points SET key = '0' WHERE key = ''`) 212 if err != nil { 213 return err 214 } 215 216 _, err = sdb.db.Exec(`UPDATE meta SET version = 4`) 217 if err != nil { 218 return err 219 } 220 sdb.meta.Version = 4 221 } 222 223 return nil 224 } 225 226 // reset the database by permanently wiping all data 227 func (sdb *DbSqlite) reset() error { 228 var err error 229 230 // truncate several tables 231 tables := []string{"meta", "edges", "node_points", "edge_points"} 232 for _, v := range tables { 233 _, err = sdb.db.Exec(`DELETE FROM ` + v) 234 if err != nil { 235 return fmt.Errorf("Error truncating table: %v", err) 236 } 237 } 238 239 // we need to initialize root node and user 240 // preserve root ID 241 sdb.meta.RootID, err = sdb.initRoot(sdb.meta.RootID) 242 if err != nil { 243 return fmt.Errorf("error initializing root node: %v", err) 244 } 245 246 // make sure we find root ID 247 nodes, err := sdb.getNodes(nil, "all", sdb.meta.RootID, "", false) 248 if err != nil { 249 return fmt.Errorf("error fetching root node: %v", err) 250 } 251 252 if len(nodes) < 1 { 253 return fmt.Errorf("root node not found") 254 } 255 256 return nil 257 } 258 259 // verifyNodeHashes recursively verifies all the hash values for all nodes 260 // this walks to the bottom of the tree, and then works its way back up 261 func (sdb *DbSqlite) verifyNodeHashes(fix bool) error { 262 // must run this in a transaction so we don't get any modifications 263 // while reading child nodes. This may be expensive for a large DB, so 264 // we may want to eventually break this down into transactions for each node 265 // and its children. 266 tx, err := sdb.db.Begin() 267 if err != nil { 268 return err 269 } 270 271 rollback := func() { 272 rbErr := tx.Rollback() 273 if rbErr != nil { 274 log.Println("Rollback error:", rbErr) 275 } 276 } 277 278 // get root node to kick things off 279 rootNodes, err := sdb.getNodes(nil, "root", "all", "", true) 280 281 if err != nil { 282 rollback() 283 return err 284 } 285 286 if len(rootNodes) < 1 { 287 rollback() 288 return errors.New("no root nodes") 289 } 290 291 root := rootNodes[0] 292 293 var verify func(node data.NodeEdge) error 294 295 verify = func(node data.NodeEdge) error { 296 children, err := sdb.getNodes(nil, node.ID, "all", "", true) 297 if err != nil { 298 return err 299 } 300 301 // it's important to go through children first as this can 302 // impact the current hash 303 for _, c := range children { 304 err := verify(c) 305 if err != nil { 306 return err 307 } 308 } 309 310 hash := node.CalcHash(children) 311 312 if hash != node.Hash { 313 log.Printf("Hash failed for %v, stored: %v, calc: %v", 314 node.ID, node.Hash, hash) 315 if fix { 316 log.Println("fixing ...") 317 _, err := tx.Exec(`UPDATE edges SET hash = ? WHERE up = ? AND down = ?`, 318 hash, node.Parent, node.ID) 319 if err != nil { 320 return err 321 } 322 } 323 } 324 325 return nil 326 } 327 328 err = verify(root) 329 330 if err != nil { 331 rollback() 332 return fmt.Errorf("Verify failed: %v", err) 333 } 334 335 err = tx.Commit() 336 if err != nil { 337 return err 338 } 339 340 return nil 341 } 342 343 func (sdb *DbSqlite) initRoot(rootID string) (string, error) { 344 log.Println("STORE: Initialize root node and admin user") 345 rootNode := data.NodeEdge{ 346 ID: rootID, 347 Type: data.NodeTypeDevice, 348 } 349 350 rootNode.ID = rootID 351 352 if rootNode.ID == "" { 353 rootNode.ID = uuid.New().String() 354 } 355 356 err := sdb.nodePoints(rootNode.ID, rootNode.Points) 357 if err != nil { 358 return "", fmt.Errorf("Error setting root node points: %v", err) 359 } 360 361 err = sdb.edgePoints(rootNode.ID, "root", data.Points{ 362 {Type: data.PointTypeTombstone, Value: 0}, 363 {Type: data.PointTypeNodeType, Text: rootNode.Type}, 364 }) 365 if err != nil { 366 return "", fmt.Errorf("Error sending root node edges: %w", err) 367 } 368 369 // create admin user off root node 370 admin := data.User{ 371 ID: uuid.New().String(), 372 FirstName: "admin", 373 LastName: "user", 374 Email: "admin", 375 Pass: "admin", 376 } 377 378 points := admin.ToPoints() 379 380 err = sdb.nodePoints(admin.ID, points) 381 if err != nil { 382 return "", fmt.Errorf("Error setting default user: %v", err) 383 } 384 385 err = sdb.edgePoints(admin.ID, rootNode.ID, data.Points{ 386 {Type: data.PointTypeTombstone, Value: 0}, 387 {Type: data.PointTypeNodeType, Text: data.NodeTypeUser}, 388 }) 389 390 if err != nil { 391 return "", err 392 } 393 394 sdb.writeLock.Lock() 395 defer sdb.writeLock.Unlock() 396 _, err = sdb.db.Exec("UPDATE meta SET root_id = ?", rootNode.ID) 397 if err != nil { 398 return "", fmt.Errorf("Error setting meta rootID: %v", err) 399 } 400 401 return rootNode.ID, nil 402 } 403 404 func (sdb *DbSqlite) initJwtKey() error { 405 sdb.meta.JWTKey = make([]byte, 20) 406 _, err := rand.Read(sdb.meta.JWTKey) 407 if err != nil { 408 return fmt.Errorf("Error reading making JWT key: %v", err) 409 } 410 411 sdb.writeLock.Lock() 412 defer sdb.writeLock.Unlock() 413 _, err = sdb.db.Exec("UPDATE meta SET jwt_key = ?", sdb.meta.JWTKey) 414 if err != nil { 415 return fmt.Errorf("Error setting meta jwt key: %v", err) 416 } 417 418 return nil 419 } 420 421 func (sdb *DbSqlite) nodePoints(id string, points data.Points) error { 422 points.Collapse() 423 424 sdb.writeLock.Lock() 425 defer sdb.writeLock.Unlock() 426 tx, err := sdb.db.Begin() 427 if err != nil { 428 return err 429 } 430 431 rollback := func() { 432 rbErr := tx.Rollback() 433 if rbErr != nil { 434 log.Println("Rollback error:", rbErr) 435 } 436 } 437 438 rowsPoints, err := tx.Query("SELECT * FROM node_points WHERE node_id=?", id) 439 if err != nil { 440 rollback() 441 return err 442 } 443 defer rowsPoints.Close() 444 445 var dbPoints data.Points 446 var dbPointIDs []string 447 448 for rowsPoints.Next() { 449 var p data.Point 450 var timeNS int64 451 var pID string 452 var nodeID string 453 var index float32 454 err := rowsPoints.Scan(&pID, &nodeID, &p.Type, &p.Key, &timeNS, &index, &p.Value, &p.Text, 455 &p.Data, &p.Tombstone, &p.Origin) 456 if err != nil { 457 rollback() 458 return err 459 } 460 p.Time = time.Unix(0, timeNS) 461 dbPoints = append(dbPoints, p) 462 dbPointIDs = append(dbPointIDs, pID) 463 } 464 465 if err := rowsPoints.Close(); err != nil { 466 rollback() 467 return fmt.Errorf("Error closing rowsPoints: %v", err) 468 } 469 470 var writePoints data.Points 471 var writePointIDs []string 472 473 var hashUpdate uint32 474 475 NextPin: 476 for _, pIn := range points { 477 if pIn.Time.IsZero() { 478 pIn.Time = time.Now() 479 } 480 481 if pIn.Key == "" { 482 pIn.Key = "0" 483 } 484 485 for j, pDb := range dbPoints { 486 if pIn.Type == pDb.Type && pIn.Key == pDb.Key { 487 // found a match 488 if pDb.Time.Before(pIn.Time) || pDb.Time.Equal(pIn.Time) { 489 writePoints = append(writePoints, pIn) 490 writePointIDs = append(writePointIDs, dbPointIDs[j]) 491 // back out old CRC and add in new one 492 hashUpdate ^= pDb.CRC() 493 hashUpdate ^= pIn.CRC() 494 } else { 495 log.Println("Ignoring node point due to timestamps:", id, pIn) 496 } 497 continue NextPin 498 } 499 } 500 501 // point was not found so write it 502 writePoints = append(writePoints, pIn) 503 hashUpdate ^= pIn.CRC() 504 writePointIDs = append(writePointIDs, uuid.New().String()) 505 } 506 507 stmt, err := tx.Prepare(`INSERT INTO node_points(id, node_id, type, key, time, 508 idx, value, text, data, tombstone, origin) 509 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 510 ON CONFLICT(id) DO UPDATE SET 511 type = ?3, 512 key = ?4, 513 time = ?5, 514 idx = ?6, 515 value = ?7, 516 text = ?8, 517 data = ?9, 518 tombstone = ?10, 519 origin = ?11 520 `) 521 522 if err != nil { 523 rollback() 524 return err 525 } 526 527 defer func() { 528 err := stmt.Close() 529 if err != nil { 530 log.Println("Error closing sqlite statement:", err) 531 } 532 }() 533 534 for i, p := range writePoints { 535 tNs := p.Time.UnixNano() 536 pID := writePointIDs[i] 537 _, err = stmt.Exec(pID, id, p.Type, p.Key, tNs, 0, p.Value, p.Text, p.Data, p.Tombstone, 538 p.Origin) 539 if err != nil { 540 rollback() 541 return err 542 } 543 } 544 545 stmt.Close() 546 547 err = sdb.updateHash(tx, id, hashUpdate) 548 if err != nil { 549 rollback() 550 return fmt.Errorf("Error updating upstream hash: %v", err) 551 } 552 553 err = tx.Commit() 554 if err != nil { 555 return err 556 } 557 558 return nil 559 } 560 561 func (sdb *DbSqlite) edgePoints(nodeID, parentID string, points data.Points) error { 562 points.Collapse() 563 564 if nodeID == parentID { 565 return fmt.Errorf("Error: edgePoints nodeID=parentID=%v", nodeID) 566 } 567 568 if nodeID == sdb.meta.RootID { 569 for _, p := range points { 570 if p.Type == data.PointTypeTombstone && p.Value > 0 { 571 return fmt.Errorf("Error, can't delete root node") 572 } 573 } 574 } 575 576 sdb.writeLock.Lock() 577 defer sdb.writeLock.Unlock() 578 579 var err error 580 if parentID == "" { 581 parentID = "root" 582 } 583 584 tx, err := sdb.db.Begin() 585 if err != nil { 586 return err 587 } 588 589 rollback := func() { 590 rbErr := tx.Rollback() 591 if rbErr != nil { 592 log.Println("Rollback error:", rbErr) 593 } 594 } 595 596 edges, err := sdb.edges(tx, "SELECT * FROM edges WHERE up=? AND down=?", parentID, nodeID) 597 if err != nil { 598 rollback() 599 return err 600 } 601 602 var edge data.Edge 603 604 newEdge := false 605 606 if len(edges) <= 0 { 607 newEdge = true 608 edge.ID = uuid.New().String() 609 } else { 610 edge = edges[0] 611 } 612 613 rowsPoints, err := tx.Query("SELECT * FROM edge_points WHERE edge_id=?", edge.ID) 614 if err != nil { 615 rollback() 616 return err 617 } 618 defer rowsPoints.Close() 619 620 var dbPoints data.Points 621 var dbPointIDs []string 622 623 for rowsPoints.Next() { 624 var p data.Point 625 var timeNS int64 626 var pID string 627 var nodeID string 628 var index float32 629 err := rowsPoints.Scan(&pID, &nodeID, &p.Type, &p.Key, &timeNS, &index, &p.Value, &p.Text, 630 &p.Data, &p.Tombstone, &p.Origin) 631 if err != nil { 632 rollback() 633 return err 634 } 635 p.Time = time.Unix(0, timeNS) 636 dbPoints = append(dbPoints, p) 637 dbPointIDs = append(dbPointIDs, pID) 638 } 639 640 if err := rowsPoints.Close(); err != nil { 641 rollback() 642 return fmt.Errorf("Error closing rowsPoints: %v", err) 643 } 644 645 var writePoints data.Points 646 var writePointIDs []string 647 648 var hashUpdate uint32 649 650 var nodeType string 651 652 NextPin: 653 for _, pIn := range points { 654 // we don't store node type points 655 if pIn.Type == data.PointTypeNodeType { 656 nodeType = pIn.Text 657 continue NextPin 658 } 659 660 if pIn.Time.IsZero() { 661 pIn.Time = time.Now() 662 } 663 664 if pIn.Key == "" { 665 pIn.Key = "0" 666 } 667 668 for j, pDb := range dbPoints { 669 if pIn.Type == pDb.Type && pIn.Key == pDb.Key { 670 // found a match 671 if pDb.Time.Before(pIn.Time) || pDb.Time.Equal(pIn.Time) { 672 writePoints = append(writePoints, pIn) 673 writePointIDs = append(writePointIDs, dbPointIDs[j]) 674 // back out old CRC and add in new one 675 hashUpdate ^= pDb.CRC() 676 hashUpdate ^= pIn.CRC() 677 } else { 678 log.Println("Ignoring edge point due to timestamps:", edge.ID, pIn) 679 } 680 continue NextPin 681 } 682 } 683 684 // point was not found so write it 685 writePoints = append(writePoints, pIn) 686 hashUpdate ^= pIn.CRC() 687 writePointIDs = append(writePointIDs, uuid.New().String()) 688 } 689 690 // loop through write points and write them 691 stmt, err := tx.Prepare(`INSERT INTO edge_points(id, edge_id, type, key, time, 692 idx, value, text, data, tombstone, origin) 693 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 694 ON CONFLICT(id) DO UPDATE SET 695 type = ?3, 696 key = ?4, 697 time = ?5, 698 idx = ?6, 699 value = ?7, 700 text = ?8, 701 data = ?9, 702 tombstone = ?10, 703 origin = ?11 704 `) 705 706 if err != nil { 707 rollback() 708 return err 709 } 710 711 for i, p := range writePoints { 712 tNs := p.Time.UnixNano() 713 pID := writePointIDs[i] 714 _, err = stmt.Exec(pID, edge.ID, p.Type, p.Key, tNs, 0, p.Value, p.Text, p.Data, p.Tombstone, 715 p.Origin) 716 if err != nil { 717 stmt.Close() 718 rollback() 719 return err 720 } 721 } 722 723 stmt.Close() 724 725 // we don't update the hash here as it gets updated later in updateHash() 726 // SQLite is amazing as it appears the below INSERT can be read later in the read before 727 // the transaction is finished. 728 729 // write edge 730 if newEdge { 731 if nodeType == "" { 732 rollback() 733 return fmt.Errorf("Node type must be sent with new edges") 734 } 735 // did not find edge, need to add it 736 edge.Up = parentID 737 edge.Down = nodeID 738 edge.Type = nodeType 739 740 // look for existing node points that must be added to the hash 741 rowsPoints, err := tx.Query("SELECT * FROM node_points WHERE node_id=?", nodeID) 742 if err != nil { 743 rollback() 744 return err 745 } 746 defer rowsPoints.Close() 747 748 for rowsPoints.Next() { 749 var p data.Point 750 var timeNS int64 751 var pID string 752 var nodeID string 753 var index float32 754 err := rowsPoints.Scan(&pID, &nodeID, &p.Type, &p.Key, &timeNS, &index, &p.Value, &p.Text, 755 &p.Data, &p.Tombstone, &p.Origin) 756 if err != nil { 757 rollback() 758 return err 759 } 760 p.Time = time.Unix(0, timeNS) 761 hashUpdate ^= p.CRC() 762 } 763 764 if err := rowsPoints.Close(); err != nil { 765 rollback() 766 return fmt.Errorf("Error closing rowsPoints: %v", err) 767 } 768 769 _, err = tx.Exec(`INSERT INTO edges(id, up, down, hash, type) VALUES (?, ?, ?, ?, ?)`, 770 edge.ID, edge.Up, edge.Down, 0, edge.Type) 771 772 // (hash will be populated later) 773 774 if err != nil { 775 log.Println("edge insert failed, trying again ...:", err) 776 // FIXME, occasionally the above INSERT will fail with "database is locked (5) (SQLITE_BUSY)" 777 // FIXME, not sure if retry is required any more since we removed the nested 778 // queries 779 // not sure why, but the below retry seems to work around this issue for now 780 _, err := tx.Exec(`INSERT INTO edges(id, up, down, hash, type) VALUES (?, ?, ?, ?)`, 781 edge.ID, edge.Up, edge.Down, edge.Hash, edge.Type) 782 783 // TODO check for downstream node and add in its hash 784 if err != nil { 785 rollback() 786 return fmt.Errorf("Error when writing edge: %v", err) 787 } 788 } 789 790 if parentID == "root" { 791 log.Println("inserting new root node, update root in meta") 792 _, err = tx.Exec("UPDATE meta SET root_id = ?", nodeID) 793 if err != nil { 794 rollback() 795 return fmt.Errorf("Error update root id in meta: %w", err) 796 } 797 sdb.meta.RootID = nodeID 798 } 799 } 800 801 err = sdb.updateHash(tx, nodeID, hashUpdate) 802 if err != nil { 803 rollback() 804 return fmt.Errorf("Error updating upstream hash: %v", err) 805 } 806 807 err = tx.Commit() 808 if err != nil { 809 return err 810 } 811 812 return nil 813 } 814 815 func (sdb *DbSqlite) updateHash(tx *sql.Tx, id string, hashUpdate uint32) error { 816 // key in edgeCache is up-down 817 cache := make(map[string]uint32) 818 err := sdb.updateHashHelper(tx, id, hashUpdate, cache) 819 if err != nil { 820 return err 821 } 822 823 // write update hash values back to edges 824 stmt, err := tx.Prepare(`UPDATE edges SET hash = ? WHERE id = ?`) 825 826 if err != nil { 827 return err 828 } 829 830 for id, hash := range cache { 831 _, err = stmt.Exec(hash, id) 832 if err != nil { 833 stmt.Close() 834 return fmt.Errorf("Error updating edge hash: %v", err) 835 } 836 } 837 838 stmt.Close() 839 840 return nil 841 } 842 843 func (sdb *DbSqlite) updateHashHelper(tx *sql.Tx, id string, hashUpdate uint32, cache map[string]uint32) error { 844 edges, err := sdb.edges(tx, "SELECT * FROM edges WHERE down=?", id) 845 if err != nil { 846 return err 847 } 848 849 for _, e := range edges { 850 if _, ok := cache[e.ID]; !ok { 851 cache[e.ID] = e.Hash 852 } 853 854 cache[e.ID] ^= hashUpdate 855 856 if e.Up != "none" { 857 err := sdb.updateHashHelper(tx, e.Up, hashUpdate, cache) 858 if err != nil { 859 return err 860 } 861 } 862 } 863 return nil 864 } 865 866 func (sdb *DbSqlite) edges(tx *sql.Tx, query string, args ...any) ([]data.Edge, error) { 867 var rowsEdges *sql.Rows 868 var err error 869 870 if tx != nil { 871 rowsEdges, err = tx.Query(query, args...) 872 } else { 873 rowsEdges, err = sdb.db.Query(query, args...) 874 } 875 876 if err != nil { 877 return nil, fmt.Errorf("Error getting edges: %v", err) 878 } 879 defer rowsEdges.Close() 880 881 var edges []data.Edge 882 883 for rowsEdges.Next() { 884 var edge data.Edge 885 err = rowsEdges.Scan(&edge.ID, &edge.Up, &edge.Down, &edge.Hash, &edge.Type) 886 if err != nil { 887 return nil, fmt.Errorf("Error scanning edges: %v", err) 888 } 889 890 edges = append(edges, edge) 891 } 892 893 if err := rowsEdges.Close(); err != nil { 894 return nil, err 895 } 896 897 if len(edges) < 1 { 898 return edges, nil 899 } 900 901 // Load edge points 902 edgeIDs := make([]any, len(edges)) 903 for i, edge := range edges { 904 edgeIDs[i] = edge.ID 905 } 906 edgePoints, err := sdb.queryPoints( 907 tx, 908 "SELECT * FROM edge_points WHERE edge_id IN(?"+ 909 strings.Repeat(",?", len(edgeIDs)-1)+")", 910 edgeIDs..., 911 ) 912 if err != nil { 913 return nil, fmt.Errorf("error getting edge points: %w", err) 914 } 915 916 for i := range edges { 917 edges[i].Points = edgePoints[edges[i].ID] 918 } 919 920 return edges, nil 921 } 922 923 // Close the db 924 func (sdb *DbSqlite) Close() error { 925 return sdb.db.Close() 926 } 927 928 func (sdb *DbSqlite) rootNodeID() string { 929 return sdb.meta.RootID 930 } 931 932 // If parent is set to "all", then all instances of the node are returned. 933 // If parent is set and id is "all", then all child nodes are returned. 934 // Parent can be set to "root" and id to "all" to fetch the root node(s). 935 func (sdb *DbSqlite) getNodes(tx *sql.Tx, parent, id, typ string, includeDel bool) ([]data.NodeEdge, error) { 936 var ret []data.NodeEdge 937 938 if parent == "" || parent == "none" { 939 return nil, errors.New("Parent must be set to valid ID, or all") 940 } 941 942 if id == "" { 943 id = "all" 944 } 945 946 var q string 947 948 switch { 949 case parent == "root": 950 // return a single root node 951 q = fmt.Sprintf("SELECT * FROM edges WHERE down = '%v'", sdb.meta.RootID) 952 case parent == "all" && id == "all": 953 return nil, errors.New("invalid combination of parent and id") 954 case parent == "all": 955 q = fmt.Sprintf("SELECT * FROM edges WHERE down = '%v'", id) 956 case id == "all": 957 q = fmt.Sprintf("SELECT * FROM edges WHERE up = '%v'", parent) 958 default: 959 // both parent and id are specified 960 q = fmt.Sprintf("SELECT * FROM edges WHERE up='%v' AND down = '%v'", parent, id) 961 } 962 963 if typ != "" { 964 q += fmt.Sprintf("AND type = '%v'", typ) 965 } 966 967 edges, err := sdb.edges(tx, q) 968 969 if err != nil { 970 return ret, err 971 } 972 973 if len(edges) < 1 { 974 return ret, nil 975 } 976 977 // Populate `ret` with NodeEdges with edge points 978 for _, edge := range edges { 979 var ne data.NodeEdge 980 ne.ID = edge.Down 981 ne.Parent = edge.Up 982 ne.Hash = edge.Hash 983 ne.Type = edge.Type 984 ne.EdgePoints = edge.Points 985 986 if !includeDel { 987 tombstone, _ := ne.IsTombstone() 988 if tombstone { 989 // skip deleted nodes 990 continue 991 } 992 } 993 994 ret = append(ret, ne) 995 } 996 997 if len(ret) < 1 { 998 return ret, nil 999 } 1000 1001 // Load node points for each NodeEdge 1002 nodeIDs := make([]any, len(ret)) 1003 for i, ne := range ret { 1004 nodeIDs[i] = ne.ID 1005 } 1006 nodePoints, err := sdb.queryPoints( 1007 tx, 1008 "SELECT * FROM node_points WHERE node_id IN(?"+ 1009 strings.Repeat(",?", len(nodeIDs)-1)+")", 1010 nodeIDs..., 1011 ) 1012 if err != nil { 1013 return nil, fmt.Errorf("children error getting node points: %v", err) 1014 } 1015 1016 for i, ne := range ret { 1017 ret[i].Points = nodePoints[ne.ID] 1018 } 1019 1020 return ret, nil 1021 } 1022 1023 // returns points, and error 1024 func (sdb *DbSqlite) queryPoints(tx *sql.Tx, query string, args ...any) (map[string]data.Points, error) { 1025 retPoints := make(map[string]data.Points) 1026 1027 var rowsPoints *sql.Rows 1028 var err error 1029 1030 if tx != nil { 1031 rowsPoints, err = tx.Query(query, args...) 1032 } else { 1033 rowsPoints, err = sdb.db.Query(query, args...) 1034 } 1035 1036 if err != nil { 1037 return nil, err 1038 } 1039 defer rowsPoints.Close() 1040 1041 for rowsPoints.Next() { 1042 var p data.Point 1043 var timeNS int64 1044 var pID string 1045 var nodeOrEdgeID string 1046 var index float32 1047 err := rowsPoints.Scan( 1048 &pID, &nodeOrEdgeID, &p.Type, &p.Key, &timeNS, &index, &p.Value, 1049 &p.Text, &p.Data, &p.Tombstone, &p.Origin, 1050 ) 1051 if err != nil { 1052 return nil, err 1053 } 1054 p.Time = time.Unix(0, timeNS) 1055 retPoints[nodeOrEdgeID] = append(retPoints[nodeOrEdgeID], p) 1056 } 1057 1058 return retPoints, nil 1059 } 1060 1061 // userCheck checks user authentication 1062 // returns nil, nil if user is not found 1063 func (sdb *DbSqlite) userCheck(email, password string) (data.Nodes, error) { 1064 var users []data.NodeEdge 1065 1066 rows, err := sdb.db.Query("SELECT down FROM edges WHERE type=?", data.NodeTypeUser) 1067 if err != nil { 1068 return nil, fmt.Errorf("userCheck, error query error: %v", err) 1069 } 1070 defer rows.Close() 1071 1072 var ids []string 1073 1074 for rows.Next() { 1075 var id string 1076 err = rows.Scan(&id) 1077 if err != nil { 1078 log.Println("Error scanning user id:", id) 1079 continue 1080 } 1081 1082 ids = append(ids, id) 1083 } 1084 1085 if err := rows.Close(); err != nil { 1086 return nil, err 1087 } 1088 1089 for _, id := range ids { 1090 ne, err := sdb.getNodes(nil, "all", id, "", false) 1091 if err != nil { 1092 log.Println("Error getting user node for id:", id) 1093 continue 1094 } 1095 if len(ne) < 1 { 1096 continue 1097 } 1098 1099 n := ne[0].ToNode() 1100 u := n.ToUser() 1101 if u.Email == email && u.Pass == password { 1102 users = append(users, ne...) 1103 } 1104 } 1105 1106 // make sure all these user nodes are still alive and have path to root 1107 var ret []data.NodeEdge 1108 1109 var checkUserPathRoot func(string) (bool, error) 1110 1111 checkUserPathRoot = func(id string) (bool, error) { 1112 edges, err := sdb.edges(nil, "SELECT * FROM edges WHERE down=?", id) 1113 if err != nil { 1114 return false, err 1115 } 1116 1117 NextEdge: 1118 for _, e := range edges { 1119 // make sure edge is not tombstone 1120 for _, p := range e.Points { 1121 if p.Type == data.PointTypeTombstone && p.Value != 0 { 1122 // this edge does not have a path to root, try next edge 1123 continue NextEdge 1124 } 1125 } 1126 1127 if e.Up == "root" { 1128 return true, nil 1129 } 1130 1131 // continue walking upstream 1132 ok, err := checkUserPathRoot(e.Up) 1133 if err != nil { 1134 return false, err 1135 } 1136 1137 if ok { 1138 // found a path, return 1139 return ok, nil 1140 } 1141 1142 // look at the next edge 1143 } 1144 1145 return false, nil 1146 } 1147 1148 for _, u := range users { 1149 ok, err := checkUserPathRoot(u.ID) 1150 if err != nil { 1151 return nil, err 1152 } 1153 1154 if ok { 1155 ret = append(ret, u) 1156 } 1157 } 1158 1159 return ret, nil 1160 } 1161 1162 // up returns upstream ids for a node 1163 func (sdb *DbSqlite) up(id string, includeDeleted bool) ([]string, error) { 1164 var ups []string 1165 1166 edges, err := sdb.edges(nil, "SELECT * FROM edges WHERE down=?", id) 1167 if err != nil { 1168 return nil, err 1169 } 1170 1171 for _, e := range edges { 1172 if includeDeleted { 1173 ups = append(ups, e.Up) 1174 } else { 1175 p, _ := e.Points.Find(data.PointTypeTombstone, "") 1176 if math.Mod(p.Value, 2) == 0 { 1177 ups = append(ups, e.Up) 1178 } 1179 } 1180 } 1181 1182 return ups, nil 1183 }