29 using System.Collections.Generic;
32 using System.IO.Compression;
33 using System.Reflection;
34 using System.Security.Cryptography;
38 using OpenSim.Framework;
42 namespace OpenSim.Data.PGSQL
46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48 protected virtual Assembly Assembly
50 get {
return GetType().Assembly; }
56 private const int DaysBetweenAccessTimeUpdates = 30;
58 private bool m_enableCompression =
false;
60 private string m_connectionString;
61 private object m_dbLock =
new object();
66 private HashAlgorithm hasher =
new SHA256CryptoServiceProvider();
68 #region IPlugin Members
70 public string Version {
get {
return "1.0.0.0"; } }
85 m_log.ErrorFormat(
"[PGSQL XASSETDATA]: ***********************************************************");
86 m_log.ErrorFormat(
"[PGSQL XASSETDATA]: ***********************************************************");
87 m_log.ErrorFormat(
"[PGSQL XASSETDATA]: ***********************************************************");
88 m_log.ErrorFormat(
"[PGSQL XASSETDATA]: THIS PLUGIN IS STRICTLY EXPERIMENTAL.");
89 m_log.ErrorFormat(
"[PGSQL XASSETDATA]: DO NOT USE FOR ANY DATA THAT YOU DO NOT MIND LOSING.");
90 m_log.ErrorFormat(
"[PGSQL XASSETDATA]: DATABASE TABLES CAN CHANGE AT ANY TIME, CAUSING EXISTING DATA TO BE LOST.");
91 m_log.ErrorFormat(
"[PGSQL XASSETDATA]: ***********************************************************");
92 m_log.ErrorFormat(
"[PGSQL XASSETDATA]: ***********************************************************");
93 m_log.ErrorFormat(
"[PGSQL XASSETDATA]: ***********************************************************");
95 m_connectionString = connect;
98 using (NpgsqlConnection dbcon =
new NpgsqlConnection(m_connectionString))
108 throw new NotImplementedException();
118 get {
return "PGSQL XAsset storage engine"; }
123 #region IAssetDataPlugin Members
138 using (NpgsqlConnection dbcon =
new NpgsqlConnection(m_connectionString))
142 using (NpgsqlCommand cmd =
new NpgsqlCommand(
143 @"SELECT name, description, access_time, ""AssetType"", local, temporary, asset_flags, creatorid, data
145 JOIN XAssetsData ON XAssetsMeta.hash = XAssetsData.Hash WHERE id=:ID",
148 cmd.Parameters.Add(m_database.CreateParameter(
"ID", assetID));
152 using (NpgsqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
158 (
string)dbReader[
"name"],
159 Convert.ToSByte(dbReader[
"AssetType"]),
160 dbReader[
"creatorid"].ToString());
162 asset.Data = (byte[])dbReader[
"data"];
163 asset.Description = (string)dbReader[
"description"];
165 string local = dbReader[
"local"].ToString();
166 if (local.Equals(
"1") || local.Equals(
"true", StringComparison.InvariantCultureIgnoreCase))
171 asset.Temporary = Convert.ToBoolean(dbReader[
"temporary"]);
172 asset.Flags = (
AssetFlags)Convert.ToInt32(dbReader[
"asset_flags"]);
174 if (m_enableCompression)
178 MemoryStream outputStream =
new MemoryStream();
179 WebUtil.CopyStream(decompressionStream, outputStream, int.MaxValue);
181 asset.Data = outputStream.ToArray();
189 UpdateAccessTime(asset.
Metadata, (
int)dbReader[
"access_time"]);
195 m_log.Error(string.Format(
"[PGSQL XASSET DATA]: Failure fetching asset {0}", assetID), e);
215 using (NpgsqlConnection dbcon =
new NpgsqlConnection(m_connectionString))
219 using (NpgsqlTransaction transaction = dbcon.BeginTransaction())
221 string assetName = asset.Name;
222 if (asset.
Name.Length > 64)
224 assetName = asset.Name.Substring(0, 64);
226 "[XASSET DB]: Name '{0}' for asset {1} truncated from {2} to {3} characters on add",
227 asset.Name, asset.ID, asset.Name.Length, assetName.Length);
230 string assetDescription = asset.Description;
233 assetDescription = asset.Description.Substring(0, 64);
235 "[XASSET DB]: Description '{0}' for asset {1} truncated from {2} to {3} characters on add",
236 asset.Description, asset.ID, asset.Description.Length, assetDescription.Length);
239 if (m_enableCompression)
241 MemoryStream outputStream =
new MemoryStream();
247 compressionStream.Close();
248 byte[] compressedData = outputStream.ToArray();
249 asset.Data = compressedData;
253 byte[] hash = hasher.ComputeHash(asset.Data);
256 UUID.TryParse(asset.ID, out asset_id);
264 using (NpgsqlCommand cmd =
266 @"insert INTO XAssetsMeta(id, hash, name, description, ""AssetType"", local, temporary, create_time, access_time, asset_flags, creatorid)
267 Select :ID, :Hash, :Name, :Description, :AssetType, :Local, :Temporary, :CreateTime, :AccessTime, :AssetFlags, :CreatorID
268 where not exists( Select id from XAssetsMeta where id = :ID);
271 set id = :ID, hash = :Hash, name = :Name, description = :Description,
272 ""AssetType"" = :AssetType, local = :Local, temporary = :Temporary, create_time = :CreateTime,
273 access_time = :AccessTime, asset_flags = :AssetFlags, creatorid = :CreatorID
280 int now = (int)
Utils.DateTimeToUnixTime(DateTime.UtcNow);
281 cmd.Parameters.Add(m_database.CreateParameter(
"ID", asset_id));
282 cmd.Parameters.Add(m_database.CreateParameter(
"Hash", hash));
283 cmd.Parameters.Add(m_database.CreateParameter(
"Name", assetName));
284 cmd.Parameters.Add(m_database.CreateParameter(
"Description", assetDescription));
285 cmd.Parameters.Add(m_database.CreateParameter(
"AssetType", asset.Type));
286 cmd.Parameters.Add(m_database.CreateParameter(
"Local", asset.Local));
287 cmd.Parameters.Add(m_database.CreateParameter(
"Temporary", asset.Temporary));
288 cmd.Parameters.Add(m_database.CreateParameter(
"CreateTime", now));
289 cmd.Parameters.Add(m_database.CreateParameter(
"AccessTime", now));
290 cmd.Parameters.Add(m_database.CreateParameter(
"CreatorID", asset.Metadata.CreatorID));
291 cmd.Parameters.Add(m_database.CreateParameter(
"AssetFlags", (int)asset.
Flags));
293 cmd.ExecuteNonQuery();
298 m_log.ErrorFormat(
"[ASSET DB]: PGSQL failure creating asset metadata {0} with name \"{1}\". Error: {2}",
299 asset.FullID, asset.Name, e.Message);
301 transaction.Rollback();
306 if (!ExistsData(dbcon, transaction, hash))
310 using (NpgsqlCommand cmd =
312 @"INSERT INTO XAssetsData(hash, data) VALUES(:Hash, :Data)",
315 cmd.Parameters.Add(m_database.CreateParameter(
"Hash", hash));
316 cmd.Parameters.Add(m_database.CreateParameter(
"Data", asset.Data));
317 cmd.ExecuteNonQuery();
322 m_log.ErrorFormat(
"[XASSET DB]: PGSQL failure creating asset data {0} with name \"{1}\". Error: {2}",
323 asset.FullID, asset.Name, e.Message);
325 transaction.Rollback();
331 transaction.Commit();
346 private void UpdateAccessTime(
AssetMetadata assetMetadata,
int accessTime)
348 DateTime now = DateTime.UtcNow;
350 if ((now -
Utils.UnixTimeToDateTime(accessTime)).TotalDays < DaysBetweenAccessTimeUpdates)
355 using (NpgsqlConnection dbcon =
new NpgsqlConnection(m_connectionString))
359 new NpgsqlCommand(
@"update XAssetsMeta set access_time=:AccessTime where id=:ID", dbcon);
364 UUID.TryParse(assetMetadata.ID, out asset_id);
369 cmd.Parameters.Add(m_database.CreateParameter(
"id", asset_id));
370 cmd.Parameters.Add(m_database.CreateParameter(
"access_time", (int)
Utils.DateTimeToUnixTime(now)));
371 cmd.ExecuteNonQuery();
377 "[XASSET PGSQL DB]: Failure updating access_time for asset {0} with name {1} : {2}",
378 assetMetadata.ID, assetMetadata.Name, e.Message);
392 private bool ExistsData(NpgsqlConnection dbcon, NpgsqlTransaction transaction, byte[] hash)
398 using (NpgsqlCommand cmd =
new NpgsqlCommand(
@"SELECT hash FROM XAssetsData WHERE hash=:Hash", dbcon))
400 cmd.Parameters.Add(m_database.CreateParameter(
"Hash", hash));
404 using (NpgsqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
416 "[XASSETS DB]: PGSql failure in ExistsData fetching hash {0}. Exception {1}{2}",
417 hash, e.Message, e.StackTrace);
431 if (uuids.Length == 0)
434 HashSet<UUID> exist =
new HashSet<UUID>();
436 string ids =
"'" + string.Join(
"','", uuids) +
"'";
437 string sql = string.Format(
@"SELECT id FROM XAssetsMeta WHERE id IN ({0})", ids);
439 using (NpgsqlConnection conn =
new NpgsqlConnection(m_connectionString))
442 using (NpgsqlCommand cmd =
new NpgsqlCommand(sql, conn))
444 using (NpgsqlDataReader reader = cmd.ExecuteReader())
446 while (reader.Read())
448 UUID
id = DBGuid.FromDB(reader[
"id"]);
455 bool[] results =
new bool[uuids.Length];
456 for (
int i = 0; i < uuids.Length; i++)
457 results[i] = exist.Contains(uuids[i]);
470 bool assetExists =
false;
474 using (NpgsqlConnection dbcon =
new NpgsqlConnection(m_connectionString))
477 using (NpgsqlCommand cmd =
new NpgsqlCommand(
@"SELECT id FROM XAssetsMeta WHERE id=:ID", dbcon))
479 cmd.Parameters.Add(m_database.CreateParameter(
"id", uuid));
483 using (NpgsqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
494 m_log.Error(string.Format(
"[XASSETS DB]: PGSql failure fetching asset {0}", uuid), e);
514 List<AssetMetadata> retList =
new List<AssetMetadata>(count);
518 using (NpgsqlConnection dbcon =
new NpgsqlConnection(m_connectionString))
521 NpgsqlCommand cmd =
new NpgsqlCommand(
@"SELECT name, description, access_time, ""AssetType"", temporary, id, asset_flags, creatorid
523 LIMIT :start, :count", dbcon);
524 cmd.Parameters.Add(m_database.CreateParameter(
"start", start));
525 cmd.Parameters.Add(m_database.CreateParameter(
"count", count));
529 using (NpgsqlDataReader dbReader = cmd.ExecuteReader())
531 while (dbReader.Read())
534 metadata.Name = (string)dbReader[
"name"];
535 metadata.Description = (string)dbReader[
"description"];
536 metadata.Type = Convert.ToSByte(dbReader[
"AssetType"]);
537 metadata.Temporary = Convert.ToBoolean(dbReader[
"temporary"]);
538 metadata.Flags = (
AssetFlags)Convert.ToInt32(dbReader[
"asset_flags"]);
539 metadata.FullID = DBGuid.FromDB(dbReader[
"id"]);
540 metadata.CreatorID = dbReader[
"creatorid"].ToString();
545 UpdateAccessTime(metadata, (
int)dbReader[
"access_time"]);
547 retList.Add(metadata);
553 m_log.Error(
"[XASSETS DB]: PGSql failure fetching asset set" + Environment.NewLine + e.ToString());
567 using (NpgsqlConnection dbcon =
new NpgsqlConnection(m_connectionString))
571 using (NpgsqlCommand cmd =
new NpgsqlCommand(
@"delete from XAssetsMeta where id=:ID", dbcon))
573 cmd.Parameters.Add(m_database.CreateParameter(id, id));
574 cmd.ExecuteNonQuery();
bool[] AssetsExist(UUID[] uuids)
Check if the assets exist in the database.
void StoreAsset(AssetBase asset)
Create an asset in database, or update it if existing.
AssetBase GetAsset(UUID assetID)
Fetch Asset assetID from database
OpenSim.Server.Handlers.Simulation.Utils Utils
void Initialise()
Default-initialises the plugin
Ionic.Zlib.GZipStream GZipStream
Asset class. All Assets are reference by this class or a class derived from this class ...
A management class for the MS SQL Storage Engine
List< AssetMetadata > FetchAssetMetadataSet(int start, int count)
Returns a list of AssetMetadata objects. The list is a subset of the entire data set offset by start ...
This interface exists to distinguish between the normal IAssetDataPlugin and the one used by XAssetSe...
Ionic.Zlib.CompressionMode CompressionMode
void Initialise(string connect)
bool ExistsAsset(UUID uuid)
Check if the asset exists in the database