| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 | // Copyright (C) 2025 The Syncthing Authors.//// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this file,// You can obtain one at https://mozilla.org/MPL/2.0/.package sqliteimport (	"fmt"	"time"	"github.com/syncthing/syncthing/lib/protocol")type folderDB struct {	*baseDB	folderID string	localDeviceIdx  int64	deleteRetention time.Duration}func openFolderDB(folder, path string, deleteRetention time.Duration) (*folderDB, error) {	pragmas := []string{		"journal_mode = WAL",		"optimize = 0x10002",		"auto_vacuum = INCREMENTAL",		fmt.Sprintf("application_id = %d", applicationIDFolder),	}	schemas := []string{		"sql/schema/common/*",		"sql/schema/folder/*",	}	migrations := []string{		"sql/migrations/common/*",		"sql/migrations/folder/*",	}	base, err := openBase(path, maxDBConns, pragmas, schemas, migrations)	if err != nil {		return nil, err	}	fdb := &folderDB{		folderID:        folder,		baseDB:          base,		deleteRetention: deleteRetention,	}	_ = fdb.PutKV("folderID", []byte(folder))	// Touch device IDs that should always exist and have a low index	// numbers, and will never change	fdb.localDeviceIdx, _ = fdb.deviceIdxLocked(protocol.LocalDeviceID)	fdb.tplInput["LocalDeviceIdx"] = fdb.localDeviceIdx	return fdb, nil}// Open the database with options suitable for the migration inserts. This// is not a safe mode of operation for normal processing, use only for bulk// inserts with a close afterwards.func openFolderDBForMigration(folder, path string, deleteRetention time.Duration) (*folderDB, error) {	pragmas := []string{		"journal_mode = OFF",		"foreign_keys = 0",		"synchronous = 0",		"locking_mode = EXCLUSIVE",		fmt.Sprintf("application_id = %d", applicationIDFolder),	}	schemas := []string{		"sql/schema/common/*",		"sql/schema/folder/*",	}	base, err := openBase(path, 1, pragmas, schemas, nil)	if err != nil {		return nil, err	}	fdb := &folderDB{		folderID:        folder,		baseDB:          base,		deleteRetention: deleteRetention,	}	// Touch device IDs that should always exist and have a low index	// numbers, and will never change	fdb.localDeviceIdx, _ = fdb.deviceIdxLocked(protocol.LocalDeviceID)	fdb.tplInput["LocalDeviceIdx"] = fdb.localDeviceIdx	return fdb, nil}func (s *folderDB) deviceIdxLocked(deviceID protocol.DeviceID) (int64, error) {	devStr := deviceID.String()	var idx int64	if err := s.stmt(`		INSERT INTO devices(device_id)		VALUES (?)		ON CONFLICT(device_id) DO UPDATE			SET device_id = excluded.device_id		RETURNING idx	`).Get(&idx, devStr); err != nil {		return 0, wrap(err)	}	return idx, nil}
 |