From 689fa2e9d9e674b1250fa857cd3737cb303bbe5c Mon Sep 17 00:00:00 2001 From: Zack Galbreath Date: Thu, 19 Dec 2024 14:52:39 -0500 Subject: [PATCH] Replace custom SQL in removeBuilds with Eloquent relationships (#2624) This commit introduces the use of Eloquent relationships to remove the following types of shared records: * buildfailure * buildfailuredetails * coveragefile --- app/Models/BuildUpdate.php | 57 ++++++++++++++++ app/Models/CoverageFile.php | 40 +++++++++++ app/Models/RichBuildAlert.php | 53 +++++++++++++++ app/Models/RichBuildAlertDetails.php | 53 +++++++++++++++ app/Utils/DatabaseCleanupUtils.php | 99 +++++++++------------------- phpstan-baseline.neon | 4 +- 6 files changed, 237 insertions(+), 69 deletions(-) create mode 100644 app/Models/BuildUpdate.php create mode 100644 app/Models/CoverageFile.php create mode 100644 app/Models/RichBuildAlert.php create mode 100644 app/Models/RichBuildAlertDetails.php diff --git a/app/Models/BuildUpdate.php b/app/Models/BuildUpdate.php new file mode 100644 index 0000000000..2741699a52 --- /dev/null +++ b/app/Models/BuildUpdate.php @@ -0,0 +1,57 @@ + + */ +class BuildUpdate extends Model +{ + protected $table = 'buildupdate'; + + public $timestamps = false; + + protected $fillable = [ + 'starttime', + 'endtime', + 'command', + 'type', + 'nfiles', + 'warnings', + 'revision', + 'priorrevision', + 'path', + ]; + + protected $casts = [ + 'id' => 'integer', + 'starttime' => 'datetime', + 'endtime' => 'datetime', + 'nfiles' => 'integer', + 'warnings' => 'integer', + ]; + + /** + * @return BelongsToMany + */ + public function builds(): BelongsToMany + { + return $this->belongsToMany(Build::class, 'build2update', 'updateid', 'buildid'); + } +} diff --git a/app/Models/CoverageFile.php b/app/Models/CoverageFile.php new file mode 100644 index 0000000000..6155bf2e73 --- /dev/null +++ b/app/Models/CoverageFile.php @@ -0,0 +1,40 @@ + + */ +class CoverageFile extends Model +{ + protected $table = 'coveragefile'; + + public $timestamps = false; + + protected $fillable = [ + 'fullpath', + 'file', + 'crc32', + ]; + + protected $casts = [ + 'crc32' => 'integer', + ]; + + /** + * @return HasManyThrough + */ + public function builds(): HasManyThrough + { + return $this->hasManyThrough(Build::class, Coverage::class, 'fileid', 'id', 'id', 'buildid'); + } +} diff --git a/app/Models/RichBuildAlert.php b/app/Models/RichBuildAlert.php new file mode 100644 index 0000000000..5cf4be4126 --- /dev/null +++ b/app/Models/RichBuildAlert.php @@ -0,0 +1,53 @@ + + */ +class RichBuildAlert extends Model +{ + protected $table = 'buildfailure'; + + public $timestamps = false; + + protected $fillable = [ + 'buildid', + 'detailsid', + 'sourcefile', + 'newstatus', + ]; + + protected $casts = [ + 'buildid' => 'integer', + 'detailsid' => 'integer', + 'newstatus' => 'integer', + ]; + + /** + * @return BelongsTo + */ + public function build(): BelongsTo + { + return $this->belongsTo(Build::class, 'buildid'); + } + + /** + * @return HasOne + */ + public function details(): HasOne + { + return $this->hasOne(RichBuildAlertDetails::class, 'id', 'detailsid'); + } +} diff --git a/app/Models/RichBuildAlertDetails.php b/app/Models/RichBuildAlertDetails.php new file mode 100644 index 0000000000..8a83334989 --- /dev/null +++ b/app/Models/RichBuildAlertDetails.php @@ -0,0 +1,53 @@ + + */ +class RichBuildAlertDetails extends Model +{ + protected $table = 'buildfailuredetails'; + + public $timestamps = false; + + protected $fillable = [ + 'type', + 'stdoutput', + 'stderror', + 'exitcondition', + 'language', + 'targetname', + 'outputfile', + 'outputtype', + 'crc32', + ]; + + protected $casts = [ + 'type' => 'integer', + 'crc32' => 'integer', + ]; + + /** + * @return HasManyThrough + */ + public function builds(): HasManyThrough + { + return $this->hasManyThrough(Build::class, RichBuildAlert::class, 'detailsid', 'id', 'id', 'buildid'); + } +} diff --git a/app/Utils/DatabaseCleanupUtils.php b/app/Utils/DatabaseCleanupUtils.php index 0349c04f22..fe9cbcd56d 100644 --- a/app/Utils/DatabaseCleanupUtils.php +++ b/app/Utils/DatabaseCleanupUtils.php @@ -6,8 +6,11 @@ use App\Models\Build; use App\Models\BuildGroup; +use App\Models\BuildUpdate; use App\Models\Configure; +use App\Models\CoverageFile; use App\Models\Note; +use App\Models\RichBuildAlertDetails; use CDash\Database; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\DB; @@ -120,26 +123,18 @@ public static function removeBuild($buildid) : void $buildids[] = intval($b); } - $db = Database::getInstance(); - $buildid_prepare_array = $db->createPreparedArray(count($buildids)); + // Use Eloquent relationships to delete shared records that are only + // used by builds that are about to be deleted. - // Delete buildfailuredetails that are only used by builds that are being - // deleted. - DB::delete(" - DELETE FROM buildfailuredetails WHERE id IN ( - SELECT a.detailsid - FROM buildfailure AS a - LEFT JOIN buildfailure AS b ON ( - a.detailsid=b.detailsid - AND b.buildid NOT IN $buildid_prepare_array - ) - WHERE a.buildid IN $buildid_prepare_array - GROUP BY a.detailsid - HAVING count(b.detailsid)=0 - ) - ", array_merge($buildids, $buildids)); - - // Delete the configure if not shared. + // buildfailuredetails + RichBuildAlertDetails::whereHas('builds', function (Builder $query) use ($buildids) { + $query->whereIn('build.id', $buildids); + }) + ->whereDoesntHave('builds', function (Builder $query) use ($buildids) { + $query->whereNotIn('build.id', $buildids); + })->delete(); + + // configure Configure::whereHas('builds', function (Builder $query) use ($buildids) { $query->whereIn('id', $buildids); }) @@ -147,32 +142,15 @@ public static function removeBuild($buildid) : void $query->whereNotIn('id', $buildids); })->delete(); - // coverage files are kept unless they are shared - DB::delete(" - DELETE FROM coveragefile - WHERE id IN ( - SELECT f1.id - FROM ( - SELECT a.fileid AS id, COUNT(DISTINCT a.buildid) AS c - FROM coverage a - WHERE a.buildid IN $buildid_prepare_array - GROUP BY a.fileid - ) AS f1 - INNER JOIN ( - SELECT b.fileid AS id, COUNT(DISTINCT b.buildid) AS c - FROM coverage b - INNER JOIN ( - SELECT fileid - FROM coverage - WHERE buildid IN $buildid_prepare_array - ) AS d ON b.fileid = d.fileid - GROUP BY b.fileid - ) AS f2 ON (f1.id = f2.id) - WHERE f1.c = f2.c - ) - ", array_merge($buildids, $buildids)); - - // Delete notes if not shared. + // coveragefile + CoverageFile::whereHas('builds', function (Builder $query) use ($buildids) { + $query->whereIn('build.id', $buildids); + }) + ->whereDoesntHave('builds', function (Builder $query) use ($buildids) { + $query->whereNotIn('build.id', $buildids); + })->delete(); + + // note Note::whereHas('builds', function (Builder $query) use ($buildids) { $query->whereIn('id', $buildids); }) @@ -180,29 +158,16 @@ public static function removeBuild($buildid) : void $query->whereNotIn('id', $buildids); })->delete(); - // Delete the update if not shared - $build2update = DB::select(" - SELECT a.updateid - FROM build2update AS a - LEFT JOIN build2update AS b ON ( - a.updateid=b.updateid - AND b.buildid NOT IN $buildid_prepare_array - ) - WHERE a.buildid IN $buildid_prepare_array - GROUP BY a.updateid - HAVING count(b.updateid)=0 - ", array_merge($buildids, $buildids)); - - $updateids = []; - foreach ($build2update as $build2update_array) { - // Update is not shared we delete - $updateids[] = intval($build2update_array->updateid); - } + // buildupdate + BuildUpdate::whereHas('builds', function (Builder $query) use ($buildids) { + $query->whereIn('build.id', $buildids); + }) + ->whereDoesntHave('builds', function (Builder $query) use ($buildids) { + $query->whereNotIn('build.id', $buildids); + })->delete(); - if (count($updateids) > 0) { - $updateids_prepare_array = $db->createPreparedArray(count($updateids)); - DB::delete("DELETE FROM buildupdate WHERE id IN $updateids_prepare_array", $updateids); - } + $db = Database::getInstance(); + $buildid_prepare_array = $db->createPreparedArray(count($buildids)); // Delete tests and testoutputs that are not shared. // First find all the tests and testoutputs from builds that are about to be deleted. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6eb6e4c2be..ce790b59b0 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3842,12 +3842,12 @@ parameters: - message: "#^Dynamic call to static method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:whereIn\\(\\)\\.$#" - count: 2 + count: 5 path: app/Utils/DatabaseCleanupUtils.php - message: "#^Dynamic call to static method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:whereNotIn\\(\\)\\.$#" - count: 2 + count: 5 path: app/Utils/DatabaseCleanupUtils.php -