@@ -29,6 +29,7 @@ type keyDefinition struct {
2929}
3030
3131var keys = []keyDefinition {
32+ {"cursor" , "TEXT NOT NULL" },
3233 {"name" , "TEXT NOT NULL" },
3334 {"version" , "TEXT NOT NULL" },
3435 {"formatVersion" , "TEXT NOT NULL" },
@@ -47,20 +48,25 @@ var keys = []keyDefinition{
4748const defaultMaxBulkAddBatch = 2000
4849
4950type SQLiteRepository struct {
50- db * sql.DB
51- path string
52- maxBulkAddBatch int
53- numberFields int
51+ db * sql.DB
52+ path string
53+ maxBulkAddBatchSize int
54+ maxTotalArgs int
5455}
5556
5657var _ Repository = new (SQLiteRepository )
5758
58- func NewFileSQLDB (path string ) (* SQLiteRepository , error ) {
59+ type FileSQLDBOptions struct {
60+ Path string
61+ BatchSizeInserts int
62+ }
63+
64+ func NewFileSQLDB (options FileSQLDBOptions ) (* SQLiteRepository , error ) {
5965 // NOTE: Even using sqlcache (with Ristretto or Redis), data column needs to be processed (Unmarshalled)
6066 // again for all the Get queries performed, so there is no advantage in time of using sqlcache with SQLite
6167 // for our use case.
6268
63- db , err := sql .Open ("sqlite" , path )
69+ db , err := sql .Open ("sqlite" , options . Path )
6470 if err != nil {
6571 return nil , fmt .Errorf ("failed to open database: %w" , err )
6672 }
@@ -69,19 +75,27 @@ func NewFileSQLDB(path string) (*SQLiteRepository, error) {
6975 return nil , fmt .Errorf ("failed to connect to database: %w" , err )
7076 }
7177
72- dbRepo , err := newSQLiteRepository (db )
78+ dbRepo , err := newSQLiteRepository (sqlDBOptions {
79+ db : db ,
80+ batchSizeInserts : options .BatchSizeInserts ,
81+ })
7382 if err != nil {
7483 return nil , fmt .Errorf ("failed to create SQLite repository: %w" , err )
7584 }
7685 if err := dbRepo .Initialize (context .Background ()); err != nil {
7786 return nil , fmt .Errorf ("failed to create database: %w" , err )
7887 }
79- dbRepo .path = path
88+ dbRepo .path = options . Path
8089
8190 return dbRepo , nil
8291}
8392
84- func newSQLiteRepository (db * sql.DB ) (* SQLiteRepository , error ) {
93+ type sqlDBOptions struct {
94+ db * sql.DB
95+ batchSizeInserts int
96+ }
97+
98+ func newSQLiteRepository (options sqlDBOptions ) (* SQLiteRepository , error ) {
8599 // https://www.sqlite.org/pragma.html#pragma_journal_mode
86100 // Not observed any performance difference with WAL mode, so keeping it as DELETE mode for now.
87101 // if _, err = db.Exec("PRAGMA journal_mode = WAL;"); err != nil {
@@ -106,10 +120,14 @@ func newSQLiteRepository(db *sql.DB) (*SQLiteRepository, error) {
106120 // if _, err := db.Exec("PRAGMA cache_size = -10000;"); err != nil {
107121 // return nil, fmt.Errorf("failed to update cache_size: %w", err)
108122 // }
123+ batchSize := defaultMaxBulkAddBatch
124+ if options .batchSizeInserts > 0 {
125+ batchSize = options .batchSizeInserts
126+ }
109127 return & SQLiteRepository {
110- db : db ,
111- maxBulkAddBatch : defaultMaxBulkAddBatch ,
112- numberFields : len (keys ),
128+ db : options . db ,
129+ maxBulkAddBatchSize : batchSize ,
130+ maxTotalArgs : batchSize * len (keys ),
113131 }, nil
114132}
115133
@@ -119,6 +137,7 @@ func (r *SQLiteRepository) File(ctx context.Context) string {
119137
120138func (r * SQLiteRepository ) Ping (ctx context.Context ) error {
121139 span , ctx := apm .StartSpan (ctx , "SQL: Ping" , "app" )
140+ span .Context .SetLabel ("database.path" , r .File (ctx ))
122141 defer span .End ()
123142 if r .db == nil {
124143 return errors .New ("database is not initialized" )
@@ -131,14 +150,15 @@ func (r *SQLiteRepository) Ping(ctx context.Context) error {
131150
132151func (r * SQLiteRepository ) Initialize (ctx context.Context ) error {
133152 span , ctx := apm .StartSpan (ctx , "SQL: Initialize" , "app" )
153+ span .Context .SetLabel ("database.path" , r .File (ctx ))
134154 defer span .End ()
135155 createQuery := strings.Builder {}
136156 createQuery .WriteString ("CREATE TABLE IF NOT EXISTS " )
137157 createQuery .WriteString ("packages (" )
138158 for _ , i := range keys {
139159 createQuery .WriteString (fmt .Sprintf ("%s %s, " , i .Name , i .SQLType ))
140160 }
141- createQuery .WriteString ("PRIMARY KEY (name, version));" )
161+ createQuery .WriteString ("PRIMARY KEY (cursor, name, version));" )
142162 if _ , err := r .db .ExecContext (ctx , createQuery .String ()); err != nil {
143163 return err
144164 }
@@ -159,19 +179,18 @@ func (r *SQLiteRepository) Initialize(ctx context.Context) error {
159179
160180func (r * SQLiteRepository ) BulkAdd (ctx context.Context , database string , pkgs []* Package ) error {
161181 span , ctx := apm .StartSpan (ctx , "SQL: Insert batches" , "app" )
182+ span .Context .SetLabel ("insert.batch.size" , r .maxBulkAddBatchSize )
183+ span .Context .SetLabel ("database.path" , r .File (ctx ))
162184 defer span .End ()
163185
164186 if len (pkgs ) == 0 {
165187 return nil
166188 }
167189
168190 totalProcessed := 0
169- args := make ([]any , 0 , r .maxBulkAddBatch * r . numberFields )
191+ args := make ([]any , 0 , r .maxTotalArgs )
170192 for {
171193 read := 0
172- // reuse args slice
173- args = args [:0 ]
174-
175194 var sb strings.Builder
176195 sb .WriteString ("INSERT INTO " )
177196 sb .WriteString (database )
@@ -184,7 +203,7 @@ func (r *SQLiteRepository) BulkAdd(ctx context.Context, database string, pkgs []
184203 }
185204 sb .WriteString (") values" )
186205
187- endBatch := totalProcessed + r .maxBulkAddBatch
206+ endBatch := totalProcessed + r .maxBulkAddBatchSize
188207 for i := totalProcessed ; i < endBatch && i < len (pkgs ); i ++ {
189208 sb .WriteString ("(" )
190209 for j := range keys {
@@ -206,6 +225,7 @@ func (r *SQLiteRepository) BulkAdd(ctx context.Context, database string, pkgs []
206225 discoveryFields := addCommasToString (pkgs [i ].DiscoveryFields )
207226
208227 args = append (args ,
228+ pkgs [i ].Cursor ,
209229 pkgs [i ].Name ,
210230 pkgs [i ].Version ,
211231 pkgs [i ].FormatVersion ,
@@ -240,6 +260,9 @@ func (r *SQLiteRepository) BulkAdd(ctx context.Context, database string, pkgs []
240260 if totalProcessed >= len (pkgs ) {
241261 break
242262 }
263+
264+ // reuse args slice
265+ args = args [:0 ]
243266 }
244267
245268 return nil
@@ -263,6 +286,7 @@ func addCommasToString(s string) string {
263286
264287func (r * SQLiteRepository ) All (ctx context.Context , database string , whereOptions WhereOptions ) ([]* Package , error ) {
265288 span , ctx := apm .StartSpan (ctx , "SQL: Get All" , "app" )
289+ span .Context .SetLabel ("database.path" , r .File (ctx ))
266290 defer span .End ()
267291
268292 var all []* Package
@@ -276,6 +300,7 @@ func (r *SQLiteRepository) All(ctx context.Context, database string, whereOption
276300
277301func (r * SQLiteRepository ) AllFunc (ctx context.Context , database string , whereOptions WhereOptions , process func (ctx context.Context , pkg * Package ) error ) error {
278302 span , ctx := apm .StartSpan (ctx , "SQL: Get All (process each package)" , "app" )
303+ span .Context .SetLabel ("database.path" , r .File (ctx ))
279304 defer span .End ()
280305
281306 useBaseData := whereOptions == nil || ! whereOptions .UseFullData ()
@@ -301,8 +326,6 @@ func (r *SQLiteRepository) AllFunc(ctx context.Context, database string, whereOp
301326 clause , whereArgs = whereOptions .Where ()
302327 query .WriteString (clause )
303328 }
304- // TODO: remove debug
305- // fmt.Println("Query:", query.String())
306329 rows , err := r .db .QueryContext (ctx , query .String (), whereArgs ... )
307330 if err != nil {
308331 return err
@@ -313,6 +336,7 @@ func (r *SQLiteRepository) AllFunc(ctx context.Context, database string, whereOp
313336 var pkg Package
314337 for rows .Next () {
315338 if err := rows .Scan (
339+ & pkg .Cursor ,
316340 & pkg .Name ,
317341 & pkg .Version ,
318342 & pkg .FormatVersion ,
@@ -345,6 +369,7 @@ func (r *SQLiteRepository) AllFunc(ctx context.Context, database string, whereOp
345369
346370func (r * SQLiteRepository ) Drop (ctx context.Context , table string ) error {
347371 span , ctx := apm .StartSpan (ctx , "SQL: Drop" , "app" )
372+ span .Context .SetLabel ("database.path" , r .File (ctx ))
348373 defer span .End ()
349374 query := fmt .Sprintf ("DROP TABLE IF EXISTS %s" , table )
350375 _ , err := r .db .ExecContext (ctx , query )
@@ -370,16 +395,34 @@ type FilterOptions struct {
370395type SQLOptions struct {
371396 Filter * FilterOptions
372397
398+ CurrentCursor string
399+
373400 IncludeFullData bool // If true, the query will return the full data field instead of the base data field
374401}
375402
376403func (o * SQLOptions ) Where () (string , []any ) {
377- if o == nil || o . Filter == nil {
404+ if o == nil {
378405 return "" , nil
379406 }
380407 var sb strings.Builder
381408 var args []any
409+ // Always filter by cursor
410+ if o .CurrentCursor != "" {
411+ sb .WriteString ("cursor = ?" )
412+ args = append (args , o .CurrentCursor )
413+ }
414+
415+ if o .Filter == nil {
416+ if sb .Len () == 0 {
417+ return "" , nil
418+ }
419+ return fmt .Sprintf (" WHERE %s" , sb .String ()), args
420+ }
421+
382422 if o .Filter .Type != "" {
423+ if sb .Len () > 0 {
424+ sb .WriteString (" AND " )
425+ }
383426 sb .WriteString ("type = ?" )
384427 args = append (args , o .Filter .Type )
385428 }
0 commit comments