Skip to content

SQLite Asset + CSV support #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 28, 2025
Prev Previous commit
Next Next commit
Add SQLite asset custom editor, scripted importer and build processor
  • Loading branch information
gilzoide committed Jun 28, 2025
commit 095defdfd4b6e16fea379fd8a256a472d64bdecd
16 changes: 16 additions & 0 deletions Editor/Gilzoide.SqliteNet.Editor.asmdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Gilzoide.SqliteNet.Editor",
"rootNamespace": "SQLite.Editor",
"references": [
"GUID:17f96cd3b93974f6493e51a2f25c1241"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
7 changes: 7 additions & 0 deletions Editor/Gilzoide.SqliteNet.Editor.asmdef.meta

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

69 changes: 69 additions & 0 deletions Editor/SQLiteAssetBuildProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#if !UNITY_ANDROID && !UNITY_WEBGL
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;

namespace SQLite.Editor
{
public class SQLiteAssetBuildProcessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
public int callbackOrder => 0;

public void OnPreprocessBuild(BuildReport report)
{
foreach (SQLiteAsset sqliteAsset in GetAffectedAssets())
{
string filePath = $"Assets/StreamingAssets/{sqliteAsset.StreamingAssetsPath}";
string directoryPath = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
File.WriteAllBytes(filePath, sqliteAsset.Bytes);
sqliteAsset.Bytes = Array.Empty<byte>();
}
}

public void OnPostprocessBuild(BuildReport report)
{
foreach (SQLiteAsset sqliteAsset in GetAffectedAssets())
{
string filePath = $"Assets/StreamingAssets/{sqliteAsset.StreamingAssetsPath}";
if (File.Exists(filePath))
{
sqliteAsset.Bytes = File.ReadAllBytes(filePath);
FileUtil.DeleteFileOrDirectory(filePath);
FileUtil.DeleteFileOrDirectory(filePath + ".meta");
DeleteEmptyDirectories(Path.GetDirectoryName(filePath));
}
}
}

private static void DeleteEmptyDirectories(string directory)
{
while (!string.IsNullOrWhiteSpace(directory))
{
if (Directory.EnumerateFileSystemEntries(directory).Any())
{
return;
}
FileUtil.DeleteFileOrDirectory(directory);
FileUtil.DeleteFileOrDirectory(directory + ".meta");
directory = Path.GetDirectoryName(directory);
}
}

private static IEnumerable<SQLiteAsset> GetAffectedAssets()
{
return AssetDatabase.FindAssets($"t:{nameof(SQLiteAsset)}")
.Select(AssetDatabase.GUIDToAssetPath)
.Select(AssetDatabase.LoadAssetAtPath<SQLiteAsset>)
.Where(sqlite => sqlite.UseStreamingAssets);
}
}
}
#endif
11 changes: 11 additions & 0 deletions Editor/SQLiteAssetBuildProcessor.cs.meta

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

72 changes: 72 additions & 0 deletions Editor/SQLiteAssetEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace SQLite.Editor
{
[CustomEditor(typeof(SQLiteAsset))]
[CanEditMultipleObjects]
public class SQLiteAssetEditor : UnityEditor.Editor
{
private class TableInfo
{
public string Name { get; set; }
public string Sql { get; set; }

public void Deconstruct(out string name, out string sql)
{
name = Name;
sql = Sql;
}
}

[SerializeField] private List<string> _expandedTables = new List<string>();

public override void OnInspectorGUI()
{
DrawDefaultInspector();
EditorGUILayout.Space();

using (new EditorGUI.DisabledScope(true))
{
EditorGUILayout.TextField("Database size in bytes", EditorUtility.FormatBytes(((SQLiteAsset) target).Bytes.Length));
}

if (serializedObject.isEditingMultipleObjects)
{
return;
}

EditorGUILayout.Space();

using (new EditorGUI.DisabledScope(true))
using (var db = ((SQLiteAsset) target).CreateConnection())
{
EditorGUILayout.LabelField("Tables", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
foreach ((string name, string sql) in db.Query<TableInfo>("SELECT name, sql FROM SQLite_schema WHERE type = 'table'"))
{
bool previouslyExpanded = _expandedTables.Contains(name);
bool expanded = EditorGUILayout.Foldout(previouslyExpanded, name, true);
if (previouslyExpanded && !expanded)
{
_expandedTables.Remove(name);
}
else if (!previouslyExpanded && expanded)
{
_expandedTables.Add(name);
}

if (expanded)
{
EditorGUILayout.TextField("SQL", sql);
int count = db.ExecuteScalar<int>($"SELECT COUNT(*) FROM {SQLiteConnection.Quote(name)}");
EditorGUILayout.IntField("Row Count", count);
}
EditorGUILayout.Space();
}
EditorGUI.indentLevel--;
}
}
}
}
11 changes: 11 additions & 0 deletions Editor/SQLiteAssetEditor.cs.meta

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

36 changes: 36 additions & 0 deletions Editor/SQLiteAssetImporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.IO;
#if UNITY_2020_2_OR_NEWER
using UnityEditor.AssetImporters;
#else
using UnityEditor.Experimental.AssetImporters;
#endif
using UnityEngine;

namespace SQLite.Editor
{
[ScriptedImporter(0, new[] { "sqlite", "sqlite2", "sqlite3" })]
public class SQLiteAssetImporter : ScriptedImporter
{
[Tooltip("Flags controlling how the SQLite connection should be opened. 'ReadWrite' and 'Create' flags will be ignored, since SQLite assets are read-only.")]
[SerializeField] private SQLiteOpenFlags _openFlags = SQLiteOpenFlags.ReadOnly;

[Tooltip("Whether to store DateTime properties as ticks (true) or strings (false).")]
[SerializeField] private bool _storeDateTimeAsTicks = true;

[Tooltip("Name of the file created for the database inside Streaming Assets folder during builds.\n\n"
+ "If empty, the database bytes will be stored in the asset itself.\n\n"
+ "Loading databases from Streaming Assets is not supported in Android and WebGL platforms.")]
[SerializeField] private string _streamingAssetsPath;

public override void OnImportAsset(AssetImportContext ctx)
{
var asset = ScriptableObject.CreateInstance<SQLiteAsset>();
asset.OpenFlags = _openFlags;
asset.StoreDateTimeAsTicks = _storeDateTimeAsTicks;
asset.Bytes = File.ReadAllBytes(ctx.assetPath);
asset.StreamingAssetsPath = _streamingAssetsPath;
ctx.AddObjectToAsset("sqlite", asset);
ctx.SetMainObject(asset);
}
}
}
11 changes: 11 additions & 0 deletions Editor/SQLiteAssetImporter.cs.meta

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

8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ This package provides the excelent [SQLite-net](https://github.com/praeclarum/sq
+ Enabled modules: [R\*Tree](https://sqlite.org/rtree.html), [Geopoly](https://sqlite.org/geopoly.html), [FTS5](https://sqlite.org/fts5.html), [Built-In Math Functions](https://www.sqlite.org/lang_mathfunc.html)
+ Supports Windows, Linux, macOS, WebGL, Android, iOS, tvOS and visionOS platforms
+ Supports persisting data in WebGL builds by using a [custom VFS backed by Indexed DB](https://github.com/gilzoide/idbvfs).

- [SQLiteAsset](Runtime/SQLiteAsset.cs): read-only SQLite database Unity assets.
+ Files with the extensions ".sqlite", ".sqlite2" and ".sqlite3" will be imported as SQLite database assets.
+ Use the `CreateConnection()` method for connecting to the database provided by the asset.
Make sure to `Dispose()` of any connections you create.
+ SQLite assets may be loaded from Streaming Assets folder or from memory, depending on the value of their "Streaming Assets Path" property.
+ **Warning**: Android and WebGL platforms don't support loading SQLite databases from Streaming Assets and will always load them in memory.
+ `SQLiteConnection.SerializeToAsset` extension method for serializing a database to an instance of `SQLiteAsset`.

## Optional packages
- [SQLite Asset](https://github.com/gilzoide/unity-sqlite-asset): read-only SQLite database assets for Unity with scripted importer for ".sqlite", ".sqlite2" and ".sqlite3" files
Expand Down