4 using System.Collections.Generic;
6 using System.Data.SQLite;
9 using System.Threading.Tasks;
11 using SiliconStudio.Core.Extensions;
13 namespace SiliconStudio.BuildEngine
18 public const string DefaultDatabaseFilename =
"Metadata.db";
20 private readonly Dictionary<string, long> objectUrlIds =
new Dictionary<string, long>();
21 private readonly Dictionary<MetadataKey, long> keyIds =
new Dictionary<MetadataKey, long>();
23 private SQLiteConnection connection;
25 private readonly
object lockObject =
new object();
27 public bool Open(
string path,
bool create)
29 if (!
File.Exists(path))
31 return create && Create(path);
34 string connectionString = String.Format(
"Data Source={0}", path);
38 if (connection != null)
39 throw new InvalidOperationException(
"Connection to the metadata database already opened");
41 connection =
new SQLiteConnection(connectionString);
50 return Task.Run(() => Open(path, create));
62 SQLiteConnection.CreateFile(path);
69 if (!Open(path,
false))
72 const string Query =
@"
74 `KeyId` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
75 `TypeId` INTEGER NOT NULL,
76 `Name` char(255) NOT NULL
78 CREATE TABLE `ObjectUrls` (
79 `ObjectUrlId` INTEGER NOT NULL PRIMARY KEY,
80 `Location` char(255) NOT NULL
82 CREATE TABLE `Metadata` (
83 `ObjectUrlId` INTEGER NOT NULL,
84 `KeyId` INTEGER NOT NULL,
86 PRIMARY KEY (`ObjectUrlId`, `KeyId`),
87 FOREIGN KEY(ObjectUrlId) REFERENCES ObjectUrls(ObjectUrlId),
88 FOREIGN KEY(KeyId) REFERENCES Keys(KeyId)
92 ExecuteNonQuery(Query);
98 return Task.Run(() => Create(path));
106 if (connection != null)
115 return Task.Run(() => Close());
120 var keysToRemove =
new List<MetadataKey>(
keyIds.Keys);
121 const string Query =
@"SELECT * FROM `Keys`";
123 DataTable dataTable = ExecuteReader(Query);
124 foreach (DataRow row
in dataTable.Rows)
126 var typeId = (int)(
long)row[
"TypeId"];
127 var keyId = (long)row[
"KeyId"];
132 keysToRemove.Remove(key);
136 foreach (var key
in keysToRemove)
140 return keyIds.Keys.ToArray();
145 return Task.Run(() => FetchAllKeys());
150 var urlsToRemove =
new List<string>();
151 const string Query =
@"SELECT * FROM `ObjectUrls`";
152 DataTable dataTable = ExecuteReader(Query);
153 foreach (DataRow row
in dataTable.Rows)
155 var url = (string)row[
"Location"];
156 var urlId = (long)row[
"ObjectUrlId"];
157 objectUrlIds[url] = urlId;
158 urlsToRemove.SwapRemove(url);
161 foreach (var url
in urlsToRemove)
163 objectUrlIds.Remove(url);
165 return objectUrlIds.Keys.ToArray();
170 return Task.Run(() => FetchAllObjectUrls());
175 string query = String.Format(
@"SELECT * FROM `Metadata` INNER JOIN ObjectUrls ON `ObjectUrls`.`ObjectUrlId` = `Metadata`.`ObjectUrlId` INNER JOIN Keys ON `Keys`.`KeyId` = `Metadata`.`KeyId` WHERE `ObjectUrls`.`Location` = '{0}'", FormatUrl(objectUrl));
176 return ParseResult(ExecuteReader(query));
181 return Task.Run(() => Fetch(objectUrl));
186 string query = String.Format(
@"SELECT * FROM `Metadata` INNER JOIN ObjectUrls ON `ObjectUrls`.`ObjectUrlId` = `Metadata`.`ObjectUrlId` INNER JOIN Keys ON `Keys`.`KeyId` = `Metadata`.`KeyId` WHERE `Keys`.`Name` = '{0}' AND `Keys`.`TypeId` = '{1}'", key.Name, (int)key.
Type);
187 return ParseResult(ExecuteReader(query));
192 return Task.Run(() => Fetch(key));
197 string query = String.Format(
@"SELECT * FROM `Metadata` INNER JOIN ObjectUrls ON `ObjectUrls`.`ObjectUrlId` = `Metadata`.`ObjectUrlId` INNER JOIN Keys ON `Keys`.`KeyId` = `Metadata`.`KeyId` WHERE `ObjectUrls`.`Location` = '{0}' AND `Keys`.`Name` = '{1}' AND `Keys`.`TypeId` = '{2}'", FormatUrl(objectUrl), key.Name, (int)key.
Type);
198 return ParseResult(ExecuteReader(query)).SingleOrDefault();
203 return Task.Run(() => Fetch(objectUrl, key));
218 string query = String.Format(
@"SELECT * FROM `Metadata` INNER JOIN ObjectUrls ON `ObjectUrls`.`ObjectUrlId` = `Metadata`.`ObjectUrlId` INNER JOIN Keys ON `Keys`.`KeyId` = `Metadata`.`KeyId`");
220 return ParseResult(ExecuteReader(query));
225 return Task.Run(() => FetchAll());
230 var result =
new List<IObjectMetadata>();
231 foreach (DataRow row
in dataTable.Rows)
233 var url = (string)row[
"Location"];
234 var name = (string)row[
"Name"];
237 var keyId = (long)row[
"KeyId"];
238 var objectUrlId = (long)row[
"ObjectUrlId"];
240 objectUrlIds[FormatUrl(url)] = objectUrlId;
241 object value = key.ConvertValue(row[
"Value"].ToString());
249 if (key == null)
throw new ArgumentNullException(
"key");
250 if (!key.
IsValid())
throw new ArgumentException(
@"Key is invalid.",
"key");
251 var typeId = (int)key.
Type;
256 if (connection != null)
259 string query = String.Format(
@"INSERT INTO `Keys` (`TypeId`, `Name`) VALUES ('{0}', '{1}')", typeId, key.Name);
260 return ExecuteNonQuery(query) == 1;
269 if (key == null)
throw new ArgumentNullException(
"key");
270 if (!key.
IsValid())
throw new ArgumentException(
@"Key is invalid.",
"key");
271 var typeId = (int)key.
Type;
276 if (connection != null)
278 string query = String.Format(
@"DELETE FROM `Keys` WHERE `Keys`.`TypeId` = '{0}' AND `Keys`.`Name` = '{1}'", typeId, key.Name);
279 return ExecuteNonQuery(query) == 1;
288 if (data == null)
throw new ArgumentNullException(
"data");
290 long keyId, objectUrlId;
291 keyIds.TryGetValue(data.Key, out keyId);
292 objectUrlIds.TryGetValue(FormatUrl(data.
ObjectUrl), out objectUrlId);
295 if (keyId != 0 && objectUrlId != 0)
297 previousData = Fetch(objectUrlId, keyId);
301 previousData = Fetch(data);
305 if (previousData == null)
307 keyId = keyId == 0 ? GetKeyId(data.
Key) : keyId;
308 objectUrlId = objectUrlId == 0 ? GetOrCreateObjectUrlId(data.
ObjectUrl) : objectUrlId;
311 throw new InvalidOperationException(String.Format(
"The key {0} does not exist in database.", data.
Key));
313 return InsertMetadata(objectUrlId, keyId, data.
Value.ToString());
317 return UpdateMetadata(objectUrlId, keyId, data.
Value.ToString());
322 long keyId, objectUrlId;
323 keyIds.TryGetValue(data.Key, out keyId);
324 objectUrlIds.TryGetValue(FormatUrl(data.
ObjectUrl), out objectUrlId);
325 keyId = keyId == 0 ? GetKeyId(data.
Key) : keyId;
326 objectUrlId = objectUrlId == 0 ? GetOrCreateObjectUrlId(data.
ObjectUrl) : objectUrlId;
328 string query = String.Format(
@"DELETE FROM `Metadata` WHERE `Metadata`.`ObjectUrlId` = '{0}' AND `Metadata`.`KeyId` = '{1}'", objectUrlId, keyId);
329 return ExecuteNonQuery(query) == 1;
342 private static string FormatUrl(
string url)
344 return url.Replace(
"'",
"''").ToLowerInvariant();
347 private long GetKeyId(MetadataKey key)
349 if (key == null)
throw new ArgumentNullException(
"key");
350 string query = String.Format(
@"SELECT `KeyId` FROM `Keys` WHERE `Name` = '{0}' AND `TypeId` = '{1}'", key.Name, (int)key.Type);
351 var result = ExecuteScalar(query);
353 keyIds[key] = (long)result;
354 return result != null ? (long)result : 0;
357 private long GetObjectUrlId(
string url)
359 if (url == null)
throw new ArgumentNullException(
"url");
360 string query = String.Format(
@"SELECT `ObjectUrlId` FROM `ObjectUrls` WHERE `Location` = '{0}'", FormatUrl(url));
361 var result = ExecuteScalar(query);
363 objectUrlIds[FormatUrl(url)] = (long)result;
364 return result != null ? (long)result : 0;
367 private bool CreateObjectUrlId(
string url)
369 if (url == null)
throw new ArgumentNullException(
"url");
370 string query = String.Format(
@"INSERT INTO `ObjectUrls` (`Location`) VALUES ('{0}')", FormatUrl(url));
371 return ExecuteNonQuery(query) == 1;
374 private long GetOrCreateObjectUrlId(
string url)
376 if (url == null)
throw new ArgumentNullException(
"url");
377 long objectUrlId = GetObjectUrlId(url);
378 if (objectUrlId == 0)
380 if (CreateObjectUrlId(url))
381 objectUrlId = GetObjectUrlId(url);
386 private IObjectMetadata Fetch(
long objectUrlId,
long keyId)
388 string query = String.Format(
@"SELECT * FROM `Metadata` INNER JOIN ObjectUrls ON `ObjectUrls`.`ObjectUrlId` = `Metadata`.`ObjectUrlId` INNER JOIN Keys ON `Keys`.`KeyId` = `Metadata`.`KeyId` WHERE `Metadata`.`ObjectUrlId` = '{0}' AND `Metadata`.`KeyId` = '{1}'", objectUrlId, keyId);
389 return ParseResult(ExecuteReader(query)).SingleOrDefault();
392 private bool InsertMetadata(
long objectUrlId,
long keyId,
string value)
394 string query = String.Format(
@"INSERT INTO `Metadata` (`ObjectUrlId`, `KeyId`, `Value`) VALUES ('{0}', '{1}', '{2}')", objectUrlId, keyId, value);
395 return ExecuteNonQuery(query) == 1;
398 private bool UpdateMetadata(
long objectUrlId,
long keyId,
string value)
400 string query = String.Format(
@"UPDATE `Metadata` SET `Value` = '{0}' WHERE `ObjectUrlId` = '{1}' AND `KeyId` = '{2}'", value, objectUrlId, keyId);
401 return ExecuteNonQuery(query) == 1;
404 private int ExecuteNonQuery(
string query)
408 var command =
new SQLiteCommand(connection) { CommandText = query };
409 return command.ExecuteNonQuery();
413 private object ExecuteScalar(
string query)
417 var command =
new SQLiteCommand(connection) { CommandText = query };
418 return command.ExecuteScalar();
422 private DataTable ExecuteReader(
string query)
426 var command =
new SQLiteCommand(connection) { CommandText = query };
427 SQLiteDataReader reader = command.ExecuteReader();
428 var dataTable =
new DataTable();
429 dataTable.Load(reader);