@@ -51,20 +51,42 @@ public class PostgreSQLStorage {
5151 )
5252 defer { Task { _ = try ? await connection. close ( ) } }
5353
54- let query : PostgresQuery = """
54+ // First, check if a fuzzer with this name already exists
55+ let checkQuery : PostgresQuery = " SELECT fuzzer_id, status FROM main WHERE fuzzer_name = \( name) "
56+ let checkResult = try await connection. query ( checkQuery, logger: self . logger)
57+ let checkRows = try await checkResult. collect ( )
58+
59+ if let existingRow = checkRows. first {
60+ let existingFuzzerId = try existingRow. decode ( Int . self, context: . default)
61+ let existingStatus = try existingRow. decode ( String . self, context: . default)
62+
63+ // Update status to active if it was inactive
64+ if existingStatus != " active " {
65+ let updateQuery : PostgresQuery = " UPDATE main SET status = 'active' WHERE fuzzer_id = \( existingFuzzerId) "
66+ try await connection. query ( updateQuery, logger: self . logger)
67+ logger. info ( " Reactivated existing fuzzer: fuzzerId= \( existingFuzzerId) " )
68+ } else {
69+ logger. info ( " Reusing existing active fuzzer: fuzzerId= \( existingFuzzerId) " )
70+ }
71+
72+ return existingFuzzerId
73+ }
74+
75+ // If no existing fuzzer found, create a new one
76+ let insertQuery : PostgresQuery = """
5577 INSERT INTO main (fuzzer_name, engine_type, status)
5678 VALUES ( \( name) , \( engineType) , 'active')
5779 RETURNING fuzzer_id
5880 """
5981
60- let result = try await connection. query ( query , logger: self . logger)
82+ let result = try await connection. query ( insertQuery , logger: self . logger)
6183 let rows = try await result. collect ( )
6284 guard let row = rows. first else {
6385 throw PostgreSQLStorageError . noResult
6486 }
6587
6688 let fuzzerId = try row. decode ( Int . self, context: . default)
67- self . logger. info ( " Fuzzer registration successful : fuzzerId=\( fuzzerId) " )
89+ self . logger. info ( " Created new fuzzer : fuzzerId=\( fuzzerId) " )
6890 return fuzzerId
6991 }
7092
@@ -362,10 +384,115 @@ public class PostgreSQLStorage {
362384 public func getRecentPrograms( fuzzerId: Int , since: Date , limit: Int = 100 ) async throws -> [ ( Program , ExecutionMetadata ) ] {
363385 logger. info ( " Getting recent programs: fuzzerId= \( fuzzerId) , since= \( since) , limit= \( limit) " )
364386
365- // For now, return empty array
366- // TODO: Implement actual database query when PostgreSQL is set up
367- logger. info ( " Mock recent programs lookup: no programs found " )
368- return [ ]
387+ guard let eventLoopGroup = databasePool. getEventLoopGroup ( ) else {
388+ throw PostgreSQLStorageError . noResult
389+ }
390+
391+ let connection = try await PostgresConnection . connect (
392+ on: eventLoopGroup. next ( ) ,
393+ configuration: PostgresConnection . Configuration (
394+ host: " localhost " ,
395+ port: 5433 ,
396+ username: " fuzzilli " ,
397+ password: " fuzzilli123 " ,
398+ database: " fuzzilli " ,
399+ tls: . disable
400+ ) ,
401+ id: 0 ,
402+ logger: logger
403+ )
404+ defer { Task { _ = try ? await connection. close ( ) } }
405+
406+ // Query for recent programs with their latest execution metadata
407+ let queryString = """
408+ SELECT
409+ p.program_base64,
410+ p.program_size,
411+ p.program_hash,
412+ p.created_at,
413+ eo.outcome,
414+ eo.description,
415+ e.execution_time_ms,
416+ e.coverage_total,
417+ e.signal_code,
418+ e.exit_code
419+ FROM program p
420+ LEFT JOIN execution e ON p.program_base64 = e.program_base64
421+ LEFT JOIN execution_outcome eo ON e.execution_outcome_id = eo.id
422+ WHERE p.fuzzer_id = \( fuzzerId)
423+ AND p.created_at >= ' \( since. ISO8601Format ( ) ) '
424+ ORDER BY p.created_at DESC
425+ LIMIT \( limit)
426+ """
427+
428+ let query = PostgresQuery ( stringLiteral: queryString)
429+ let result = try await connection. query ( query, logger: self . logger)
430+ let rows = try await result. collect ( )
431+
432+ var programs : [ ( Program , ExecutionMetadata ) ] = [ ]
433+
434+ for row in rows {
435+ let programBase64 = try row. decode ( String . self, context: . default)
436+ let programSize = try row. decode ( Int . self, context: . default)
437+ let programHash = try row. decode ( String . self, context: . default)
438+ let createdAt = try row. decode ( Date . self, context: . default)
439+ let outcome = try row. decode ( String ? . self, context: . default)
440+ let description = try row. decode ( String ? . self, context: . default)
441+ let executionTimeMs = try row. decode ( Int ? . self, context: . default)
442+ let coverageTotal = try row. decode ( Double ? . self, context: . default)
443+ let signalCode = try row. decode ( Int ? . self, context: . default)
444+ let exitCode = try row. decode ( Int ? . self, context: . default)
445+
446+ // Decode the program from base64
447+ guard let programData = Data ( base64Encoded: programBase64) else {
448+ logger. warning ( " Failed to decode base64 data for program: \( programHash) " )
449+ continue
450+ }
451+
452+ let program : Program
453+ do {
454+ let protobuf = try Fuzzilli_Protobuf_Program ( serializedBytes: programData)
455+ program = try Program ( from: protobuf)
456+ } catch {
457+ logger. warning ( " Failed to decode program from protobuf: \( programHash) , error: \( error) " )
458+ continue
459+ }
460+
461+ // Create execution metadata
462+
463+ // Map outcome string to database ID
464+ let outcomeId : Int
465+ switch ( outcome ?? " Succeeded " ) . lowercased ( ) {
466+ case " crashed " :
467+ outcomeId = 1
468+ case " failed " :
469+ outcomeId = 2
470+ case " succeeded " :
471+ outcomeId = 3
472+ case " timedout " :
473+ outcomeId = 4
474+ case " sigcheck " :
475+ outcomeId = 34
476+ default :
477+ outcomeId = 3 // Default to succeeded
478+ }
479+
480+ let dbOutcome = DatabaseExecutionOutcome (
481+ id: outcomeId,
482+ outcome: outcome ?? " Succeeded " ,
483+ description: description ?? " Program executed successfully "
484+ )
485+
486+ var metadata = ExecutionMetadata ( lastOutcome: dbOutcome)
487+ if let coverage = coverageTotal {
488+ metadata. lastCoverage = coverage
489+ }
490+
491+ programs. append ( ( program, metadata) )
492+ }
493+
494+ logger. info ( " Loaded \( programs. count) recent programs from database " )
495+ return programs
369496 }
370497
371498 /// Update program metadata
0 commit comments