2525#include "tail_sql.h"
2626#include "tail_file.h"
2727
28+ #include <inttypes.h>
29+
2830struct query_status {
2931 int id ;
3032 int rows ;
@@ -271,6 +273,138 @@ static int stmt_add_param_concat(struct flb_tail_config *ctx,
271273 return 0 ;
272274}
273275
276+ /*
277+ * Scalable stale inode cleanup: use a temp table to avoid SQLite variable limits.
278+ *
279+ * The legacy implementation builds:
280+ * DELETE ... WHERE inode NOT IN (?,?,?,...);
281+ * which requires one bound parameter per inode and fails when the number of
282+ * monitored files exceeds SQLITE_LIMIT_VARIABLE_NUMBER (commonly 32766 in our
283+ * bundled SQLite, but can vary).
284+ */
285+ static int flb_tail_db_stale_file_delete_temp_table (struct flb_tail_config * ctx ,
286+ uint64_t file_count )
287+ {
288+ int ret ;
289+ int changes ;
290+ int txn_started = FLB_FALSE ;
291+ sqlite3_stmt * stmt_insert_inode = NULL ;
292+ struct mk_list * head ;
293+ struct mk_list * tmp ;
294+ struct flb_tail_file * file ;
295+
296+ /* If there are no monitored files, delete everything from the DB table. */
297+ if (file_count == 0 ) {
298+ ret = flb_sqldb_query (ctx -> db , "DELETE FROM in_tail_files;" , NULL , NULL );
299+ if (ret != FLB_OK ) {
300+ flb_plg_error (ctx -> ins , "db: cannot delete all stale inodes (no monitored files)" );
301+ goto error ;
302+ }
303+
304+ changes = sqlite3_changes (ctx -> db -> handler );
305+ flb_plg_info (ctx -> ins , "db: delete unmonitored stale inodes from the database: count=%d" ,
306+ changes );
307+ goto error ;
308+ }
309+
310+ /* Create/clear temp table holding current monitored inodes. */
311+ ret = flb_sqldb_query (ctx -> db ,
312+ "CREATE TEMP TABLE IF NOT EXISTS in_tail_current_inodes ("
313+ " inode INTEGER PRIMARY KEY"
314+ ");" ,
315+ NULL , NULL );
316+ if (ret != FLB_OK ) {
317+ flb_plg_error (ctx -> ins , "db: cannot create temp table for inode cleanup" );
318+ tail_db_unlock (ctx );
319+ goto error ;
320+ }
321+
322+ ret = flb_sqldb_query (ctx -> db , "DELETE FROM in_tail_current_inodes;" , NULL , NULL );
323+ if (ret != FLB_OK ) {
324+ flb_plg_error (ctx -> ins , "db: cannot clear temp inode table" );
325+ goto error ;
326+ }
327+
328+ /* Use a transaction for faster bulk inserts. */
329+ ret = flb_sqldb_query (ctx -> db , "BEGIN;" , NULL , NULL );
330+ if (ret != FLB_OK ) {
331+ flb_plg_error (ctx -> ins , "db: cannot begin transaction for temp inode inserts" );
332+ goto error ;
333+ }
334+ txn_started = FLB_TRUE ;
335+
336+ ret = sqlite3_prepare_v2 (ctx -> db -> handler ,
337+ "INSERT OR IGNORE INTO in_tail_current_inodes(inode) VALUES (?);" ,
338+ -1 , & stmt_insert_inode , 0 );
339+ if (ret != SQLITE_OK ) {
340+ flb_plg_error (ctx -> ins , "db: cannot prepare temp inode insert statement, ret=%d" , ret );
341+ goto error ;
342+ }
343+
344+ mk_list_foreach_safe (head , tmp , & ctx -> files_static ) {
345+ file = mk_list_entry (head , struct flb_tail_file , _head );
346+
347+ ret = sqlite3_bind_int64 (stmt_insert_inode , 1 , (sqlite3_int64 ) file -> inode );
348+ if (ret != SQLITE_OK ) {
349+ flb_plg_error (ctx -> ins , "db: error binding temp inode insert: inode=%" PRIu64 ", ret=%d" ,
350+ file -> inode , ret );
351+ goto error ;
352+ }
353+
354+ ret = sqlite3_step (stmt_insert_inode );
355+ if (ret != SQLITE_DONE ) {
356+ flb_plg_error (ctx -> ins , "db: error inserting inode into temp table: inode=%" PRIu64 ", ret=%d" ,
357+ file -> inode , ret );
358+ goto error ;
359+ }
360+
361+ sqlite3_clear_bindings (stmt_insert_inode );
362+ sqlite3_reset (stmt_insert_inode );
363+ }
364+
365+ sqlite3_finalize (stmt_insert_inode );
366+ stmt_insert_inode = NULL ;
367+
368+ /* Delete any inode that is not in the current monitored set. */
369+ ret = flb_sqldb_query (ctx -> db ,
370+ "DELETE FROM in_tail_files "
371+ "WHERE inode NOT IN (SELECT inode FROM in_tail_current_inodes);" ,
372+ NULL , NULL );
373+ if (ret != FLB_OK ) {
374+ flb_plg_error (ctx -> ins , "db: cannot delete stale inodes using temp table" );
375+ goto error ;
376+ }
377+
378+ ret = flb_sqldb_query (ctx -> db , "COMMIT;" , NULL , NULL );
379+ if (ret != FLB_OK ) {
380+ flb_plg_error (ctx -> ins , "db: cannot commit transaction for temp inode inserts" );
381+ goto error ;
382+ }
383+ txn_started = FLB_FALSE ;
384+
385+ changes = sqlite3_changes (ctx -> db -> handler );
386+ flb_plg_info (ctx -> ins , "db: delete unmonitored stale inodes from the database: count=%d" ,
387+ changes );
388+
389+ tail_db_unlock (ctx );
390+
391+ return 0 ;
392+
393+ error :
394+ if (stmt_insert_inode ) {
395+ sqlite3_finalize (stmt_insert_inode );
396+ }
397+
398+ if (txn_started == FLB_TRUE ) {
399+ /* Best-effort rollback */
400+ flb_sqldb_query (ctx -> db , "ROLLBACK;" , NULL , NULL );
401+ }
402+
403+ tail_db_unlock (ctx );
404+
405+ return -1 ;
406+ }
407+
274408int flb_tail_db_file_set (struct flb_tail_file * file ,
275409 struct flb_tail_config * ctx )
276410{
@@ -430,6 +564,7 @@ int flb_tail_db_stale_file_delete(struct flb_input_instance *ins,
430564 size_t sql_size ;
431565 uint64_t idx ;
432566 uint64_t file_count = ctx -> files_static_count ;
567+ int max_vars = -1 ;
433568 flb_sds_t stale_delete_sql ;
434569 flb_sds_t sds_tmp ;
435570 sqlite3_stmt * stmt_delete_inodes = NULL ;
@@ -447,14 +582,38 @@ int flb_tail_db_stale_file_delete(struct flb_input_instance *ins,
447582 return -1 ;
448583 }
449584
585+
586+ /*
587+ * Avoid SQLite variable limits for large monitored file sets.
588+ *
589+ * sqlite3_limit(..., SQLITE_LIMIT_VARIABLE_NUMBER, -1) returns the current
590+ * runtime limit (compile-time hard limit may be higher). If our monitored
591+ * file count exceeds this, the legacy NOT IN (?,?,...) statement will fail
592+ * at prepare-time.
593+ */
594+ max_vars = sqlite3_limit (ctx -> db -> handler , SQLITE_LIMIT_VARIABLE_NUMBER , -1 );
595+ if (max_vars > 0 && file_count > (uint64_t ) max_vars ) {
596+ flb_plg_warn (ctx -> ins ,
597+ "db: large file set detected (%" PRIu64 " files) exceeds SQLite variable limit (%d); "
598+ "using temp-table cleanup for stale inode deletion" ,
599+ file_count , max_vars );
600+ return flb_tail_db_stale_file_delete_temp_table (ctx , file_count );
601+ }
602+
450603 /* Create a stmt sql buffer */
451604 sql_size = SQL_DELETE_STALE_FILE_START_LEN ;
452605 sql_size += SQL_DELETE_STALE_FILE_WHERE_LEN ;
453606 sql_size += SQL_STMT_START_PARAM_LEN ;
454607 sql_size += SQL_STMT_PARAM_END_LEN ;
455608 sql_size += SQL_STMT_END_LEN ;
456609 if (file_count > 0 ) {
457- sql_size += (SQL_STMT_ADD_PARAM_LEN * file_count );
610+ /*
611+ * We already account for the first '?' via SQL_STMT_START_PARAM_LEN.
612+ * Additional parameters are count-1 occurrences of ",?".
613+ */
614+ if (file_count > 1 ) {
615+ sql_size += (SQL_STMT_ADD_PARAM_LEN * (file_count - 1 ));
616+ }
458617 }
459618
460619 stale_delete_sql = flb_sds_create_size (sql_size + 1 );
0 commit comments