Skip to content

Commit 7456fa8

Browse files
[#99] Link layout entries to analyzed serialized files; document layout-only NULLs
1 parent c8a9e47 commit 7456fa8

5 files changed

Lines changed: 538 additions & 4 deletions

File tree

Analyzer/SQLite/Parsers/ContentLayoutParser.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,14 @@ private static void Fail(string message)
7272
throw new Exception(message);
7373
}
7474

75+
// Called after all files are processed, so the analyzed .cf files all have their
76+
// serialized_files rows and the layout entries can be linked to them.
7577
public void FinalizeDatabase()
7678
{
79+
if (m_ImportedLayout != null)
80+
{
81+
m_Writer.LinkSerializedFiles();
82+
}
7783
}
7884

7985
public void Dispose()

Analyzer/SQLite/Writers/ContentLayoutSQLWriter.cs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using Microsoft.Data.Sqlite;
45
using UnityDataTools.Analyzer.SQLite.Commands.ContentLayout;
56
using UnityDataTools.Models;
@@ -27,6 +28,9 @@ internal class ContentLayoutSQLWriter : IDisposable
2728
private bool m_Initialized;
2829
private SqliteConnection m_Database;
2930

31+
// The layout files that can have a .cf file on disk, kept for LinkSerializedFiles.
32+
private List<(int Index, string ContentHash)> m_ImportedFiles = new();
33+
3034
public ContentLayoutSQLWriter(SqliteConnection database)
3135
{
3236
m_Database = database;
@@ -75,10 +79,16 @@ public void WriteContentLayout(string filename, ContentLayout layout)
7579
m_AddSerializedFile.SetValue("is_builtin", file.IsBuiltIn ? 1 : 0);
7680
m_AddSerializedFile.SetValue("content_hash",
7781
string.IsNullOrEmpty(file.ContentHash) ? null : file.ContentHash);
78-
// Filled in when the analyzed input also contains the build content.
82+
// Filled in by LinkSerializedFiles when the analyzed input also contains the
83+
// build content.
7984
m_AddSerializedFile.SetValue("serialized_file", null);
8085
m_AddSerializedFile.ExecuteNonQuery();
8186

87+
if (!string.IsNullOrEmpty(file.ContentHash))
88+
{
89+
m_ImportedFiles.Add((file.Index, file.ContentHash));
90+
}
91+
8292
// Empty arrays can be omitted from the json, leaving the fields null.
8393
foreach (var assetPath in file.SourceAssets ?? [])
8494
{
@@ -166,6 +176,52 @@ public void WriteContentLayout(string filename, ContentLayout layout)
166176
ExecuteDDL(Properties.Resources.ContentLayoutIndexes);
167177
}
168178

179+
// Fills in the serialized_file column, linking each layout entry to the serialized_files
180+
// row of its analyzed .cf file. Called after all files are processed; entries whose file
181+
// was not part of the analyzed input (e.g. a layout-only analyze) stay NULL. The match is
182+
// on the file name (the content hash), ignoring any directory part that the analyze pass
183+
// recorded in serialized_files.name.
184+
public void LinkSerializedFiles()
185+
{
186+
var fileNameToId = new Dictionary<string, int>();
187+
using (var select = m_Database.CreateCommand())
188+
{
189+
select.CommandText = "SELECT id, name FROM serialized_files";
190+
using var reader = select.ExecuteReader();
191+
while (reader.Read())
192+
{
193+
fileNameToId[Path.GetFileName(reader.GetString(1)).ToLowerInvariant()] = reader.GetInt32(0);
194+
}
195+
}
196+
197+
using var transaction = m_Database.BeginTransaction();
198+
using var update = m_Database.CreateCommand();
199+
update.Transaction = transaction;
200+
update.CommandText = "UPDATE content_layout_serialized_files SET serialized_file = @id WHERE file_index = @file_index";
201+
update.Parameters.Add("@id", SqliteType.Integer);
202+
update.Parameters.Add("@file_index", SqliteType.Integer);
203+
204+
try
205+
{
206+
foreach (var file in m_ImportedFiles)
207+
{
208+
if (fileNameToId.TryGetValue(file.ContentHash + ".cf", out var id))
209+
{
210+
update.Parameters["@id"].Value = id;
211+
update.Parameters["@file_index"].Value = file.Index;
212+
update.ExecuteNonQuery();
213+
}
214+
}
215+
216+
transaction.Commit();
217+
}
218+
catch (Exception)
219+
{
220+
transaction.Rollback();
221+
throw;
222+
}
223+
}
224+
169225
private void SetTransaction(SqliteTransaction transaction)
170226
{
171227
m_AddContentLayout.SetTransaction(transaction);

Documentation/analyzer.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,8 @@ top-level `RootAssets` list folded into the `is_root_asset` flag:
295295
* `content_layout_serialized_files`: one row per serialized file (.cf Content File) of the build,
296296
keyed by `file_index` (the json array index). Records the symbolic `cfid`, `is_builtin`, and the
297297
`content_hash` that gives the filename (`content_hash || '.cf'`). The `serialized_file` column
298-
references the core `serialized_files` table when the build content is part of the analyzed
299-
input, connecting the layout to the analyzed objects.
298+
references the core `serialized_files` table, connecting the layout to the analyzed objects
299+
(see [Analyzing the layout with or without the build content](#analyzing-the-layout-with-or-without-the-build-content)).
300300
* `content_layout_source_assets`: the source assets included in each serialized file.
301301
* `content_layout_serialized_file_dependencies`: file-to-file dependency edges. The 1-based
302302
`position` column preserves the json array order, which is how PPtr `m_FileID` values resolve
@@ -323,6 +323,23 @@ filenames), `content_layout_loadable_objects_view` (loadables resolved to their
323323
SELECT * FROM content_layout_source_assets_view WHERE asset_path = 'Assets/Textures/GreenStatic.png';
324324
```
325325

326+
### Analyzing the layout with or without the build content
327+
328+
A `ContentLayout.json` can be analyzed on its own — useful for running SQL queries against a large
329+
layout — or together with the build output it describes. The layout tables themselves are
330+
identical in both cases; what differs is the connection to the core tables:
331+
332+
* When the build content is part of the analyzed input, each `content_layout_serialized_files`
333+
row is linked to its analyzed file through the `serialized_file` column, and the views resolve
334+
across that link (e.g. `content_layout_loadable_objects_view` shows the object, type, name and
335+
size of each loadable).
336+
* In a layout-only analyze there is nothing to link to, so expect NULL in
337+
`content_layout_serialized_files.serialized_file`, in the `archive` column of
338+
`content_layout_serialized_files_view`, and in the `object`, `type`, `name` and `size` columns
339+
of `content_layout_loadable_objects_view`. Built-in entries (`is_builtin = 1`) additionally
340+
always have a NULL `content_hash` and `serialized_file` - they are not files produced by the
341+
build.
342+
326343
## BuildReport
327344

328345
See [BuildReport.md](buildreport.md) for details of the tables and views related to analyzing BuildReport files.

0 commit comments

Comments
 (0)