Skip to content

Commit

Permalink
#81, #82, 데이베이스 검증 작업 구현 코드 가독성 개선
Browse files Browse the repository at this point in the history
[*] DbCheckJob을 여러 개의 하위 클래스로 모두 쪼갬
[*] Win32InputSimulator에서 공유 코드를 NativeInputSimulator로 빼내고 실질적인 구현 코드들만 남기기. (추후 ArduinoInputSimulator, InceptionInputSimulator 구현 시 편의 위해)    -> 작업 중
[+] 라이브러리 단에서 정규 표현식을 이용한 단어 검색 구현 완료. 그러나 아직 GUI쪽 코드를 작성하지 않았음.
  ㄴ SQLite같은 경우는 자체적으로 정규 표현식 검색을 지원하지 않기에, sqlean 라이브러리를 임베드하여 구현함.
[+] 추가 데이터베이스 검증 작업 #82 구현 완료
  • Loading branch information
hsheric0210 committed Dec 2, 2023
1 parent eb289d8 commit 2379d46
Show file tree
Hide file tree
Showing 44 changed files with 1,002 additions and 461 deletions.
19 changes: 19 additions & 0 deletions AutoKkutuLib.Sqlite/AutoKkutuLib.Sqlite.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,23 @@
<ProjectReference Include="..\AutoKkutuLib\AutoKkutuLib.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Resources\" />
</ItemGroup>

<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions AutoKkutuLib.Sqlite/Database/Sqlite/SqliteDbConnection.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using AutoKkutuLib.Database.Sql;
using AutoKkutuLib.Database.Sql.Query;
using AutoKkutuLib.Postgres.Database.PostgreSql.Query;
using AutoKkutuLib.Sqlite.Properties;
using Dapper;
using Microsoft.Data.Sqlite;
using System.Text;
using System.Text.RegularExpressions;

namespace AutoKkutuLib.Database.Sqlite;

Expand All @@ -12,6 +14,7 @@ public sealed class SqliteDbConnection : DbConnectionBase
private QueryFactory query = null!;
public override QueryFactory Query => query;
public override string DbType => "SQLite";
private const string regexpFileName = "regexp.dll";

private SqliteDbConnection(SqliteConnection connection) : base(connection) { }

Expand Down Expand Up @@ -42,11 +45,15 @@ public override string GetWordListColumnOptions()
{
try
{
if (!File.Exists(regexpFileName))
File.WriteAllBytes(regexpFileName, Resources.sqlean_regexp_library);

// Open the connection
var nativeConnection = SqliteDatabaseHelper.OpenConnection(connectionString);
var connection = new SqliteDbConnection(nativeConnection);
connection.query = new SqliteQueryFactory(connection);

nativeConnection.LoadExtension(regexpFileName); // 'regexp_like' 명령을 사용하기 위해서 필수적인 라이브러리
nativeConnection.CreateFunction<int, int, int, int, int, int, int>(connection.GetWordPriorityFuncName(), WordPriorityFunc, true);
nativeConnection.CreateFunction<string, int, string, int, int, int, int, int, int, int, int, int>(connection.GetMissionWordPriorityFuncName(), MissionWordPriorityFunc, true);

Expand Down
73 changes: 73 additions & 0 deletions AutoKkutuLib.Sqlite/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

124 changes: 124 additions & 0 deletions AutoKkutuLib.Sqlite/Properties/Resources.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="sqlean_regexp_library" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\regexp.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>
Binary file added AutoKkutuLib.Sqlite/Resources/regexp.dll
Binary file not shown.
10 changes: 10 additions & 0 deletions AutoKkutuLib.Sqlite/Resources/regexp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
regexp: Regular Expressions in SQLite
https://github.com/nalgeon/sqlean

regexp.dll is SQLite Regular Expressions extension library included in sqlean 0.21.8
You can download it from https://github.com/nalgeon/sqlean/releases
For more information, see: https://github.com/nalgeon/sqlean/blob/main/docs/regexp.md

regexp.dll은 sqlean 0.21.8에 포함된 SQLite 정규 표현식 확장 라이브러리입니다.
이는 https://github.com/nalgeon/sqlean/releases 에서 다운로드할 수 있습니다.
더 자세한 정보는 https://github.com/nalgeon/sqlean/blob/main/docs/regexp.md 을 참조하세요.
62 changes: 62 additions & 0 deletions AutoKkutuLib/Database/Jobs/DbCheck/DbCheckJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using AutoKkutuLib.Browser;
using AutoKkutuLib.Database.Jobs.DbCheck.WordTableChecks;
using AutoKkutuLib.Database.Path;
using Dapper;

namespace AutoKkutuLib.Database.Jobs.DbCheck;

public class DbCheckJob
{
private readonly NodeManager nodeManager;
private DbConnectionBase Db => nodeManager.DbConnection;

public DbCheckJob(NodeManager nodeManager) => this.nodeManager = nodeManager;

#region Main check process
/// <summary>
/// 데이터베이스의 무결성을 검증하고, 문제를 발견하면 수정합니다.
/// </summary>
/// <param name="UseOnlineDB">온라인 검사(끄투 사전을 통한 검사)를 진행하는지의 여부</param>
public void CheckDB(bool UseOnlineDB, BrowserBase? browser)
{
// FIXME: Move to caller
//if (UseOnlineDB && string.IsNullOrWhiteSpace(JSEvaluator.EvaluateJS("document.getElementById('dict-output').style")))
// MessageBox.Show("사전 창을 감지하지 못했습니다.\n끄투 사전 창을 여십시오.", "데이터베이스 관리자", MessageBoxButton.OK, MessageBoxImage.Warning);
// return;

DatabaseEvents.TriggerDatabaseIntegrityCheckStart();

Task.Run(() =>
{
var jobs = new List<DbCheckSubtaskBase>()
{
new DeduplicateWordTableJob(Db),
new RefreshNodeListJob(nodeManager),
new WordTableCheck(nodeManager),
new InvalidEndNodeCheck(Db),
new RunVacuumJob(Db)
};
try
{
var affected = 0;
var totalElementCount = Db.ExecuteScalar<int>($"SELECT COUNT(*) FROM {DatabaseConstants.WordTableName}");
LibLogger.Info<DbCheckJob>("Database has Total {0} elements.", totalElementCount);

foreach (var job in jobs)
affected += job.Execute();

LibLogger.Info<DbCheckJob>("Total {0} problems are solved.", affected);

new DataBaseIntegrityCheckDoneEventArgs($"{affected} 개의 문제점 수정됨").TriggerDatabaseIntegrityCheckDone();
}
catch (Exception ex)
{
LibLogger.Error<DbCheckJob>(ex, "Exception while checking database");
}

foreach (var job in jobs)
job.BriefResult();
});
}
#endregion
}
30 changes: 30 additions & 0 deletions AutoKkutuLib/Database/Jobs/DbCheck/DbCheckSubtaskBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Diagnostics;

namespace AutoKkutuLib.Database.Jobs.DbCheck;
internal abstract class DbCheckSubtaskBase
{
protected DbConnectionBase Db { get; }
protected string CheckName { get; }
protected DbCheckSubtaskBase(DbConnectionBase db, string checkName)
{
Db = db;
CheckName = checkName;
}

public int Execute()
{
LibLogger.Info(CheckName, "Starting: {0}", CheckName);

var watch = new Stopwatch();
watch.Start();
var count = RunCore();
watch.Stop();

LibLogger.Info(CheckName, "Finished: {0} (Took {1}ms)", CheckName, watch.ElapsedMilliseconds);

return count;
}

protected abstract int RunCore();
public abstract void BriefResult();
}
26 changes: 26 additions & 0 deletions AutoKkutuLib/Database/Jobs/DbCheck/DeduplicateWordTableJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace AutoKkutuLib.Database.Jobs.DbCheck;
internal class DeduplicateWordTableJob : DbCheckSubtaskBase
{
private int removed;

public DeduplicateWordTableJob(DbConnectionBase db) : base(db, "Deduplicate Word Table")
{
}

protected override int RunCore()
{
try
{
removed = Db.Query.Deduplicate().Execute();
LibLogger.Info(CheckName, "Removed {0} duplicate word entries.", removed);
}
catch (Exception ex)
{
LibLogger.Error(CheckName, ex, "Word table deduplication failed");
}

return removed;
}

public override void BriefResult() => LibLogger.Info(CheckName, "Removed {0} duplicate word entries.", removed);
}
Loading

0 comments on commit 2379d46

Please sign in to comment.