Sunday, April 7, 2013

Remove old modelstore from an upgraded AX2012 R2 database

One of the benefits of AX2012 R2 compared to AX2012 RTM is the fact that they decided to split the database in two parts, one part for the business data and one part for the application data. This makes it a lot easier to copy business data from production to test without overwriting the application data in test. Sure, there were ways around this in RTM, but you can't deny it is a lot easier to do this now. Another benefit is that you no longer have the application (modelstore) residing in the business database. This means you don't need to have a full backup with point-in-time recovery for the production application. The application in RTM would be aroud 2.5 GB exported, but inside the database you would have huge indexes and also the compiled assemblies stored. All of these data are now stored in the application database in R2. I would argue that there is no need to have the recovery mode set to "Full" on these databases containing the application. I mean, each time you import a modelstore, have a look at the database log. It will grow huge.

Inspired by a response from Kevin Kidder at the Community Site, I decided to try make a Stored Procedure that removes the modelstore elements inside the database. Getting rid of that old modelstore from RTM that now sits in the business database after successfully upgrading to R2 will reduce the size of the database with a couple of GB, so why not.

I found this nice script to remove all schema elements, and used is as a base for my script. I also initialized a new modelstore for the purpose of testing my script. Actually, the SQL scripts being used for creating a modelstore is available as resources in the AxUtilLib assembly. You can study them by using any tool that can revese engineer a .Net dll.

This procedure might not be flawless, so use it with care. Obviosuly; take a backup and test. By default the procedure will print out the SQL statements you need to run in order to remove all the modelstore elements in the database. You will have to explicitly pass "w" for it to execute the statements for you. You will have to decide if you want to evalute the output and run it manually, or let the procedure do it all.

Please report feedback and/or errors back to me. :-)

Here it is:

CREATE PROCEDURE [DBO].[CleanUpModelStore]
(
  @WORKTEST CHAR(1) = 't'  -- use 'w' to work and 't' to print
)
AS
/*-----------------------------------------------------------------------------------------
 
  Author : Tommy Skaue
  Date:    06.04.2013
 
  Description: Drops all modelstore elements.
  Usage and License: Free and at own risk
  Parameter: t = test/print out
             w = work and execute statements
    
  Report any errors or feedback to tommy@skaue.com.
  
  Inspired by the script made by Ranjith Kumar S
  http://ranjithk.com/2010/01/31/script-to-drop-all-objects-of-a-schema/
 
-------------------------------------------------------------------------------------------*/
BEGIN   
 
DECLARE @SQL VARCHAR(5000)
DECLARE @MODELSTOREELEMENTQUERY VARCHAR(2500)
DECLARE @MSG VARCHAR(500)
DECLARE @SCHEMANAME VARCHAR (50) = 'DBO'
 
IF OBJECT_ID('TEMPDB..#DROPMODELSTORE') IS NOT NULL DROP TABLE #DROPMODELSTORE
CREATE TABLE #DROPMODELSTORE
(
   ID INT IDENTITY(1,1)
  ,SQLSTATEMENT VARCHAR(5000)
 )
 
-- Common filter for ModelElements
SELECT @ModelStoreElementQuery = 
 'AND (T.NAME LIKE ''Model%'' OR 
  T.NAME IN (
   ''axIdAllocsTable'',
   ''AxIdAsk'',
   ''existingPaths'',
   ''Sources'', 
   ''newPaths'',
   ''SourceMerge'',
   ''SYSXPPASSEMBLY'', 
   ''ElementTypes'', 
   ''SchemaVersion'', 
   ''Layer'', 
   ''LayerVersioning'',
   ''GlobalFieldIdPool''))'
 
-- removes all the foreign keys that reference a PK in the target schema
 SELECT @SQL =
  'SELECT
       ''ALTER TABLE ''+SCHEMA_NAME(FK.SCHEMA_ID)+''.''+OBJECT_NAME(FK.PARENT_OBJECT_ID)+'' DROP CONSTRAINT ''+ FK.NAME
  FROM SYS.FOREIGN_KEYS FK
  JOIN SYS.TABLES T ON T.OBJECT_ID = FK.REFERENCED_OBJECT_ID
  WHERE T.SCHEMA_ID = SCHEMA_ID(''' + @SCHEMANAME + ''')
    AND FK.SCHEMA_ID <> T.SCHEMA_ID
    ' + @MODELSTOREELEMENTQUERY + '
  ORDER BY FK.NAME DESC'
 
 --IF @WORKTEST = 't' PRINT (@SQL )
 INSERT INTO #DROPMODELSTORE
 EXEC (@SQL)
  
 -- drop all default constraints, check constraints and Foreign Keys
 SELECT @SQL =
 'SELECT
       ''ALTER TABLE ''+SCHEMA_NAME(T.SCHEMA_ID)+''.''+OBJECT_NAME(FK.PARENT_OBJECT_ID)+'' DROP CONSTRAINT ''+ FK.[NAME]
  FROM SYS.OBJECTS FK
  JOIN SYS.TABLES T ON T.OBJECT_ID = FK.PARENT_OBJECT_ID
  WHERE T.SCHEMA_ID = SCHEMA_ID(''' + @SCHEMANAME + ''')
   AND FK.TYPE IN (''D'', ''C'', ''F'')' + @MODELSTOREELEMENTQUERY
  
 --IF @WORKTEST = 't' PRINT (@SQL )
 INSERT INTO #DROPMODELSTORE
 EXEC (@SQL)
 
 -- Common filter for ModelElements
SELECT @MODELSTOREELEMENTQUERY = 
 'SELECT OBJECT_ID FROM SYS.OBJECTS SO2 WHERE 
  SO2.PARENT_OBJECT_ID IN 
  (
   SELECT SO3.OBJECT_ID FROM SYS.OBJECTS SO3 WHERE SO3.TYPE IN (''U'') AND 
   (
    SO3.NAME LIKE ''Model%'' OR 
    SO3.NAME IN (
     ''axIdAllocsTable'',
     ''AxIdAsk'',
     ''existingPaths'',
     ''Sources'', 
     ''newPaths'',
     ''SourceMerge'',
     ''SYSXPPASSEMBLY'', 
     ''ElementTypes'', 
     ''SchemaVersion'', 
     ''Layer'', 
     ''LayerVersioning'',
     ''GlobalFieldIdPool'')
   ) 
  )
  OR
  (
   SO2.TYPE IN (''U'') AND 
   (
    SO2.NAME LIKE ''Model%'' OR 
    SO2.NAME IN (
     ''axIdAllocsTable'',
     ''AxIdAsk'',
     ''existingPaths'',
     ''Sources'', 
     ''newPaths'',
     ''SourceMerge'',
     ''SYSXPPASSEMBLY'', 
     ''ElementTypes'', 
     ''SchemaVersion'', 
     ''Layer'', 
     ''LayerVersioning'',
     ''GlobalFieldIdPool'')
   )
  ) 
  OR
  (
   SO2.TYPE IN (''V'') AND 
   (
   SO2.NAME LIKE ''SysModel%'' OR
   SO2.NAME LIKE ''Util%''     OR
   SO2.NAME LIKE ''Model%''    OR
   SO2.NAME IN (''ConfigurationKeys'', ''LicenseCodes'', ''Origins'',''SECURABLEOBJECT'') OR
   SO2.NAME IN (
    ''SECURITYENTRYPOINTLINK'',
    ''SECURITYPERMISSION'',
    ''SECURITYROLE'',
    ''SECURITYROLEEXPLODEDGRAPH'',
    ''SECURITYROLEPERMISSIONOVERRIDE'',
    ''SECURITYROLETASKGRANT'',
    ''SECURITYSUBROLE'',
    ''SECURITYSUBTASK'',
    ''SECURITYTASK'',
    ''SECURITYTASKENTRYPOINT'',
    ''SECURITYTASKEXPLODEDGRAPH'',
    ''SECURITYTASKPERMISSIONOVERRIDE''
    )
   )
  )
  OR
  (
   SO2.TYPE IN (''TR'') AND
   SO2.NAME IN (
    ''ModelSecurityPermission_CreateSecurable'',
    ''UpdateChangedBy'',
    ''SetInstalledAndChangedBy'',
    ''UpdateModelFromManifestChangedBy'',
    ''IO_Trig_INS_ModelElement'',
    ''IO_Trig_INS_ModelElements'',
    ''IO_Trig_Del_ModelElements'',
    ''ModelSecurityCommon_Insert''
    )
  )
  OR
  (
   SO2.TYPE IN (''P'') AND
   (
   SO2.NAME LIKE ''XI_%'' OR
   SO2.NAME LIKE ''XU_%''
   )  
  )
  OR
  (
   SO2.TYPE IN (''FN'', ''TF'',''IF'',''FS'',''FT'') AND
   SO2.NAME IN (
    ''CreateTemplateName'', 
    ''GetLayerMask'',
    ''GetNextAvailableFieldOrIndexAxId'',
    ''GetNextAvailableAxId'',
    ''GetAxIdHole'',
    ''IsAxIdExcluded'',
    ''SECURITYROLE_FUNC'',
    ''SECURITYTASK_FUNC''
   )
  )'
 -- drop all other objects in order  
 SELECT @SQL =  
 'SELECT 
 CASE 
  WHEN SO.TYPE=''PK'' THEN ''ALTER TABLE     ''+SCHEMA_NAME(SO.SCHEMA_ID)+''.''+OBJECT_NAME(SO.PARENT_OBJECT_ID)+'' DROP CONSTRAINT ''+ SO.NAME
  WHEN SO.TYPE=''U''  THEN ''DROP TABLE      ''+SCHEMA_NAME(SO.SCHEMA_ID)+''.''+ SO.[NAME]
  WHEN SO.TYPE=''V''  THEN ''DROP VIEW       ''+SCHEMA_NAME(SO.SCHEMA_ID)+''.''+ SO.[NAME]
  WHEN SO.TYPE=''P''  THEN ''DROP PROCEDURE  ''+SCHEMA_NAME(SO.SCHEMA_ID)+''.''+ SO.[NAME]         
  WHEN SO.TYPE=''TR'' THEN ''DROP TRIGGER    ''+SCHEMA_NAME(SO.SCHEMA_ID)+''.''+ SO.[NAME]
  WHEN SO.TYPE IN (''FN'', ''TF'',''IF'',''FS'',''FT'') 
                         THEN ''DROP FUNCTION   ''+SCHEMA_NAME(SO.SCHEMA_ID)+''.''+ SO.[NAME]
 END
FROM SYS.OBJECTS SO
 WHERE SO.SCHEMA_ID = SCHEMA_ID(''' + @SCHEMANAME + ''')
  AND SO.TYPE IN (''PK'', ''FN'', ''TF'', ''TR'', ''V'', ''U'', ''P'')
  AND SO.OBJECT_ID IN (' + @MODELSTOREELEMENTQUERY + ')
ORDER BY 
 CASE 
  WHEN TYPE = ''PK'' THEN 1
        WHEN TYPE IN (''FN'', ''TF'', ''P'',''IF'',''FS'',''FT'') 
                     THEN 2
        WHEN TYPE = ''TR'' THEN 3
        WHEN TYPE = ''V''  THEN 4
        WHEN TYPE = ''U''  THEN 5
        ELSE 6
    END'
 
--IF @WORKTEST = 't' PRINT (@SQL )
INSERT INTO #DROPMODELSTORE
EXEC (@SQL)
 
DECLARE @ID INT, @STATEMENT VARCHAR(2000)
DECLARE STATEMENT_CURSOR CURSOR
FOR SELECT SQLSTATEMENT
    FROM #DROPMODELSTORE
    ORDER BY ID ASC
    
 OPEN STATEMENT_CURSOR
 FETCH STATEMENT_CURSOR INTO @STATEMENT
 
 WHILE (@@FETCH_STATUS = 0) BEGIN
  IF @WorkTest = 't' 
   PRINT (@statement)
  ELSE
   BEGIN
    PRINT (@statement)
    EXEC(@statement)
   END
  
  FETCH STATEMENT_CURSOR INTO @STATEMENT    
 END
 
 CLOSE STATEMENT_CURSOR
 DEALLOCATE STATEMENT_CURSOR
 
 PRINT '------- ALL - DONE -------'   
END
GO
Hope this helps someone.