diff options
Diffstat (limited to 'OpenSim/Data')
-rw-r--r-- | OpenSim/Data/AssetDataBase.cs | 3 | ||||
-rw-r--r-- | OpenSim/Data/IAssetData.cs | 2 | ||||
-rw-r--r-- | OpenSim/Data/IXAssetDataPlugin.cs | 2 | ||||
-rw-r--r-- | OpenSim/Data/MSSQL/MSSQLAssetData.cs | 35 | ||||
-rw-r--r-- | OpenSim/Data/MySQL/MySQLAssetData.cs | 42 | ||||
-rw-r--r-- | OpenSim/Data/MySQL/MySQLXAssetData.cs | 40 | ||||
-rw-r--r-- | OpenSim/Data/PGSQL/PGSQLAssetData.cs | 35 | ||||
-rw-r--r-- | OpenSim/Data/PGSQL/PGSQLXAssetData.cs | 37 | ||||
-rw-r--r-- | OpenSim/Data/SQLite/SQLiteAssetData.cs | 37 | ||||
-rw-r--r-- | OpenSim/Data/Tests/AssetTests.cs | 16 |
10 files changed, 166 insertions, 83 deletions
diff --git a/OpenSim/Data/AssetDataBase.cs b/OpenSim/Data/AssetDataBase.cs index e1a810c..1bb432c 100644 --- a/OpenSim/Data/AssetDataBase.cs +++ b/OpenSim/Data/AssetDataBase.cs | |||
@@ -37,9 +37,8 @@ namespace OpenSim.Data | |||
37 | public abstract class AssetDataBase : IAssetDataPlugin | 37 | public abstract class AssetDataBase : IAssetDataPlugin |
38 | { | 38 | { |
39 | public abstract AssetBase GetAsset(UUID uuid); | 39 | public abstract AssetBase GetAsset(UUID uuid); |
40 | |||
41 | public abstract void StoreAsset(AssetBase asset); | 40 | public abstract void StoreAsset(AssetBase asset); |
42 | public abstract bool ExistsAsset(UUID uuid); | 41 | public abstract bool[] AssetsExist(UUID[] uuids); |
43 | 42 | ||
44 | public abstract List<AssetMetadata> FetchAssetMetadataSet(int start, int count); | 43 | public abstract List<AssetMetadata> FetchAssetMetadataSet(int start, int count); |
45 | 44 | ||
diff --git a/OpenSim/Data/IAssetData.cs b/OpenSim/Data/IAssetData.cs index f31b215c..a41e310 100644 --- a/OpenSim/Data/IAssetData.cs +++ b/OpenSim/Data/IAssetData.cs | |||
@@ -35,7 +35,7 @@ namespace OpenSim.Data | |||
35 | { | 35 | { |
36 | AssetBase GetAsset(UUID uuid); | 36 | AssetBase GetAsset(UUID uuid); |
37 | void StoreAsset(AssetBase asset); | 37 | void StoreAsset(AssetBase asset); |
38 | bool ExistsAsset(UUID uuid); | 38 | bool[] AssetsExist(UUID[] uuids); |
39 | List<AssetMetadata> FetchAssetMetadataSet(int start, int count); | 39 | List<AssetMetadata> FetchAssetMetadataSet(int start, int count); |
40 | void Initialise(string connect); | 40 | void Initialise(string connect); |
41 | bool Delete(string id); | 41 | bool Delete(string id); |
diff --git a/OpenSim/Data/IXAssetDataPlugin.cs b/OpenSim/Data/IXAssetDataPlugin.cs index 74ad6f4..2d24797 100644 --- a/OpenSim/Data/IXAssetDataPlugin.cs +++ b/OpenSim/Data/IXAssetDataPlugin.cs | |||
@@ -39,7 +39,7 @@ namespace OpenSim.Data | |||
39 | { | 39 | { |
40 | AssetBase GetAsset(UUID uuid); | 40 | AssetBase GetAsset(UUID uuid); |
41 | void StoreAsset(AssetBase asset); | 41 | void StoreAsset(AssetBase asset); |
42 | bool ExistsAsset(UUID uuid); | 42 | bool[] AssetsExist(UUID[] uuids); |
43 | List<AssetMetadata> FetchAssetMetadataSet(int start, int count); | 43 | List<AssetMetadata> FetchAssetMetadataSet(int start, int count); |
44 | void Initialise(string connect); | 44 | void Initialise(string connect); |
45 | bool Delete(string id); | 45 | bool Delete(string id); |
diff --git a/OpenSim/Data/MSSQL/MSSQLAssetData.cs b/OpenSim/Data/MSSQL/MSSQLAssetData.cs index f3e008d..ce70396 100644 --- a/OpenSim/Data/MSSQL/MSSQLAssetData.cs +++ b/OpenSim/Data/MSSQL/MSSQLAssetData.cs | |||
@@ -225,17 +225,38 @@ namespace OpenSim.Data.MSSQL | |||
225 | // } | 225 | // } |
226 | 226 | ||
227 | /// <summary> | 227 | /// <summary> |
228 | /// Check if asset exist in m_database | 228 | /// Check if the assets exist in the database. |
229 | /// </summary> | 229 | /// </summary> |
230 | /// <param name="uuid"></param> | 230 | /// <param name="uuids">The assets' IDs</param> |
231 | /// <returns>true if exist.</returns> | 231 | /// <returns>For each asset: true if it exists, false otherwise</returns> |
232 | override public bool ExistsAsset(UUID uuid) | 232 | public override bool[] AssetsExist(UUID[] uuids) |
233 | { | 233 | { |
234 | if (GetAsset(uuid) != null) | 234 | if (uuids.Length == 0) |
235 | return new bool[0]; | ||
236 | |||
237 | HashSet<UUID> exist = new HashSet<UUID>(); | ||
238 | |||
239 | string ids = "'" + string.Join("','", uuids) + "'"; | ||
240 | string sql = string.Format("SELECT id FROM assets WHERE id IN ({0})", ids); | ||
241 | |||
242 | using (SqlConnection conn = new SqlConnection(m_connectionString)) | ||
243 | using (SqlCommand cmd = new SqlCommand(sql, conn)) | ||
235 | { | 244 | { |
236 | return true; | 245 | conn.Open(); |
246 | using (SqlDataReader reader = cmd.ExecuteReader()) | ||
247 | { | ||
248 | while (reader.Read()) | ||
249 | { | ||
250 | UUID id = DBGuid.FromDB(reader["id"]); | ||
251 | exist.Add(id); | ||
252 | } | ||
253 | } | ||
237 | } | 254 | } |
238 | return false; | 255 | |
256 | bool[] results = new bool[uuids.Length]; | ||
257 | for (int i = 0; i < uuids.Length; i++) | ||
258 | results[i] = exist.Contains(uuids[i]); | ||
259 | return results; | ||
239 | } | 260 | } |
240 | 261 | ||
241 | /// <summary> | 262 | /// <summary> |
diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs index 21362b9..c96139d 100644 --- a/OpenSim/Data/MySQL/MySQLAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLAssetData.cs | |||
@@ -257,46 +257,44 @@ namespace OpenSim.Data.MySQL | |||
257 | } | 257 | } |
258 | 258 | ||
259 | /// <summary> | 259 | /// <summary> |
260 | /// Check if the asset exists in the database | 260 | /// Check if the assets exist in the database. |
261 | /// </summary> | 261 | /// </summary> |
262 | /// <param name="uuid">The asset UUID</param> | 262 | /// <param name="uuidss">The assets' IDs</param> |
263 | /// <returns>true if it exists, false otherwise.</returns> | 263 | /// <returns>For each asset: true if it exists, false otherwise</returns> |
264 | override public bool ExistsAsset(UUID uuid) | 264 | public override bool[] AssetsExist(UUID[] uuids) |
265 | { | 265 | { |
266 | // m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid); | 266 | if (uuids.Length == 0) |
267 | return new bool[0]; | ||
267 | 268 | ||
268 | bool assetExists = false; | 269 | HashSet<UUID> exist = new HashSet<UUID>(); |
270 | |||
271 | string ids = "'" + string.Join("','", uuids) + "'"; | ||
272 | string sql = string.Format("SELECT id FROM assets WHERE id IN ({0})", ids); | ||
269 | 273 | ||
270 | lock (m_dbLock) | 274 | lock (m_dbLock) |
271 | { | 275 | { |
272 | using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) | 276 | using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) |
273 | { | 277 | { |
274 | dbcon.Open(); | 278 | dbcon.Open(); |
275 | using (MySqlCommand cmd = new MySqlCommand("SELECT id FROM assets WHERE id=?id", dbcon)) | 279 | using (MySqlCommand cmd = new MySqlCommand(sql, dbcon)) |
276 | { | 280 | { |
277 | cmd.Parameters.AddWithValue("?id", uuid.ToString()); | 281 | using (MySqlDataReader dbReader = cmd.ExecuteReader()) |
278 | |||
279 | try | ||
280 | { | 282 | { |
281 | using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) | 283 | while (dbReader.Read()) |
282 | { | 284 | { |
283 | if (dbReader.Read()) | 285 | UUID id = DBGuid.FromDB(dbReader["id"]); |
284 | { | 286 | exist.Add(id); |
285 | // m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid); | ||
286 | assetExists = true; | ||
287 | } | ||
288 | } | 287 | } |
289 | } | 288 | } |
290 | catch (Exception e) | ||
291 | { | ||
292 | m_log.Error( | ||
293 | string.Format("[ASSETS DB]: MySql failure fetching asset {0}. Exception ", uuid), e); | ||
294 | } | ||
295 | } | 289 | } |
296 | } | 290 | } |
297 | } | 291 | } |
298 | 292 | ||
299 | return assetExists; | 293 | bool[] results = new bool[uuids.Length]; |
294 | for (int i = 0; i < uuids.Length; i++) | ||
295 | results[i] = exist.Contains(uuids[i]); | ||
296 | |||
297 | return results; | ||
300 | } | 298 | } |
301 | 299 | ||
302 | /// <summary> | 300 | /// <summary> |
diff --git a/OpenSim/Data/MySQL/MySQLXAssetData.cs b/OpenSim/Data/MySQL/MySQLXAssetData.cs index 91389ce..1bf6a9a 100644 --- a/OpenSim/Data/MySQL/MySQLXAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLXAssetData.cs | |||
@@ -397,45 +397,43 @@ namespace OpenSim.Data.MySQL | |||
397 | } | 397 | } |
398 | 398 | ||
399 | /// <summary> | 399 | /// <summary> |
400 | /// Check if the asset exists in the database | 400 | /// Check if the assets exist in the database. |
401 | /// </summary> | 401 | /// </summary> |
402 | /// <param name="uuid">The asset UUID</param> | 402 | /// <param name="uuids">The asset UUID's</param> |
403 | /// <returns>true if it exists, false otherwise.</returns> | 403 | /// <returns>For each asset: true if it exists, false otherwise</returns> |
404 | public bool ExistsAsset(UUID uuid) | 404 | public bool[] AssetsExist(UUID[] uuids) |
405 | { | 405 | { |
406 | // m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid); | 406 | if (uuids.Length == 0) |
407 | return new bool[0]; | ||
408 | |||
409 | HashSet<UUID> exists = new HashSet<UUID>(); | ||
407 | 410 | ||
408 | bool assetExists = false; | 411 | string ids = "'" + string.Join("','", uuids) + "'"; |
412 | string sql = string.Format("SELECT ID FROM assets WHERE ID IN ({0})", ids); | ||
409 | 413 | ||
410 | lock (m_dbLock) | 414 | lock (m_dbLock) |
411 | { | 415 | { |
412 | using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) | 416 | using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) |
413 | { | 417 | { |
414 | dbcon.Open(); | 418 | dbcon.Open(); |
415 | using (MySqlCommand cmd = new MySqlCommand("SELECT ID FROM XAssetsMeta WHERE ID=?ID", dbcon)) | 419 | using (MySqlCommand cmd = new MySqlCommand(sql, dbcon)) |
416 | { | 420 | { |
417 | cmd.Parameters.AddWithValue("?ID", uuid.ToString()); | 421 | using (MySqlDataReader dbReader = cmd.ExecuteReader()) |
418 | |||
419 | try | ||
420 | { | 422 | { |
421 | using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) | 423 | while (dbReader.Read()) |
422 | { | 424 | { |
423 | if (dbReader.Read()) | 425 | UUID id = DBGuid.FromDB(dbReader["ID"]); |
424 | { | 426 | exists.Add(id); |
425 | // m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid); | ||
426 | assetExists = true; | ||
427 | } | ||
428 | } | 427 | } |
429 | } | 428 | } |
430 | catch (Exception e) | ||
431 | { | ||
432 | m_log.Error(string.Format("[XASSETS DB]: MySql failure fetching asset {0}", uuid), e); | ||
433 | } | ||
434 | } | 429 | } |
435 | } | 430 | } |
436 | } | 431 | } |
437 | 432 | ||
438 | return assetExists; | 433 | bool[] results = new bool[uuids.Length]; |
434 | for (int i = 0; i < uuids.Length; i++) | ||
435 | results[i] = exists.Contains(uuids[i]); | ||
436 | return results; | ||
439 | } | 437 | } |
440 | 438 | ||
441 | 439 | ||
diff --git a/OpenSim/Data/PGSQL/PGSQLAssetData.cs b/OpenSim/Data/PGSQL/PGSQLAssetData.cs index ab74856..ca18dc9 100644 --- a/OpenSim/Data/PGSQL/PGSQLAssetData.cs +++ b/OpenSim/Data/PGSQL/PGSQLAssetData.cs | |||
@@ -231,17 +231,38 @@ namespace OpenSim.Data.PGSQL | |||
231 | // } | 231 | // } |
232 | 232 | ||
233 | /// <summary> | 233 | /// <summary> |
234 | /// Check if asset exist in m_database | 234 | /// Check if the assets exist in the database. |
235 | /// </summary> | 235 | /// </summary> |
236 | /// <param name="uuid"></param> | 236 | /// <param name="uuids">The assets' IDs</param> |
237 | /// <returns>true if exist.</returns> | 237 | /// <returns>For each asset: true if it exists, false otherwise</returns> |
238 | override public bool ExistsAsset(UUID uuid) | 238 | public override bool[] AssetsExist(UUID[] uuids) |
239 | { | 239 | { |
240 | if (GetAsset(uuid) != null) | 240 | if (uuids.Length == 0) |
241 | return new bool[0]; | ||
242 | |||
243 | HashSet<UUID> exist = new HashSet<UUID>(); | ||
244 | |||
245 | string ids = "'" + string.Join("','", uuids) + "'"; | ||
246 | string sql = string.Format("SELECT id FROM assets WHERE id IN ({0})", ids); | ||
247 | |||
248 | using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) | ||
249 | using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) | ||
241 | { | 250 | { |
242 | return true; | 251 | conn.Open(); |
252 | using (NpgsqlDataReader reader = cmd.ExecuteReader()) | ||
253 | { | ||
254 | while (reader.Read()) | ||
255 | { | ||
256 | UUID id = DBGuid.FromDB(reader["id"]); | ||
257 | exist.Add(id); | ||
258 | } | ||
259 | } | ||
243 | } | 260 | } |
244 | return false; | 261 | |
262 | bool[] results = new bool[uuids.Length]; | ||
263 | for (int i = 0; i < uuids.Length; i++) | ||
264 | results[i] = exist.Contains(uuids[i]); | ||
265 | return results; | ||
245 | } | 266 | } |
246 | 267 | ||
247 | /// <summary> | 268 | /// <summary> |
diff --git a/OpenSim/Data/PGSQL/PGSQLXAssetData.cs b/OpenSim/Data/PGSQL/PGSQLXAssetData.cs index e959619..c6cebff 100644 --- a/OpenSim/Data/PGSQL/PGSQLXAssetData.cs +++ b/OpenSim/Data/PGSQL/PGSQLXAssetData.cs | |||
@@ -407,6 +407,43 @@ namespace OpenSim.Data.PGSQL | |||
407 | } | 407 | } |
408 | 408 | ||
409 | /// <summary> | 409 | /// <summary> |
410 | /// Check if the assets exist in the database. | ||
411 | /// </summary> | ||
412 | /// <param name="uuids">The assets' IDs</param> | ||
413 | /// <returns>For each asset: true if it exists, false otherwise</returns> | ||
414 | public bool[] AssetsExist(UUID[] uuids) | ||
415 | { | ||
416 | if (uuids.Length == 0) | ||
417 | return new bool[0]; | ||
418 | |||
419 | HashSet<UUID> exist = new HashSet<UUID>(); | ||
420 | |||
421 | string ids = "'" + string.Join("','", uuids) + "'"; | ||
422 | string sql = string.Format(@"SELECT ""ID"" FROM XAssetsMeta WHERE ""ID"" IN ({0})", ids); | ||
423 | |||
424 | using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) | ||
425 | { | ||
426 | conn.Open(); | ||
427 | using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) | ||
428 | { | ||
429 | using (NpgsqlDataReader reader = cmd.ExecuteReader()) | ||
430 | { | ||
431 | while (reader.Read()) | ||
432 | { | ||
433 | UUID id = DBGuid.FromDB(reader["id"]); | ||
434 | exist.Add(id); | ||
435 | } | ||
436 | } | ||
437 | } | ||
438 | } | ||
439 | |||
440 | bool[] results = new bool[uuids.Length]; | ||
441 | for (int i = 0; i < uuids.Length; i++) | ||
442 | results[i] = exist.Contains(uuids[i]); | ||
443 | return results; | ||
444 | } | ||
445 | |||
446 | /// <summary> | ||
410 | /// Check if the asset exists in the database | 447 | /// Check if the asset exists in the database |
411 | /// </summary> | 448 | /// </summary> |
412 | /// <param name="uuid">The asset UUID</param> | 449 | /// <param name="uuid">The asset UUID</param> |
diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs index c32982e..1f32376 100644 --- a/OpenSim/Data/SQLite/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs | |||
@@ -152,7 +152,7 @@ namespace OpenSim.Data.SQLite | |||
152 | } | 152 | } |
153 | 153 | ||
154 | //m_log.Info("[ASSET DB]: Creating Asset " + asset.FullID.ToString()); | 154 | //m_log.Info("[ASSET DB]: Creating Asset " + asset.FullID.ToString()); |
155 | if (ExistsAsset(asset.FullID)) | 155 | if (AssetsExist(new[] { asset.FullID })[0]) |
156 | { | 156 | { |
157 | //LogAssetLoad(asset); | 157 | //LogAssetLoad(asset); |
158 | 158 | ||
@@ -214,32 +214,39 @@ namespace OpenSim.Data.SQLite | |||
214 | // } | 214 | // } |
215 | 215 | ||
216 | /// <summary> | 216 | /// <summary> |
217 | /// Check if an asset exist in database | 217 | /// Check if the assets exist in the database. |
218 | /// </summary> | 218 | /// </summary> |
219 | /// <param name="uuid">The asset UUID</param> | 219 | /// <param name="uuids">The assets' IDs</param> |
220 | /// <returns>True if exist, or false.</returns> | 220 | /// <returns>For each asset: true if it exists, false otherwise</returns> |
221 | override public bool ExistsAsset(UUID uuid) | 221 | public override bool[] AssetsExist(UUID[] uuids) |
222 | { | 222 | { |
223 | lock (this) | 223 | if (uuids.Length == 0) |
224 | return new bool[0]; | ||
225 | |||
226 | HashSet<UUID> exist = new HashSet<UUID>(); | ||
227 | |||
228 | string ids = "'" + string.Join("','", uuids) + "'"; | ||
229 | string sql = string.Format("SELECT id FROM assets WHERE id IN ({0})", ids); | ||
230 | |||
231 | lock (this) | ||
224 | { | 232 | { |
225 | using (SqliteCommand cmd = new SqliteCommand(SelectAssetSQL, m_conn)) | 233 | using (SqliteCommand cmd = new SqliteCommand(SelectAssetSQL, m_conn)) |
226 | { | 234 | { |
227 | cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); | ||
228 | using (IDataReader reader = cmd.ExecuteReader()) | 235 | using (IDataReader reader = cmd.ExecuteReader()) |
229 | { | 236 | { |
230 | if (reader.Read()) | 237 | while (reader.Read()) |
231 | { | ||
232 | reader.Close(); | ||
233 | return true; | ||
234 | } | ||
235 | else | ||
236 | { | 238 | { |
237 | reader.Close(); | 239 | UUID id = new UUID((string)reader["UUID"]); |
238 | return false; | 240 | exist.Add(id); |
239 | } | 241 | } |
240 | } | 242 | } |
241 | } | 243 | } |
242 | } | 244 | } |
245 | |||
246 | bool[] results = new bool[uuids.Length]; | ||
247 | for (int i = 0; i < uuids.Length; i++) | ||
248 | results[i] = exist.Contains(uuids[i]); | ||
249 | return results; | ||
243 | } | 250 | } |
244 | 251 | ||
245 | /// <summary> | 252 | /// <summary> |
diff --git a/OpenSim/Data/Tests/AssetTests.cs b/OpenSim/Data/Tests/AssetTests.cs index 8cb2ee0..d778d1c 100644 --- a/OpenSim/Data/Tests/AssetTests.cs +++ b/OpenSim/Data/Tests/AssetTests.cs | |||
@@ -107,10 +107,11 @@ namespace OpenSim.Data.Tests | |||
107 | public void T001_LoadEmpty() | 107 | public void T001_LoadEmpty() |
108 | { | 108 | { |
109 | TestHelpers.InMethod(); | 109 | TestHelpers.InMethod(); |
110 | 110 | ||
111 | Assert.That(m_db.ExistsAsset(uuid1), Is.False); | 111 | bool[] exist = m_db.AssetsExist(new[] { uuid1, uuid2, uuid3 }); |
112 | Assert.That(m_db.ExistsAsset(uuid2), Is.False); | 112 | Assert.IsFalse(exist[0]); |
113 | Assert.That(m_db.ExistsAsset(uuid3), Is.False); | 113 | Assert.IsFalse(exist[1]); |
114 | Assert.IsFalse(exist[2]); | ||
114 | } | 115 | } |
115 | 116 | ||
116 | [Test] | 117 | [Test] |
@@ -159,9 +160,10 @@ namespace OpenSim.Data.Tests | |||
159 | AssetBase a3b = m_db.GetAsset(uuid3); | 160 | AssetBase a3b = m_db.GetAsset(uuid3); |
160 | Assert.That(a3b, Constraints.PropertyCompareConstraint(a3a)); | 161 | Assert.That(a3b, Constraints.PropertyCompareConstraint(a3a)); |
161 | 162 | ||
162 | Assert.That(m_db.ExistsAsset(uuid1), Is.True); | 163 | bool[] exist = m_db.AssetsExist(new[] { uuid1, uuid2, uuid3 }); |
163 | Assert.That(m_db.ExistsAsset(uuid2), Is.True); | 164 | Assert.IsTrue(exist[0]); |
164 | Assert.That(m_db.ExistsAsset(uuid3), Is.True); | 165 | Assert.IsTrue(exist[1]); |
166 | Assert.IsTrue(exist[2]); | ||
165 | 167 | ||
166 | List<AssetMetadata> metadatas = m_db.FetchAssetMetadataSet(0, 1000); | 168 | List<AssetMetadata> metadatas = m_db.FetchAssetMetadataSet(0, 1000); |
167 | 169 | ||