mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-23 08:43:50 +02:00
Add database merging feature (#906)
* add initial implementation of dbmerger merges of user tables that have overlapping data is still unimplemented. otherwise, merging of all other tables has been implemented. * add handling for when user feed tables overlap * fix sqlite dll path in dbmerger build * fix accidentally inserting rows with nonunique pks * fix merger not handling missing columns well * fix sort not reassigning pks * sync merger with master * woopsies * w-woops again * reset database version in config to let vrcx generate missing fields * move to net 9 and move build folder to new build folder * remove unneeded build configurations * support avatar time in the merge * csproj pains --------- Co-authored-by: Natsumi <cmcooper123@hotmail.com>
This commit is contained in:
77
DBMerger/SqliteExtensions.cs
Normal file
77
DBMerger/SqliteExtensions.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using SQLite;
|
||||
using SQLitePCL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace DBMerger
|
||||
{
|
||||
// This class is made of mostly hardcoded copies from the sqlite lib.
|
||||
// Normally this would be very bad, but since the library is long since
|
||||
// unmaintained it shouldn't matter
|
||||
internal static class SqliteExtensions
|
||||
{
|
||||
// The prepare method is private, so fetch it here
|
||||
private static readonly MethodInfo _prepareMethod = typeof(SQLiteCommand).GetMethod("Prepare", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
|
||||
/// in the command text for each of the arguments and then executes that command.
|
||||
/// It returns each row as an array of object primitives.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// The fully escaped SQL.
|
||||
/// </param>
|
||||
/// <param name="args">
|
||||
/// Arguments to substitute for the occurences of '?' in the query.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// An enumerable with one object array for each row.
|
||||
/// </returns>
|
||||
public static List<object[]> QueryScalars(this SQLiteConnection conn, string query, params object[] args)
|
||||
{
|
||||
var cmd = conn.CreateCommand(query, args);
|
||||
return cmd.ExecuteQueryScalars(conn).ToList();
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> ExecuteQueryScalars(this SQLiteCommand cmd, SQLiteConnection conn)
|
||||
{
|
||||
if (conn.Trace)
|
||||
{
|
||||
conn.Tracer?.Invoke("Executing Query: " + cmd);
|
||||
}
|
||||
var stmt = _prepareMethod.Invoke(cmd, []) as sqlite3_stmt;
|
||||
try
|
||||
{
|
||||
int columnCount = SQLite3.ColumnCount(stmt);
|
||||
if (SQLite3.ColumnCount(stmt) < 1)
|
||||
{
|
||||
throw new InvalidOperationException("QueryScalars should return at least one column");
|
||||
}
|
||||
while (SQLite3.Step(stmt) == SQLite3.Result.Row)
|
||||
{
|
||||
var row = new object[columnCount];
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
{
|
||||
var colType = SQLite3.ColumnType(stmt, i);
|
||||
row[i] = colType switch
|
||||
{
|
||||
SQLite3.ColType.Integer => SQLite3.ColumnInt(stmt, i),
|
||||
SQLite3.ColType.Float => (float)SQLite3.ColumnDouble(stmt, i),
|
||||
SQLite3.ColType.Text => SQLite3.ColumnString(stmt, i),
|
||||
SQLite3.ColType.Blob => SQLite3.ColumnByteArray(stmt, i),
|
||||
SQLite3.ColType.Null or _ => null
|
||||
};
|
||||
}
|
||||
yield return row;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
SQLite3.Finalize(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user