Files
UnrealEngine/Engine/Source/Programs/UnrealGameSync/MetadataServer/Connectors/SqlConnector.cs
2025-05-18 13:04:45 +08:00

861 lines
33 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using MetadataServer.Models;
using MySql.Data.MySqlClient;
namespace MetadataServer.Connectors
{
public static class SqlConnector
{
private static string ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
private static bool StoreBuildMetadata = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings["StoreBuildMetadata"]);
public static LatestData GetLastIds(string Project = null)
{
// Get ids going back 432 builds for the project being asked for
// Do this by grouping by ChangeNumber to get unique entries, then take the 432nd id
long LastEventId = 0;
long LastCommentId = 0;
long LastBuildId = 0;
string ProjectLikeString = "%" + (Project == null ? String.Empty : GetProjectStream(Project)) + "%";
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
Connection.Open();
using (MySqlCommand Command = new MySqlCommand("WITH user_votes AS (SELECT UserVotes.Id, UserVotes.Changelist FROM ugs_db.UserVotes " +
"INNER JOIN ugs_db.Projects ON Projects.Id = UserVotes.ProjectId " +
"WHERE Projects.Name LIKE @param1 GROUP BY Changelist ORDER BY Changelist DESC LIMIT 100) " +
"SELECT * FROM user_votes ORDER BY user_votes.Changelist ASC LIMIT 1", Connection))
{
Command.Parameters.AddWithValue("@param1", ProjectLikeString);
using (MySqlDataReader Reader = Command.ExecuteReader())
{
while (Reader.Read())
{
LastEventId = Reader.GetInt64(0);
break;
}
}
}
using (MySqlCommand Command = new MySqlCommand("WITH comments AS (SELECT Comments.Id, Comments.ChangeNumber FROM ugs_db.Comments " +
"INNER JOIN ugs_db.Projects ON Projects.Id = Comments.ProjectId " +
"WHERE Projects.Name LIKE @param1 GROUP BY ChangeNumber ORDER BY ChangeNumber DESC LIMIT 100) " +
"SELECT * FROM comments ORDER BY comments.ChangeNumber ASC LIMIT 1", Connection))
{
Command.Parameters.AddWithValue("@param1", ProjectLikeString);
using (MySqlDataReader Reader = Command.ExecuteReader())
{
while (Reader.Read())
{
LastCommentId = Reader.GetInt32(0);
break;
}
}
}
using (MySqlCommand Command = new MySqlCommand("WITH badges AS (SELECT Badges.Id, Badges.ChangeNumber FROM ugs_db.Badges " +
"INNER JOIN ugs_db.Projects ON Projects.Id = Badges.ProjectId " +
"WHERE Projects.Name LIKE @param1 GROUP BY ChangeNumber ORDER BY ChangeNumber DESC LIMIT 100) " +
"SELECT * FROM badges ORDER BY badges.ChangeNumber ASC LIMIT 1", Connection))
{
//Command.Parameters.AddWithValue("@param1", ProjectId);
Command.Parameters.AddWithValue("@param1", ProjectLikeString);
using (MySqlDataReader Reader = Command.ExecuteReader())
{
while (Reader.Read())
{
LastBuildId = Math.Max(LastBuildId, Reader.GetInt32(0));
break;
}
}
}
}
return new LatestData { LastBuildId = LastBuildId, LastCommentId = LastCommentId, LastEventId = LastEventId };
}
public static List<EventData> GetUserVotes(string Project, long LastEventId)
{
List<EventData> ReturnedEvents = new List<EventData>();
string ProjectLikeString = "%" + (Project == null ? String.Empty : GetProjectStream(Project)) + "%";
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
Connection.Open();
using (MySqlCommand Command = new MySqlCommand("SELECT UserVotes.Id, UserVotes.Changelist, UserVotes.UserName, UserVotes.Verdict, UserVotes.Project FROM ugs_db.UserVotes " +
"INNER JOIN ugs_db.Projects ON Projects.Id = UserVotes.ProjectId WHERE UserVotes.Id > @param1 AND Projects.Name LIKE @param2 ORDER BY UserVotes.Id", Connection))
{
Command.Parameters.AddWithValue("@param1", LastEventId);
Command.Parameters.AddWithValue("@param2", ProjectLikeString);
using (MySqlDataReader Reader = Command.ExecuteReader())
{
while (Reader.Read())
{
EventData Review = new EventData();
Review.Id = Reader.GetInt64(0);
Review.Change = Reader.GetInt32(1);
Review.UserName = Reader.GetString(2);
Review.Project = Reader.IsDBNull(4) ? null : Reader.GetString(4);
if (Enum.TryParse(Reader.GetString(3), out Review.Type))
{
if (Review.Project == null || String.Compare(Review.Project, Project, true) == 0)
{
ReturnedEvents.Add(Review);
}
}
}
}
}
}
return ReturnedEvents;
}
public static List<CommentData> GetComments(string Project, long LastCommentId)
{
List<CommentData> ReturnedComments = new List<CommentData>();
string ProjectLikeString = "%" + (Project == null ? String.Empty : GetProjectStream(Project)) + "%";
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
Connection.Open();
using (MySqlCommand Command = new MySqlCommand("SELECT Comments.Id, Comments.ChangeNumber, Comments.UserName, Comments.Text, Comments.Project FROM ugs_db.Comments " +
"INNER JOIN ugs_db.Projects ON Projects.Id = Comments.ProjectId WHERE Comments.Id > @param1 AND Projects.Name LIKE @param2 ORDER BY Comments.Id", Connection))
{
Command.Parameters.AddWithValue("@param1", LastCommentId);
Command.Parameters.AddWithValue("@param2", ProjectLikeString);
using (MySqlDataReader Reader = Command.ExecuteReader())
{
while (Reader.Read())
{
CommentData Comment = new CommentData();
Comment.Id = Reader.GetInt32(0);
Comment.ChangeNumber = Reader.GetInt32(1);
Comment.UserName = Reader.GetString(2);
Comment.Text = Reader.GetString(3);
Comment.Project = Reader.GetString(4);
if (Comment.Project == null || String.Compare(Comment.Project, Project, true) == 0)
{
ReturnedComments.Add(Comment);
}
}
}
}
}
return ReturnedComments;
}
public static List<BuildData> GetBuilds(string Project, long LastBuildId)
{
List<BuildData> ReturnedBuilds = new List<BuildData>();
string ProjectLikeString = "%" + (Project == null ? String.Empty : GetProjectStream(Project)) + "%";
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
string QueryWithMetadata =
"SELECT Badges.Id, Badges.ChangeNumber, Badges.BuildType, Badges.Result, Badges.Url, Projects.Name, Badges.ArchivePath, Badges.Metadata FROM ugs_db.Badges " +
"INNER JOIN ugs_db.Projects ON Projects.Id = Badges.ProjectId WHERE Badges.Id > @param1 AND Projects.Name LIKE @param2 ORDER BY Badges.Id";
string QueryWithoutMetadata =
"SELECT Badges.Id, Badges.ChangeNumber, Badges.BuildType, Badges.Result, Badges.Url, Projects.Name, Badges.ArchivePath FROM ugs_db.Badges " +
"INNER JOIN ugs_db.Projects ON Projects.Id = Badges.ProjectId WHERE Badges.Id > @param1 AND Projects.Name LIKE @param2 ORDER BY Badges.Id";
Connection.Open();
using (MySqlCommand Command = new MySqlCommand(StoreBuildMetadata ? QueryWithMetadata : QueryWithoutMetadata, Connection))
{
Command.Parameters.AddWithValue("@param1", LastBuildId);
Command.Parameters.AddWithValue("@param2", ProjectLikeString);
using (MySqlDataReader Reader = Command.ExecuteReader())
{
while (Reader.Read())
{
BuildData Build = new BuildData();
Build.Id = Reader.GetInt32(0);
Build.ChangeNumber = Reader.GetInt32(1);
Build.BuildType = Reader.GetString(2).TrimEnd();
if (Enum.TryParse(Reader.GetString(3).TrimEnd(), true, out Build.Result))
{
Build.Url = Reader.GetString(4);
Build.Project = Reader.IsDBNull(5) ? null : Reader.GetString(5);
if (Build.Project == null || String.Compare(Build.Project, Project, true) == 0 || MatchesWildcard(Build.Project, Project))
{
ReturnedBuilds.Add(Build);
}
}
if (StoreBuildMetadata)
{
Build.Metadata = JsonConvert.DeserializeObject<BuildMetadata>(Reader.GetString(7));
}
LastBuildId = Math.Max(LastBuildId, Build.Id);
}
}
}
}
return ReturnedBuilds;
}
public static List<TelemetryErrorData> GetErrorData(int Records)
{
List<TelemetryErrorData> ReturnedErrors = new List<TelemetryErrorData>();
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
Connection.Open();
using (MySqlCommand Command = new MySqlCommand("SELECT Id, Type, Text, UserName, Project, Timestamp, Version, IpAddress FROM ugs_db.Errors ORDER BY Id DESC LIMIT @param1", Connection))
{
Command.Parameters.AddWithValue("@param1", Records);
using (MySqlDataReader Reader = Command.ExecuteReader())
{
while (Reader.Read())
{
TelemetryErrorData Error = new TelemetryErrorData();
Error.Id = Reader.GetInt32(0);
Enum.TryParse(Reader.GetString(1), true, out Error.Type);
Error.Text = Reader.GetString(2);
Error.UserName = Reader.GetString(3);
Error.Project = Reader.IsDBNull(4) ? null : Reader.GetString(4);
Error.Timestamp = Reader.GetDateTime(5);
Error.Version = Reader.GetString(6);
Error.IpAddress = Reader.GetString(7);
ReturnedErrors.Add(Error);
}
}
}
}
return ReturnedErrors;
}
private static long TryInsertAndGetProject(MySqlConnection Connection, string Project)
{
using (MySqlCommand Command = new MySqlCommand("INSERT IGNORE INTO ugs_db.Projects (Name) VALUES (@Project); SELECT Id FROM ugs_db.Projects WHERE Name = @Project", Connection))
{
Command.Parameters.AddWithValue("@Project", Project);
object ProjectId = Command.ExecuteScalar();
return Convert.ToInt64(ProjectId);
}
}
public static void PostBuild(BuildData Build)
{
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
Connection.Open();
long ProjectId = TryInsertAndGetProject(Connection, Build.Project);
string InsertWithMetadata =
"INSERT INTO ugs_db.Badges (ChangeNumber, BuildType, Result, URL, ArchivePath, ProjectId, Metadata) VALUES (@ChangeNumber, @BuildType, @Result, @URL, @ArchivePath, @ProjectId, @Metadata)";
string InsertWithoutMetadata =
"INSERT INTO ugs_db.Badges (ChangeNumber, BuildType, Result, URL, ArchivePath, ProjectId) VALUES (@ChangeNumber, @BuildType, @Result, @URL, @ArchivePath, @ProjectId)";
using (MySqlCommand Command = new MySqlCommand(StoreBuildMetadata ? InsertWithMetadata : InsertWithoutMetadata, Connection))
{
Command.Parameters.AddWithValue("@ChangeNumber", Build.ChangeNumber);
Command.Parameters.AddWithValue("@BuildType", Build.BuildType);
Command.Parameters.AddWithValue("@Result", Build.Result);
Command.Parameters.AddWithValue("@URL", Build.Url);
Command.Parameters.AddWithValue("@ArchivePath", Build.ArchivePath);
Command.Parameters.AddWithValue("@ProjectId", ProjectId);
if (StoreBuildMetadata)
{
Command.Parameters.AddWithValue("@Metadata", JsonConvert.SerializeObject(Build.Metadata ?? new BuildMetadata()));
}
Command.ExecuteNonQuery();
}
}
}
public static void PostEvent(EventData Event)
{
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
Connection.Open();
long ProjectId = TryInsertAndGetProject(Connection, Event.Project);
using (MySqlCommand Command = new MySqlCommand("INSERT INTO ugs_db.UserVotes (Changelist, UserName, Verdict, Project, ProjectId) VALUES (@Changelist, @UserName, @Verdict, @Project, @ProjectId)", Connection))
{
Command.Parameters.AddWithValue("@Changelist", Event.Change);
Command.Parameters.AddWithValue("@UserName", Event.UserName.ToString());
Command.Parameters.AddWithValue("@Verdict", Event.Type.ToString());
Command.Parameters.AddWithValue("@Project", Event.Project);
Command.Parameters.AddWithValue("@ProjectId", ProjectId);
Command.ExecuteNonQuery();
}
}
}
public static void PostComment(CommentData Comment)
{
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
Connection.Open();
long ProjectId = TryInsertAndGetProject(Connection, Comment.Project);
using (MySqlCommand Command = new MySqlCommand("INSERT INTO ugs_db.Comments (ChangeNumber, UserName, Text, Project, ProjectId) VALUES (@ChangeNumber, @UserName, @Text, @Project, @ProjectId)", Connection))
{
Command.Parameters.AddWithValue("@ChangeNumber", Comment.ChangeNumber);
Command.Parameters.AddWithValue("@UserName", Comment.UserName);
Command.Parameters.AddWithValue("@Text", Comment.Text);
Command.Parameters.AddWithValue("@Project", Comment.Project);
Command.Parameters.AddWithValue("@ProjectId", ProjectId);
Command.ExecuteNonQuery();
}
}
}
public static void PostTelemetryData(TelemetryTimingData Data, string Version, string IpAddress)
{
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
Connection.Open();
long ProjectId = TryInsertAndGetProject(Connection, Data.Project);
using (MySqlCommand Command = new MySqlCommand("INSERT INTO ugs_db.Telemetry_v2 (Action, Result, UserName, Project, Timestamp, Duration, Version, IpAddress, ProjectId) VALUES (@Action, @Result, @UserName, @Project, @Timestamp, @Duration, @Version, @IpAddress, @ProjectId)", Connection))
{
Command.Parameters.AddWithValue("@Action", Data.Action);
Command.Parameters.AddWithValue("@Result", Data.Result);
Command.Parameters.AddWithValue("@UserName", Data.UserName);
Command.Parameters.AddWithValue("@Project", Data.Project);
Command.Parameters.AddWithValue("@Timestamp", Data.Timestamp);
Command.Parameters.AddWithValue("@Duration", Data.Duration);
Command.Parameters.AddWithValue("@Version", Version);
Command.Parameters.AddWithValue("@IPAddress", IpAddress);
Command.Parameters.AddWithValue("@ProjectId", ProjectId);
Command.ExecuteNonQuery();
}
}
}
public static void PostErrorData(TelemetryErrorData Data, string Version, string IpAddress)
{
using (MySqlConnection Connection = new MySqlConnection(ConnectionString))
{
Connection.Open();
long? ProjectId = null;
if(Data.Project != null)
{
ProjectId = TryInsertAndGetProject(Connection, Data.Project);
}
using (MySqlCommand Command = new MySqlCommand("INSERT INTO ugs_db.Errors (Type, Text, UserName, Project, Timestamp, Version, IpAddress, ProjectId) VALUES (@Type, @Text, @UserName, @Project, @Timestamp, @Version, @IpAddress, @ProjectId)", Connection))
{
Command.Parameters.AddWithValue("@Type", Data.Type.ToString());
Command.Parameters.AddWithValue("@Text", Data.Text);
Command.Parameters.AddWithValue("@UserName", Data.UserName);
if (Data.Project == null)
{
Command.Parameters.AddWithValue("@Project", DBNull.Value);
Command.Parameters.AddWithValue("@ProjectId", DBNull.Value);
}
else
{
Command.Parameters.AddWithValue("@Project", Data.Project);
Command.Parameters.AddWithValue("@ProjectId", ProjectId.Value);
}
Command.Parameters.AddWithValue("@Timestamp", Data.Timestamp);
Command.Parameters.AddWithValue("@Version", Version);
Command.Parameters.AddWithValue("@IPAddress", IpAddress);
Command.ExecuteNonQuery();
}
}
}
private static string GetProjectStream(string Project)
{
// Get first two fragments of the p4 path. If it doesn't work, just return back the project.
Regex StreamPattern = new Regex("(\\/\\/[a-zA-Z0-9\\.\\-_]{1,}\\/[a-zA-Z0-9\\.\\-_]{1,})");
Match StreamMatch = StreamPattern.Match(Project);
if(StreamMatch.Success)
{
return StreamMatch.Groups[1].Value;
}
return Project;
}
private static bool MatchesWildcard(string Wildcard, string Project)
{
return Wildcard.EndsWith("...") && Project.StartsWith(Wildcard.Substring(0, Wildcard.Length - 4), StringComparison.InvariantCultureIgnoreCase);
}
private static string NormalizeUserName(string UserName)
{
return UserName.ToUpperInvariant();
}
public static long FindOrAddUserId(string Name)
{
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
return FindOrAddUserId(Name, Connection);
}
}
private static long FindOrAddUserId(string Name, MySqlConnection Connection)
{
if(Name.Length == 0)
{
return -1;
}
string NormalizedName = NormalizeUserName(Name);
using (MySqlCommand Command = new MySqlCommand("SELECT Id FROM ugs_db.Users WHERE Name = @Name", Connection))
{
Command.Parameters.AddWithValue("@Name", NormalizedName);
object UserId = Command.ExecuteScalar();
if(UserId != null)
{
return Convert.ToInt64(UserId);
}
}
using (MySqlCommand Command = new MySqlCommand("INSERT IGNORE INTO ugs_db.Users (Name) VALUES (@Name); SELECT Id FROM ugs_db.Users WHERE Name = @Name", Connection))
{
Command.Parameters.AddWithValue("@Name", NormalizedName);
object UserId = Command.ExecuteScalar();
return Convert.ToInt64(UserId);
}
}
const int IssueSummaryMaxLength = 200;
public static long AddIssue(IssueData Issue)
{
long IssueId;
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
using (MySqlCommand Command = new MySqlCommand("INSERT INTO ugs_db.Issues (Project, Summary, OwnerId, CreatedAt, FixChange) VALUES (@Project, @Summary, @OwnerId, UTC_TIMESTAMP(), 0)", Connection))
{
Command.Parameters.AddWithValue("@Project", Issue.Project);
Command.Parameters.AddWithValue("@Summary", SanitizeText(Issue.Summary, IssueSummaryMaxLength));
if (Issue.Owner != null)
{
Command.Parameters.AddWithValue("OwnerId", FindOrAddUserId(Issue.Owner, Connection));
}
else
{
Command.Parameters.AddWithValue("OwnerId", null);
}
Command.ExecuteNonQuery();
IssueId = Command.LastInsertedId;
}
}
return IssueId;
}
public static IssueData GetIssue(long IssueId)
{
List<IssueData> Issues = GetIssuesInternal(IssueId, null, true, -1);
if(Issues.Count == 0)
{
return null;
}
else
{
return Issues[0];
}
}
public static List<IssueData> GetIssues(bool IncludeResolved, int NumResults)
{
return GetIssuesInternal(-1, null, IncludeResolved, NumResults);
}
public static List<IssueData> GetIssues(string UserName)
{
return GetIssuesInternal(-1, UserName, false, -1);
}
private static List<IssueData> GetIssuesInternal(long IssueId, string UserName, bool IncludeResolved, int NumResults)
{
List<IssueData> Issues = new List<IssueData>();
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
long UserId = -1;
if(UserName != null)
{
UserId = FindOrAddUserId(UserName);
}
StringBuilder CommandBuilder = new StringBuilder();
CommandBuilder.Append("SELECT");
CommandBuilder.Append(" Issues.Id, Issues.CreatedAt, UTC_TIMESTAMP(), Issues.Project, Issues.Summary, OwnerUsers.Name, NominatedByUsers.Name, Issues.AcknowledgedAt, Issues.FixChange, Issues.ResolvedAt");
if(UserName != null)
{
CommandBuilder.Append(", IssueWatchers.UserId");
}
CommandBuilder.Append(" FROM ugs_db.Issues");
CommandBuilder.Append(" LEFT JOIN ugs_db.Users AS OwnerUsers ON OwnerUsers.Id = Issues.OwnerId");
CommandBuilder.Append(" LEFT JOIN ugs_db.Users AS NominatedByUsers ON NominatedByUsers.Id = Issues.NominatedById");
if(UserName != null)
{
CommandBuilder.Append(" LEFT JOIN ugs_db.IssueWatchers ON IssueWatchers.IssueId = Issues.Id AND IssueWatchers.UserId = @UserId");
}
if(IssueId != -1)
{
CommandBuilder.Append(" WHERE Issues.Id = @IssueId");
}
else if(!IncludeResolved)
{
CommandBuilder.Append(" WHERE Issues.ResolvedAt IS NULL");
}
if(NumResults > 0)
{
CommandBuilder.AppendFormat(" ORDER BY Issues.Id DESC LIMIT {0}", NumResults);
}
using (MySqlCommand Command = new MySqlCommand(CommandBuilder.ToString(), Connection))
{
if(IssueId != -1)
{
Command.Parameters.AddWithValue("@IssueId", IssueId);
}
if(UserName != null)
{
Command.Parameters.AddWithValue("@UserId", UserId);
}
using(MySqlDataReader Reader = Command.ExecuteReader())
{
while(Reader.Read())
{
IssueData Issue = new IssueData();
Issue.Id = Reader.GetInt64(0);
Issue.CreatedAt = Reader.GetDateTime(1);
Issue.RetrievedAt = Reader.GetDateTime(2);
Issue.Project = Reader.GetString(3);
Issue.Summary = Reader.GetString(4);
Issue.Owner = Reader.IsDBNull(5)? null : Reader.GetString(5);
Issue.NominatedBy = Reader.IsDBNull(6)? null : Reader.GetString(6);
Issue.AcknowledgedAt = Reader.IsDBNull(7)? (DateTime?)null : Reader.GetDateTime(7);
Issue.FixChange = Reader.GetInt32(8);
Issue.ResolvedAt = Reader.IsDBNull(9)? (DateTime?)null : Reader.GetDateTime(9);
if(UserName != null)
{
Issue.bNotify = !Reader.IsDBNull(10);
}
Issues.Add(Issue);
}
}
}
}
return Issues;
}
public static void UpdateIssue(long IssueId, IssueUpdateData Issue)
{
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
using (MySqlCommand Command = Connection.CreateCommand())
{
List<string> Columns = new List<string>();
List<string> Values = new List<string>();
if(Issue.Summary != null)
{
Columns.Add("Summary");
Values.Add("@Summary");
Command.Parameters.AddWithValue("@Summary", SanitizeText(Issue.Summary, IssueSummaryMaxLength));
}
if (Issue.Owner != null)
{
Columns.Add("OwnerId");
Values.Add("@OwnerId");
Command.Parameters.AddWithValue("OwnerId", FindOrAddUserId(Issue.Owner, Connection));
}
if(Issue.NominatedBy != null)
{
Columns.Add("NominatedById");
Values.Add("@NominatedById");
Command.Parameters.AddWithValue("NominatedById", FindOrAddUserId(Issue.NominatedBy, Connection));
}
if(Issue.Acknowledged.HasValue)
{
Columns.Add("AcknowledgedAt");
Values.Add(Issue.Acknowledged.Value? "UTC_TIMESTAMP()" : "NULL");
}
if(Issue.FixChange.HasValue)
{
Columns.Add("FixChange");
Values.Add("@FixChange");
Command.Parameters.AddWithValue("FixChange", Issue.FixChange.Value);
}
if(Issue.Resolved.HasValue)
{
Columns.Add("ResolvedAt");
Values.Add(Issue.Resolved.Value? "UTC_TIMESTAMP()" : "NULL");
}
StringBuilder CommandText = new StringBuilder("UPDATE ugs_db.Issues SET ");
for(int idx = 0; idx < Columns.Count; idx++)
{
CommandText.Append(String.Format("{0}={1}", Columns[idx], Values[idx]));
if(idx != Columns.Count - 1)
{
CommandText.Append(",");
}
}
CommandText.Append(" WHERE Id = @IssueId");
Command.CommandText = CommandText.ToString();
Command.Parameters.AddWithValue("@IssueId", IssueId);
Command.ExecuteNonQuery();
}
}
}
public static string SanitizeText(string Text, int Length)
{
if(Text.Length > Length)
{
int NewlineIdx = Text.LastIndexOf('\n', Length);
if(NewlineIdx == -1)
{
Text = Text.Substring(0, Length - 3).TrimEnd() + "...";
}
else
{
Text = Text.Substring(0, NewlineIdx + 1) + "...";
}
}
return Text;
}
public static void DeleteIssue(long IssueId)
{
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
using (MySqlTransaction Transaction = Connection.BeginTransaction())
{
using (MySqlCommand Command = Connection.CreateCommand())
{
Command.Transaction = Transaction;
Command.CommandText = "DELETE FROM ugs_db.IssueWatchers WHERE IssueId = @IssueId";
Command.Parameters.AddWithValue("@IssueId", IssueId);
Command.ExecuteNonQuery();
Command.CommandText = "DELETE FROM ugs_db.IssueBuilds WHERE IssueId = @IssueId";
Command.Parameters.AddWithValue("@IssueId", IssueId);
Command.ExecuteNonQuery();
Command.CommandText = "DELETE FROM ugs_db.Issues WHERE Id = @IssueId";
Command.Parameters.AddWithValue("@IssueId", IssueId);
Command.ExecuteNonQuery();
Transaction.Commit();
}
}
}
}
public static void AddDiagnostic(long IssueId, IssueDiagnosticData Diagnostic)
{
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
using (MySqlCommand Command = new MySqlCommand("INSERT INTO ugs_db.IssueDiagnostics (IssueId, BuildId, Message, Url) VALUES (@IssueId, @BuildId, @Message, @Url)", Connection))
{
Command.Parameters.AddWithValue("@IssueId", IssueId);
Command.Parameters.AddWithValue("@BuildId", Diagnostic.BuildId);
Command.Parameters.AddWithValue("@Message", SanitizeText(Diagnostic.Message, 1000));
Command.Parameters.AddWithValue("@Url", Diagnostic.Url);
Command.ExecuteNonQuery();
}
}
}
public static List<IssueDiagnosticData> GetDiagnostics(long IssueId)
{
List<IssueDiagnosticData> Diagnostics = new List<IssueDiagnosticData>();
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
StringBuilder CommandBuilder = new StringBuilder();
CommandBuilder.Append("SELECT BuildId, Message, Url FROM ugs_db.IssueDiagnostics");
CommandBuilder.Append(" WHERE IssueDiagnostics.IssueId = @IssueId");
using (MySqlCommand Command = new MySqlCommand(CommandBuilder.ToString(), Connection))
{
Command.Parameters.AddWithValue("@IssueId", IssueId);
using (MySqlDataReader Reader = Command.ExecuteReader())
{
while (Reader.Read())
{
IssueDiagnosticData Diagnostic = new IssueDiagnosticData();
Diagnostic.BuildId = Reader.IsDBNull(0)? (long?)null : (long?)Reader.GetInt64(0);
Diagnostic.Message = Reader.GetString(1);
Diagnostic.Url = Reader.GetString(2);
Diagnostics.Add(Diagnostic);
}
}
}
}
return Diagnostics;
}
public static void AddWatcher(long IssueId, string UserName)
{
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
long UserId = FindOrAddUserId(UserName, Connection);
using(MySqlCommand Command = new MySqlCommand("INSERT IGNORE INTO ugs_db.IssueWatchers (IssueId, UserId) VALUES (@IssueId, @UserId)", Connection))
{
Command.Parameters.AddWithValue("@IssueId", IssueId);
Command.Parameters.AddWithValue("@UserId", UserId);
Command.ExecuteNonQuery();
}
}
}
public static List<string> GetWatchers(long IssueId)
{
List<string> Watchers = new List<string>();
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
StringBuilder CommandBuilder = new StringBuilder();
CommandBuilder.Append("SELECT Users.Name FROM ugs_db.IssueWatchers");
CommandBuilder.Append(" LEFT JOIN ugs_db.Users ON IssueWatchers.UserId = Users.Id");
CommandBuilder.Append(" WHERE IssueWatchers.IssueId = @IssueId");
using(MySqlCommand Command = new MySqlCommand(CommandBuilder.ToString(), Connection))
{
Command.Parameters.AddWithValue("@IssueId", IssueId);
using(MySqlDataReader Reader = Command.ExecuteReader())
{
while(Reader.Read())
{
Watchers.Add(Reader.GetString(0));
}
}
}
}
return Watchers;
}
public static void RemoveWatcher(long IssueId, string UserName)
{
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
long UserId = FindOrAddUserId(UserName, Connection);
using(MySqlCommand Command = new MySqlCommand("DELETE FROM ugs_db.IssueWatchers WHERE IssueId = @IssueId AND UserId = @UserId", Connection))
{
Command.Parameters.AddWithValue("@IssueId", IssueId);
Command.Parameters.AddWithValue("@UserId", UserId);
Command.ExecuteNonQuery();
}
}
}
public static long AddBuild(long IssueId, IssueBuildData Build)
{
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
using (MySqlCommand Command = new MySqlCommand("INSERT INTO ugs_db.IssueBuilds (IssueId, Stream, `Change`, JobName, JobUrl, JobStepName, JobStepUrl, ErrorUrl, Outcome) VALUES (@IssueId, @Stream, @Change, @JobName, @JobUrl, @JobStepName, @JobStepUrl, @ErrorUrl, @Outcome)", Connection))
{
Command.Parameters.AddWithValue("@IssueId", IssueId);
Command.Parameters.AddWithValue("@Stream", Build.Stream);
Command.Parameters.AddWithValue("@Change", Build.Change);
Command.Parameters.AddWithValue("@JobName", Build.JobName);
Command.Parameters.AddWithValue("@JobUrl", Build.JobUrl);
Command.Parameters.AddWithValue("@JobStepName", Build.JobStepName);
Command.Parameters.AddWithValue("@JobStepUrl", Build.JobStepUrl);
Command.Parameters.AddWithValue("@ErrorUrl", Build.ErrorUrl);
Command.Parameters.AddWithValue("@Outcome", Build.Outcome);
Command.ExecuteNonQuery();
return Command.LastInsertedId;
}
}
}
public static List<IssueBuildData> GetBuilds(long IssueId)
{
List<IssueBuildData> Builds = new List<IssueBuildData>();
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
using(MySqlCommand Command = new MySqlCommand("SELECT IssueBuilds.Id, IssueBuilds.Stream, IssueBuilds.Change, IssueBuilds.JobName, IssueBuilds.JobUrl, IssueBuilds.JobStepName, IssueBuilds.JobStepUrl, IssueBuilds.ErrorUrl, IssueBuilds.Outcome FROM ugs_db.IssueBuilds WHERE IssueBuilds.IssueId = @IssueId", Connection))
{
Command.Parameters.AddWithValue("@IssueId", IssueId);
using(MySqlDataReader Reader = Command.ExecuteReader())
{
while(Reader.Read())
{
long Id = Reader.GetInt64(0);
string Stream = Reader.GetString(1);
int Change = Reader.GetInt32(2);
string JobName = Reader.GetString(3);
string JobUrl = Reader.GetString(4);
string JobStepName = Reader.GetString(5);
string JobStepUrl = Reader.GetString(6);
string ErrorUrl = Reader.IsDBNull(7)? null : Reader.GetString(7);
int Outcome = Reader.GetInt32(8);
Builds.Add(new IssueBuildData { Id = Id, Stream = Stream, Change = Change, JobName = JobName, JobUrl = JobUrl, JobStepName = JobStepName, JobStepUrl = JobStepUrl, ErrorUrl = ErrorUrl, Outcome = Outcome });
}
}
}
}
return Builds;
}
public static IssueBuildData GetBuild(long BuildId)
{
IssueBuildData Build = null;
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
using(MySqlCommand Command = new MySqlCommand("SELECT IssueBuilds.Id, IssueBuilds.Stream, IssueBuilds.Change, IssueBuilds.JobName, IssueBuilds.JobUrl, IssueBuilds.JobStepName, IssueBuilds.JobStepUrl, IssueBuilds.ErrorUrl, IssueBuilds.Outcome FROM ugs_db.IssueBuilds WHERE IssueBuilds.Id = @BuildId", Connection))
{
Command.Parameters.AddWithValue("@BuildId", BuildId);
using(MySqlDataReader Reader = Command.ExecuteReader())
{
while(Reader.Read())
{
long Id = Reader.GetInt64(0);
string Stream = Reader.GetString(1);
int Change = Reader.GetInt32(2);
string JobName = Reader.GetString(3);
string JobUrl = Reader.GetString(4);
string JobStepName = Reader.GetString(5);
string JobStepUrl = Reader.GetString(6);
string ErrorUrl = Reader.GetString(7);
int Outcome = Reader.GetInt32(8);
Build = new IssueBuildData { Id = Id, Stream = Stream, Change = Change, JobName = JobName, JobUrl = JobUrl, JobStepName = JobStepName, JobStepUrl = JobStepUrl, ErrorUrl = ErrorUrl, Outcome = Outcome };
}
}
}
}
return Build;
}
public static void UpdateBuild(long BuildId, int Outcome)
{
using (MySqlConnection Connection = new MySqlConnection(SqlConnector.ConnectionString))
{
Connection.Open();
using (MySqlCommand Command = new MySqlCommand("UPDATE ugs_db.IssueBuilds SET (Outcome) = (@Outcome) WHERE Id = @BuildId", Connection))
{
Command.Parameters.AddWithValue("@BuildId", BuildId);
Command.Parameters.AddWithValue("@Outcome", Outcome);
Command.ExecuteNonQuery();
}
}
}
}
}