From 80759f708b1766b6fd36bbdb6ce69f292e64e2f0 Mon Sep 17 00:00:00 2001 From: Justin Clarke Casey Date: Mon, 16 Feb 2009 19:33:11 +0000 Subject: * Apply http://opensimulator.org/mantis/view.php?id=3166 * Adds estate access list supports to NHibernate data module * Thanks Tommil --- OpenSim/Data/NHibernate/NHibernateAssetData.cs | 4 +- OpenSim/Data/NHibernate/NHibernateEstateData.cs | 8 +- OpenSim/Data/NHibernate/NHibernateGridData.cs | 8 +- OpenSim/Data/NHibernate/NHibernateInventoryData.cs | 8 +- OpenSim/Data/NHibernate/NHibernateManager.cs | 187 +++++++++++++++++---- OpenSim/Data/NHibernate/NHibernateRegionData.cs | 10 +- OpenSim/Data/NHibernate/NHibernateUserData.cs | 12 +- .../NHibernate/Resources/EstateSettings.hbm.xml | 18 ++ .../Resources/MsSql2005Dialect/001_EstateStore.sql | 22 +++ .../Resources/MySQLDialect/001_EstateStore.sql | 22 +++ .../PostgreSQLDialect/001_EstateStore.sql | 22 +++ .../Resources/SQLiteDialect/001_EstateStore.sql | 21 +++ 12 files changed, 280 insertions(+), 62 deletions(-) diff --git a/OpenSim/Data/NHibernate/NHibernateAssetData.cs b/OpenSim/Data/NHibernate/NHibernateAssetData.cs index f8715cc..8f4877d 100644 --- a/OpenSim/Data/NHibernate/NHibernateAssetData.cs +++ b/OpenSim/Data/NHibernate/NHibernateAssetData.cs @@ -59,12 +59,12 @@ namespace OpenSim.Data.NHibernate override public AssetBase FetchAsset(UUID uuid) { - return (AssetBase)manager.Load(typeof(AssetBase), uuid); + return (AssetBase)manager.Get(typeof(AssetBase), uuid); } private void Save(AssetBase asset) { - AssetBase temp = (AssetBase)manager.Load(typeof(AssetBase), asset.Metadata.FullID); + AssetBase temp = (AssetBase)manager.Get(typeof(AssetBase), asset.Metadata.FullID); if (temp == null) { manager.Insert(asset); diff --git a/OpenSim/Data/NHibernate/NHibernateEstateData.cs b/OpenSim/Data/NHibernate/NHibernateEstateData.cs index 3d74135..b16f6e1 100644 --- a/OpenSim/Data/NHibernate/NHibernateEstateData.cs +++ b/OpenSim/Data/NHibernate/NHibernateEstateData.cs @@ -89,7 +89,7 @@ namespace OpenSim.Data.NHibernate // Ensure that estate settings exist for the link if (link != null) { - if (manager.Load(typeof(EstateSettings), link.EstateID) == null) + if (manager.GetWithStatefullSession(typeof(EstateSettings), link.EstateID) == null) { // Delete broken link manager.Delete(link); @@ -116,11 +116,11 @@ namespace OpenSim.Data.NHibernate link.EstateRegionLinkID = UUID.Random(); link.RegionID = regionID; link.EstateID = estateID; - manager.Insert(link); + manager.InsertWithStatefullSession(link); } // Load estate settings according to the existing or created link. - return (EstateSettings)manager.Load(typeof(EstateSettings), link.EstateID); + return (EstateSettings)manager.GetWithStatefullSession(typeof(EstateSettings), link.EstateID); } public void StoreEstateSettings(EstateSettings estateSettings) @@ -128,7 +128,7 @@ namespace OpenSim.Data.NHibernate // Estates are always updated when stored. // Insert is always done via. load method as with the current API // this is explicitly the only way to create region link. - manager.Update(estateSettings); + manager.UpdateWithStatefullSession(estateSettings); } #endregion diff --git a/OpenSim/Data/NHibernate/NHibernateGridData.cs b/OpenSim/Data/NHibernate/NHibernateGridData.cs index e58d1ed..b89cefc 100644 --- a/OpenSim/Data/NHibernate/NHibernateGridData.cs +++ b/OpenSim/Data/NHibernate/NHibernateGridData.cs @@ -111,7 +111,7 @@ namespace OpenSim.Data.NHibernate public override DataResponse AddProfile(RegionProfileData profile) { - if (manager.Load(typeof(RegionProfileData), profile.Uuid) == null) + if (manager.Get(typeof(RegionProfileData), profile.Uuid) == null) { manager.Insert(profile); return DataResponse.RESPONSE_OK; @@ -124,7 +124,7 @@ namespace OpenSim.Data.NHibernate public override DataResponse UpdateProfile(RegionProfileData profile) { - if (manager.Load(typeof(RegionProfileData), profile.Uuid) != null) + if (manager.Get(typeof(RegionProfileData), profile.Uuid) != null) { manager.Update(profile); return DataResponse.RESPONSE_OK; @@ -137,7 +137,7 @@ namespace OpenSim.Data.NHibernate public override DataResponse DeleteProfile(string uuid) { - RegionProfileData regionProfileData = (RegionProfileData)manager.Load(typeof(RegionProfileData), new UUID(uuid)); + RegionProfileData regionProfileData = (RegionProfileData)manager.Get(typeof(RegionProfileData), new UUID(uuid)); if (regionProfileData != null) { manager.Delete(regionProfileData); @@ -148,7 +148,7 @@ namespace OpenSim.Data.NHibernate public override RegionProfileData GetProfileByUUID(UUID UUID) { - return (RegionProfileData)manager.Load(typeof(RegionProfileData), UUID); + return (RegionProfileData)manager.Get(typeof(RegionProfileData), UUID); } public override RegionProfileData GetProfileByHandle(ulong regionHandle) diff --git a/OpenSim/Data/NHibernate/NHibernateInventoryData.cs b/OpenSim/Data/NHibernate/NHibernateInventoryData.cs index 7657ae5..3e04417 100644 --- a/OpenSim/Data/NHibernate/NHibernateInventoryData.cs +++ b/OpenSim/Data/NHibernate/NHibernateInventoryData.cs @@ -110,7 +110,7 @@ namespace OpenSim.Data.NHibernate try { m_log.InfoFormat("[NHIBERNATE] getInventoryItem {0}", item); - return (InventoryItemBase)manager.Load(typeof(InventoryItemBase), item); + return (InventoryItemBase)manager.Get(typeof(InventoryItemBase), item); } catch { @@ -158,7 +158,7 @@ namespace OpenSim.Data.NHibernate /// public void deleteInventoryItem(UUID itemID) { - InventoryItemBase item = (InventoryItemBase)manager.Load(typeof(InventoryItemBase), itemID); + InventoryItemBase item = (InventoryItemBase)manager.Get(typeof(InventoryItemBase), itemID); if (item != null) { manager.Delete(item); @@ -179,7 +179,7 @@ namespace OpenSim.Data.NHibernate { try { - return (InventoryFolderBase)manager.Load(typeof(InventoryFolderBase), folder); + return (InventoryFolderBase)manager.Get(typeof(InventoryFolderBase), folder); } catch { @@ -227,7 +227,7 @@ namespace OpenSim.Data.NHibernate /// public void deleteInventoryFolder(UUID folderID) { - InventoryFolderBase item = (InventoryFolderBase)manager.Load(typeof(InventoryFolderBase), folderID); + InventoryFolderBase item = (InventoryFolderBase)manager.Get(typeof(InventoryFolderBase), folderID); if (item != null) { manager.Delete(item); diff --git a/OpenSim/Data/NHibernate/NHibernateManager.cs b/OpenSim/Data/NHibernate/NHibernateManager.cs index 26bc219..cef98f2 100644 --- a/OpenSim/Data/NHibernate/NHibernateManager.cs +++ b/OpenSim/Data/NHibernate/NHibernateManager.cs @@ -45,6 +45,43 @@ namespace OpenSim.Data.NHibernate private Configuration configuration; private ISessionFactory sessionFactory; + #region Initialization + + /// + /// Initiate NHibernate Manager + /// + /// NHibernate dialect, driver and connection string separated by ';' + /// Name of the store + public NHibernateManager(string connect, string store) + { + ParseConnectionString(connect); + + //To create sql file uncomment code below and write the name of the file + //SchemaExport exp = new SchemaExport(cfg); + //exp.SetOutputFile("nameofthefile.sql"); + //exp.Create(false, true); + + Assembly assembly = GetType().Assembly; + + sessionFactory = configuration.BuildSessionFactory(); + RunMigration(dialect, assembly, store); + } + + /// + /// Initiate NHibernate Manager with spesific assembly + /// + /// NHibernate dialect, driver and connection string separated by ';' + /// Name of the store + /// Outside assembly to be included + public NHibernateManager(string connect, string store, Assembly assembly) + { + ParseConnectionString(connect); + + configuration.AddAssembly(assembly); + sessionFactory = configuration.BuildSessionFactory(); + RunMigration(dialect, assembly, store); + } + /// /// Parses the connection string and creates the NHibernate configuration /// @@ -72,7 +109,6 @@ namespace OpenSim.Data.NHibernate "NHibernate.Driver." + parts[1]); configuration.SetProperty(Environment.ConnectionString, parts[2]); configuration.AddAssembly("OpenSim.Data.NHibernate"); - } /// @@ -96,44 +132,42 @@ namespace OpenSim.Data.NHibernate migration.Update(); } + #endregion + /// - /// Initiate NHibernate Manager + /// Gets object of given type from database with given id. + /// Uses stateless session for efficiency. /// - /// NHibernate dialect, driver and connection string separated by ';' - /// Name of the store - public NHibernateManager(string connect, string store) + /// Type of the object. + /// Id of the object. + /// The object or null if object was not found. + public object Get(Type type, Object id) { - ParseConnectionString(connect); - - //To create sql file uncomment code below and write the name of the file - //SchemaExport exp = new SchemaExport(cfg); - //exp.SetOutputFile("nameofthefile.sql"); - //exp.Create(false, true); - - Assembly assembly = GetType().Assembly; - - sessionFactory = configuration.BuildSessionFactory(); - RunMigration(dialect, assembly, store); + using (IStatelessSession session = sessionFactory.OpenStatelessSession()) + { + object obj = null; + try + { + obj = session.Get(type.FullName, id); + } + catch (Exception e) + { + m_log.ErrorFormat("[NHIBERNATE] {0} of id {1} loading threw exception: " + e.ToString(), type.Name, id); + } + return obj; + } } /// - /// Initiate NHibernate Manager with spesific assembly + /// Gets object of given type from database with given id. + /// Use this method for objects containing collections. For flat objects stateless mode is more efficient. /// - /// NHibernate dialect, driver and connection string separated by ';' - /// Name of the store - /// Outside assembly to be included - public NHibernateManager(string connect, string store, Assembly assembly) + /// Type of the object. + /// Id of the object. + /// The object or null if object was not found. + public object GetWithStatefullSession(Type type, Object id) { - ParseConnectionString(connect); - - configuration.AddAssembly(assembly); - sessionFactory = configuration.BuildSessionFactory(); - RunMigration(dialect, assembly, store); - } - - public object Load(Type type, Object id) - { - using (IStatelessSession session = sessionFactory.OpenStatelessSession()) + using (ISession session = sessionFactory.OpenSession()) { object obj = null; try @@ -146,9 +180,15 @@ namespace OpenSim.Data.NHibernate } return obj; } - + } + /// + /// Inserts given object to database. + /// Uses stateless session for efficiency. + /// + /// Object to be insterted. + /// Identifier of the object. Useful for situations when NHibernate generates the identifier. public object Insert(object obj) { try @@ -170,6 +210,39 @@ namespace OpenSim.Data.NHibernate } } + /// + /// Inserts given object to database. + /// Use this method for objects containing collections. For flat objects stateless mode is more efficient. + /// + /// Object to be insterted. + /// Identifier of the object. Useful for situations when NHibernate generates the identifier. + public object InsertWithStatefullSession(object obj) + { + try + { + using (ISession session = sessionFactory.OpenSession()) + { + using (ITransaction transaction = session.BeginTransaction()) + { + Object identifier = session.Save(obj); + transaction.Commit(); + return identifier; + } + } + } + catch (Exception e) + { + m_log.Error("[NHIBERNATE] issue inserting object ", e); + return null; + } + } + + /// + /// Updates given object to database. + /// Uses stateless session for efficiency. + /// + /// Object to be updated. + /// True if operation was succesful. public bool Update(object obj) { try @@ -191,6 +264,38 @@ namespace OpenSim.Data.NHibernate } } + /// + /// Updates given object to database. + /// Use this method for objects containing collections. For flat objects stateless mode is more efficient. + /// + /// Object to be updated. + /// True if operation was succesful. + public bool UpdateWithStatefullSession(object obj) + { + try + { + using (ISession session = sessionFactory.OpenSession()) + { + using (ITransaction transaction = session.BeginTransaction()) + { + session.Update(obj); + transaction.Commit(); + return true; + } + } + } + catch (Exception e) + { + m_log.Error("[NHIBERNATE] issue updating object ", e); + return false; + } + } + + /// + /// Deletes given object from database. + /// + /// Object to be deleted. + /// True if operation was succesful. public bool Delete(object obj) { try @@ -212,6 +317,18 @@ namespace OpenSim.Data.NHibernate } } + /// + /// Returns statefull session which can be used to execute custom nhibernate or sql queries. + /// + /// Statefull session + public ISession GetSession() + { + return sessionFactory.OpenSession(); + } + + /// + /// Drops the database schema. This exist for unit tests. It should not be invoked from other than test teardown. + /// public void DropSchema() { SchemaExport export = new SchemaExport(this.configuration); @@ -219,14 +336,10 @@ namespace OpenSim.Data.NHibernate using (ISession session = sessionFactory.OpenSession()) { - ISQLQuery sqlQuery=session.CreateSQLQuery("drop table migrations"); + ISQLQuery sqlQuery = session.CreateSQLQuery("drop table migrations"); sqlQuery.ExecuteUpdate(); } } - public ISession GetSession() - { - return sessionFactory.OpenSession(); - } } } diff --git a/OpenSim/Data/NHibernate/NHibernateRegionData.cs b/OpenSim/Data/NHibernate/NHibernateRegionData.cs index 78db52d..5af3d1a 100644 --- a/OpenSim/Data/NHibernate/NHibernateRegionData.cs +++ b/OpenSim/Data/NHibernate/NHibernateRegionData.cs @@ -63,7 +63,7 @@ namespace OpenSim.Data.NHibernate public void StoreRegionSettings(RegionSettings rs) { - RegionSettings oldRegionSettings = (RegionSettings)manager.Load(typeof(RegionSettings), rs.RegionUUID); + RegionSettings oldRegionSettings = (RegionSettings)manager.Get(typeof(RegionSettings), rs.RegionUUID); if (oldRegionSettings != null) { manager.Update(rs); @@ -76,7 +76,7 @@ namespace OpenSim.Data.NHibernate public RegionSettings LoadRegionSettings(UUID regionUUID) { - RegionSettings regionSettings = (RegionSettings) manager.Load(typeof(RegionSettings), regionUUID); + RegionSettings regionSettings = (RegionSettings) manager.Get(typeof(RegionSettings), regionUUID); if (regionSettings == null) { @@ -96,7 +96,7 @@ namespace OpenSim.Data.NHibernate { try { - SceneObjectPart old = (SceneObjectPart)manager.Load(typeof(SceneObjectPart), p.UUID); + SceneObjectPart old = (SceneObjectPart)manager.Get(typeof(SceneObjectPart), p.UUID); if (old != null) { m_log.InfoFormat("[NHIBERNATE] updating object {0}", p.UUID); @@ -120,7 +120,7 @@ namespace OpenSim.Data.NHibernate try { - Terrain old = (Terrain)manager.Load(typeof(Terrain), t.RegionID); + Terrain old = (Terrain)manager.Get(typeof(Terrain), t.RegionID); if (old != null) { m_log.InfoFormat("[NHIBERNATE] updating terrain {0}", t.RegionID); @@ -279,7 +279,7 @@ namespace OpenSim.Data.NHibernate /// Heightfield data public double[,] LoadTerrain(UUID regionID) { - Terrain t = (Terrain)manager.Load(typeof(Terrain), regionID); + Terrain t = (Terrain)manager.Get(typeof(Terrain), regionID); if (t != null) { return t.Doubles; diff --git a/OpenSim/Data/NHibernate/NHibernateUserData.cs b/OpenSim/Data/NHibernate/NHibernateUserData.cs index 3f1f260..8aa4aa2 100644 --- a/OpenSim/Data/NHibernate/NHibernateUserData.cs +++ b/OpenSim/Data/NHibernate/NHibernateUserData.cs @@ -61,7 +61,7 @@ namespace OpenSim.Data.NHibernate UserProfileData user = null; m_log.InfoFormat("[NHIBERNATE] ExistsUser; {0}", uuid); - user = (UserProfileData)manager.Load(typeof(UserProfileData), uuid); + user = (UserProfileData)manager.Get(typeof(UserProfileData), uuid); if (user == null) { @@ -78,7 +78,7 @@ namespace OpenSim.Data.NHibernate UserProfileData user; m_log.InfoFormat("[NHIBERNATE] GetUserByUUID: {0} ", uuid); - user = (UserProfileData)manager.Load(typeof(UserProfileData), uuid); + user = (UserProfileData)manager.Get(typeof(UserProfileData), uuid); if (user != null) { UserAgentData agent = GetAgentByUUID(uuid); @@ -163,7 +163,7 @@ namespace OpenSim.Data.NHibernate } - UserAgentData old = (UserAgentData)manager.Load(typeof(UserAgentData), agent.ProfileID); + UserAgentData old = (UserAgentData)manager.Get(typeof(UserAgentData), agent.ProfileID); if (old != null) { manager.Delete(old); @@ -182,7 +182,7 @@ namespace OpenSim.Data.NHibernate override public UserAgentData GetAgentByUUID(UUID uuid) { m_log.InfoFormat("[NHIBERNATE] GetAgentByUUID: {0} ", uuid); - return (UserAgentData)manager.Load(typeof(UserAgentData), uuid); + return (UserAgentData)manager.Get(typeof(UserAgentData), uuid); } override public UserProfileData GetUserByName(string fname, string lname) @@ -397,12 +397,12 @@ namespace OpenSim.Data.NHibernate /// TODO: stubs for now to get us to a compiling state gently public override AvatarAppearance GetUserAppearance(UUID user) { - return (AvatarAppearance)manager.Load(typeof(AvatarAppearance), user); + return (AvatarAppearance)manager.Get(typeof(AvatarAppearance), user); } private bool ExistsAppearance(UUID uuid) { - AvatarAppearance appearance = (AvatarAppearance)manager.Load(typeof(AvatarAppearance), uuid); + AvatarAppearance appearance = (AvatarAppearance)manager.Get(typeof(AvatarAppearance), uuid); if (appearance == null) { return false; diff --git a/OpenSim/Data/NHibernate/Resources/EstateSettings.hbm.xml b/OpenSim/Data/NHibernate/Resources/EstateSettings.hbm.xml index 2dc3949..d8c8975 100644 --- a/OpenSim/Data/NHibernate/Resources/EstateSettings.hbm.xml +++ b/OpenSim/Data/NHibernate/Resources/EstateSettings.hbm.xml @@ -35,5 +35,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_EstateStore.sql b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_EstateStore.sql index 4ca51be..b852cbc 100644 --- a/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_EstateStore.sql +++ b/OpenSim/Data/NHibernate/Resources/MsSql2005Dialect/001_EstateStore.sql @@ -38,3 +38,25 @@ CREATE TABLE EstateRegionLink ( CREATE INDEX EstateRegionLinkEstateIDIndex ON EstateRegionLink (EstateID); CREATE INDEX EstateRegionLinkERegionIDIndex ON EstateRegionLink (RegionID); + + +CREATE TABLE EstateManagers ( + EstateID INT NOT NULL, + ManagerID NVARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +); + +CREATE TABLE EstateUsers ( + EstateID INT NOT NULL, + UserID NVARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +); + +CREATE TABLE EstateGroups ( + EstateID INT NOT NULL, + GroupID NVARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +); \ No newline at end of file diff --git a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_EstateStore.sql b/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_EstateStore.sql index 2114186..69635d8 100644 --- a/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_EstateStore.sql +++ b/OpenSim/Data/NHibernate/Resources/MySQLDialect/001_EstateStore.sql @@ -38,3 +38,25 @@ CREATE TABLE EstateRegionLink ( CREATE INDEX EstateRegionLinkEstateIDIndex ON EstateRegionLink (EstateID); CREATE INDEX EstateRegionLinkERegionIDIndex ON EstateRegionLink (RegionID); + +CREATE TABLE EstateManagers ( + EstateID INT NOT NULL, + ManagerID VARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; + +CREATE TABLE EstateUsers ( + EstateID INT NOT NULL, + UserID VARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; + +CREATE TABLE EstateGroups ( + EstateID INT NOT NULL, + GroupID VARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; + diff --git a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_EstateStore.sql b/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_EstateStore.sql index e43fd86..d13a4d3 100644 --- a/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_EstateStore.sql +++ b/OpenSim/Data/NHibernate/Resources/PostgreSQLDialect/001_EstateStore.sql @@ -38,3 +38,25 @@ CREATE TABLE EstateRegionLink ( CREATE INDEX EstateRegionLinkEstateIDIndex ON EstateRegionLink (EstateID); CREATE INDEX EstateRegionLinkERegionIDIndex ON EstateRegionLink (RegionID); + + +CREATE TABLE EstateManagers ( + EstateID INT NOT NULL, + ManagerID VARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +); + +CREATE TABLE EstateUsers ( + EstateID INT NOT NULL, + UserID VARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +); + +CREATE TABLE EstateGroups ( + EstateID INT NOT NULL, + GroupID VARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +); \ No newline at end of file diff --git a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_EstateStore.sql b/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_EstateStore.sql index b540bc6..6e67630 100644 --- a/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_EstateStore.sql +++ b/OpenSim/Data/NHibernate/Resources/SQLiteDialect/001_EstateStore.sql @@ -38,3 +38,24 @@ CREATE TABLE EstateRegionLink ( CREATE INDEX EstateRegionLinkEstateIDIndex ON EstateRegionLink (EstateID); CREATE INDEX EstateRegionLinkERegionIDIndex ON EstateRegionLink (RegionID); + +CREATE TABLE EstateManagers ( + EstateID INT NOT NULL, + ManagerID VARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +); + +CREATE TABLE EstateUsers ( + EstateID INT NOT NULL, + UserID VARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +); + +CREATE TABLE EstateGroups ( + EstateID INT NOT NULL, + GroupID VARCHAR(36) NOT NULL, + ArrayIndex INT NOT NULL, + PRIMARY KEY (EstateID,ArrayIndex) +); -- cgit v1.1