Important Scripts and Procedures for SQL DBA
Imporant Scripts & Procedures for SQL DBA
Daily help for DBA task.
To know logins - sp_helpuser
-----------------------------------------
To shrink tempdb
dbcc freeproccache()
Shrink db
-----------------------------------------------------------------------------------
To script out logins - sp_help_revlogin
------------------------------------------------------------------------------------
To change db owner - sp_changedbowner 'sa'
-------------------------------------------------------------------------------------
Step 1: Check the databases that does not have sa account as the owner
SELECT name AS DBName, suser_sname(owner_sid) AS DBOwner
FROM sys.databases
WHERE suser_sname(owner_sid) <> 'sa'
Step 2: Generate the scripts to make sa account as owner for all the databases
SELECT 'ALTER AUTHORIZATION ON DATABASE::' + QUOTENAME(name) + ' TO [sa];'
from sys.databases
where name not in ('master', 'model', 'tempdb', 'msdb')
AND suser_sname(owner_sid) <> 'sa'
Step 3: Execute the result set from step 2 above to change the ownership to sa
--Sample result set from step2 above
ALTER AUTHORIZATION ON DATABASE::[AdventureWorksLT2012] TO [sa];
ALTER AUTHORIZATION ON DATABASE::[Northwind] TO [sa];
ALTER AUTHORIZATION ON DATABASE::[Pubs] TO [sa];
-------------------------------------------
Update usage - DBCC UPDATEUSAGE (0);
GO
--------------------------------------------
Consistency check -DBCC CHECKDB
---------------------------------------
Update statistics - sp_updatestats
-----------------------------------------
Orphan users -
First, make sure that this is the problem. This will lists the orphaned users:
EXEC sp_change_users_login 'Report'
If you already have a login id and password for this user, fix it by doing:
EXEC sp_change_users_login 'Auto_Fix', 'user'
If you want to create a new login id and password for this user, fix it by doing:
EXEC sp_change_users_login 'Auto_Fix', 'user', 'login', 'password'
--------------------------------------------
Run this on Sunday Midnight to know the available dbs and servers.
sp_helpdb
sp_fixeddrives
------------------------------------------------------------------
To find the connection to a db:
USE [master];
DECLARE @kill varchar(8000) = '';
SELECT @kill = @kill + 'kill ' + CONVERT(varchar(5), session_id) + ';'
FROM sys.dm_exec_sessions
WHERE database_id = db_id('MyDB')
EXEC(@kill);
-------------------------------------------------------------------------
--Script to find the backup growth in days
--Provide database name @dbname
--Proide number days @days
declare @dbname nvarchar(1024)
declare @days int
set @dbname ='ibmdba'
set @days =180;
--Daily Report
WITH TempTable(Row,database_name,backup_start_date,type,GB)
as
(
select
ROW_NUMBER() OVER(order by backup_start_date) as Row,
database_name,
backup_start_date, type,
cast(backup_size/1024/1024/1024 as decimal(10,2)) Gb
from msdb..backupset
where
--L->Logs, D->Full backup, I->Diffrential
type IN('I','D','L') and
database_name=@dbname and
backup_start_date>getdate()-@days
)
select
A.database_name,
A.backup_start_date,
A.type,
A.GB as daily_backup,
A.GB - B.GB as increment_GB
from TempTable A left join TempTable B on A.Row=B.Row+1
order by database_name,backup_start_date
-------------------------------------------------------------------------
--Truncate all the tables in a Database in SQL Server
-- Drop Temp Tables if Exists
IF OBJECT_ID('tempdb..#DropConstraint') IS NOT NULL
DROP TABLE #DropConstraint
IF OBJECT_ID('tempdb..#CreateConstraint') IS NOT NULL
DROP TABLE #CreateConstraint
IF OBJECT_ID('tempdb..#TempTruncateTables') IS NOT NULL
DROP TABLE #TempTruncateTables
-- Drop Constraint Script Save in #DropConstraint Temp Table
SELECT 'ALTER TABLE ' + '['
+ Schema_name(o.schema_id) + '].['
+ OBJECT_NAME(FK.parent_object_id) + ']'
+ ' DROP CONSTRAINT ' + '[' + FK.name + ']' AS DropConstraintQuery
INTO #DropConstraint
FROM sys.foreign_keys AS FK
INNER JOIN sys.foreign_key_columns AS FKC
ON FK.OBJECT_ID = FKC.constraint_object_id
INNER JOIN sys.objects O
ON O.OBJECT_ID = FKC.parent_object_id
-- Create Constraint Script Save in #CreateConstraint Temp Table
SELECT 'ALTER TABLE ' + '['
+ Schema_name(o.schema_id) + '].' + '['
+ OBJECT_NAME(FK.parent_object_id) + ']'
+ ' ADD CONSTRAINT ' + '[' + FK.name
+ '] Foreign Key (['
+ (SELECT name
FROM sys.columns c
WHERE c.OBJECT_ID = FKC.parent_object_id
AND c.column_id = FKC.parent_column_id)
+ ']) REFERENCES ' + '['
+ Schema_name(o.schema_id) + '].['
+ (SELECT name
FROM sys.objects o
WHERE OBJECT_ID = FKC.referenced_object_id)
+ '] (['
+ (SELECT name
FROM sys.columns c
WHERE c.OBJECT_ID = FKC.referenced_object_id
AND c.column_id = FKC.referenced_column_id)
+ '])' AS CreateConstraintQuery
INTO #CreateConstraint
FROM sys.foreign_keys AS FK
INNER JOIN sys.foreign_key_columns AS FKC
ON FK.OBJECT_ID = FKC.constraint_object_id
INNER JOIN sys.objects o
ON FKC.parent_object_id = o.OBJECT_ID
-- Build Truncate Statement for all the tables and save into #TempTruncateTables
SELECT 'Truncate table ' + Schema_name(schema_id)
+ '.' + name AS TruncateTableQuery
INTO #TempTruncateTables
FROM sys.tables
WHERE TYPE = 'U'
AND is_ms_shipped = 0
GO
-- Drop Constraints
DECLARE @DropConstraintQuery AS VARCHAR(4000)
DECLARE DropConstraintCur CURSOR FOR
SELECT DropConstraintQuery
FROM #DropConstraint
OPEN DropConstraintCur
FETCH Next FROM DropConstraintCur
INTO @DropConstraintQuery
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @SQL VARCHAR(MAX)=NULL
SET @SQL=@DropConstraintQuery
EXEC (@SQL)
PRINT ' Query ::' + @DropConstraintQuery
+ 'Completed'
FETCH Next FROM DropConstraintCur INTO @DropConstraintQuery
END
CLOSE DropConstraintCur
DEALLOCATE DropConstraintCur
GO-- Truncate tables
DECLARE @TempTruncateTablesCur AS VARCHAR(4000)
DECLARE TempTruncateTablesCur CURSOR FOR
SELECT TruncateTableQuery
FROM #TempTruncateTables
OPEN TempTruncateTablesCur
FETCH Next FROM TempTruncateTablesCur
INTO @TempTruncateTablesCur
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @SQL VARCHAR(MAX)=NULL
SET @SQL=@TempTruncateTablesCur
EXEC (@SQL)
PRINT ' Query ::' + @TempTruncateTablesCur
+ 'Completed'
FETCH Next FROM TempTruncateTablesCur INTO @TempTruncateTablesCur
END
CLOSE TempTruncateTablesCur
DEALLOCATE TempTruncateTablesCur
GO
-- Create Constraint After Truncate
DECLARE @CreateConstraintQuery AS VARCHAR(4000)
DECLARE CreateConstraintQueryCur CURSOR FOR
SELECT CreateConstraintQuery
FROM #CreateConstraint
OPEN CreateConstraintQueryCur
FETCH Next FROM CreateConstraintQueryCur
INTO @CreateConstraintQuery
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @SQL VARCHAR(MAX)=NULL
SET @SQL=@CreateConstraintQuery
EXEC (@SQL)
PRINT ' Query ::' + @CreateConstraintQuery
+ 'Completed'
FETCH Next FROM CreateConstraintQueryCur INTO @CreateConstraintQuery
END
CLOSE CreateConstraintQueryCur
DEALLOCATE CreateConstraintQueryCur
GO
----------------------------------------------------------------------------------------------------
Delete table content and maintain structure --- Change db name..
USE dbname
GO
-- disable all constraints
EXEC sp_MSForEachTable "ALTER TABLE ? NOCHECK CONSTRAINT all"
SET ROWCOUNT 0
-- delete data in all tables
EXEC sp_MSForEachTable "DELETE FROM ?"
-- enable all constraints
---------------------------------------------------------------------------------
To validate above ---
EXEC sp_MSForEachTable "select count(*) FROM ?"
It should show 0's
--------------------------------------------------------------------------
exec sp_MSForEachTable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"
USE [dbname
GO
-- disable all constraints
EXEC sp_MSForEachTable "ALTER TABLE ? NOCHECK CONSTRAINT all"
SET ROWCOUNT 0
-- delete data in all tables
EXEC sp_MSForEachTable "DELETE FROM ?"
-- enable all constraints
exec sp_MSForEachTable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"
---------------------------------------------------------------------
Set auto growth to all databases
DECLARE @dbname VARCHAR(50) = db_name() --Current DB
DECLARE @filename VARCHAR(100) --DB file name
DECLARE @SqlCmd VARCHAR(1000) --SQL Command
DECLARE db_cursor CURSOR FOR
Select Name
From sys.database_files
--Where [type] = 0 --Data files only
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @filename
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SqlCmd = 'ALTER DATABASE ' + @dbname + '
MODIFY FILE (NAME = N'''+@filename+''', FILEGROWTH = 100)' --100MB
EXEC (@SqlCmd)
FETCH NEXT FROM db_cursor INTO @filename
END
CLOSE db_cursor
DEALLOCATE db_cursor
-----------------------------------------------------------------------
IF EXISTS(SELECT name FROM sys.sysobjects WHERE name = N'ConfigAutoGrowth' AND xtype='U')
DROP TABLE ConfigAutoGrowth
GO
CREATE TABLE DBO.ConfigAutoGrowth
(
iDBID INT,
sDBName SYSNAME,
vFileName VARCHAR(max),
vGrowthOption VARCHAR(12)
)
PRINT 'Table ConfigAutoGrowth Created'
GO
-- Inserting data into staging table
INSERT INTO DBO.ConfigAutoGrowth
SELECT
SD.database_id,
SD.name,
SF.name,
--sf.fileid,
--SUSER_NAME(owner_sid),
--recovery_model_desc,
CASE SF.status & 0x100000
WHEN 1048576 THEN 'Percentage'
WHEN 0 THEN 'MB'
END AS 'GROWTH Option'
FROM SYS.SYSALTFILES SF
JOIN
SYS.DATABASES SD
ON
SD.database_id = SF.dbid
GO
-- Dynamically alters the file to set auto growth option to fixed mb
DECLARE @name VARCHAR ( max ) -- Database Name
DECLARE @dbid INT -- DBID
DECLARE @vFileName VARCHAR ( max ) -- Logical file name
DECLARE @vGrowthOption VARCHAR ( max ) -- Growth option
DECLARE @Query VARCHAR(max) -- Variable to store dynamic sql
DECLARE db_cursor CURSOR FOR
SELECT
idbid,sdbname,vfilename,vgrowthoption
FROM configautogrowth
WHERE sdbname NOT IN ( 'master' ,'msdb' )
--AND vGrowthOption = 'Percentage' or 'Mb'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @dbid,@name,@vfilename,@vgrowthoption
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Changing AutoGrowth option for database:- '+ UPPER(@name)
SET @Query = 'ALTER DATABASE '+ @name +' MODIFY FILE (NAME = '+@vFileName+',FILEGROWTH = 100MB)'
EXECUTE(@Query)
FETCH NEXT FROM db_cursor INTO @dbid,@name,@vfilename,@vgrowthoption
END
CLOSE db_cursor -- Closing the curson
DEALLOCATE db_cursor -- deallocating the cursor
GO
-- Querying system views to see if the changes are applied
DECLARE @SQL VARCHAR(8000), @sname VARCHAR(3)
SET @SQL=' USE ?
SELECT ''?'' [Dbname]
,[name] [Filename]
,CASE is_percent_growth
WHEN 1 THEN CONVERT(VARCHAR(5),growth)+''%''
ELSE CONVERT(VARCHAR(20),(growth/128))+'' MB''
END [Autogrow_Value]
,CASE max_size
WHEN -1 THEN CASE growth
WHEN 0 THEN CONVERT(VARCHAR(30),''Restricted'')
ELSE CONVERT(VARCHAR(30),''Unlimited'') END
ELSE CONVERT(VARCHAR(25),max_size/128)
END [Max_Size]
FROM ?.sys.database_files'
IF EXISTS(SELECT 1 FROM tempdb..sysobjects WHERE name='##Fdetails')
DROP TABLE ##Fdetails
CREATE TABLE ##Fdetails (Dbname VARCHAR(50),Filename VARCHAR(50),Autogrow_Value VARCHAR(15),Max_Size VARCHAR(30))
INSERT INTO ##Fdetails
EXEC sp_msforeachdb @SQL
SELECT * FROM ##Fdetails ORDER BY Dbname
--Dropping the staging table
DROP TABLE ConfigAutoGrowth
GO
--------------------------------------------------------------------------
Reason for big log file
select name, log_reuse_wait_desc from sys.databases
dbcc SQLperf(logspace)
---------------------------------------------------------------
Backup all databases at once
DECLARE @name VARCHAR(50) -- database name
DECLARE @path VARCHAR(256) -- path for backup files
DECLARE @fileName VARCHAR(256) -- filename for backup
DECLARE @fileDate VARCHAR(20) -- used for file name
-- specify database backup directory
SET @path = 'U:\msabackuptest\'
-- specify filename format
SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112)
DECLARE db_cursor CURSOR READ_ONLY FOR
SELECT name
FROM master.dbo.sysdatabases
WHERE name NOT IN ('master','model','msdb','tempdb') -- exclude these databases
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @name
WHILE @@FETCH_STATUS = 0
BEGIN
SET @fileName = @path + @name + '_' + @fileDate + '.BAK'
BACKUP DATABASE @name TO DISK = @fileName
FETCH NEXT FROM db_cursor INTO @name
END
CLOSE db_cursor
DEALLOCATE db_cursor
-------------------------------------------------------------------------
--Backup missing db backups
DECLARE @Baksql VARCHAR(8000)
DECLARE @CurrentDirectoryPath nvarchar(max)
DECLARE @BackupDate varchar(100)
DECLARE @BaseFolderPath VARCHAR(256)
DECLARE @name SYSNAME
DECLARE @FullPath VARCHAR(200)
SET @BackupDate = REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR,GETDATE(),120),'-',''),':',''),' ','_')
CREATE TABLE #TempTable(
Value nvarchar(100),
BPath nvarchar (250))
insert into #TempTable EXECUTE [master].dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer', N'BackupDirectory'
set @CurrentDirectoryPath = (select Bpath from #TempTable)
--print (@CurrentDirectoryPath)
Drop table #Temptable
Set @BaseFolderPath=@CurrentDirectoryPath + '\' + REPLACE(CAST(SERVERPROPERTY('servername') AS nvarchar),'\','$')
--print @path
DECLARE db_cursor CURSOR FOR SELECT NAME FROM sys.databases
WHERE NAME != 'TEMPDB'
AND NAME NOT IN ( SELECT DISTINCT database_name FROM msdb..backupset
WHERE backup_start_date > DATEADD(DAY,-8,GETDATE())
AND TYPE = 'D' )
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @name
WHILE @@FETCH_STATUS = 0
BEGIN
SET @FullPath = @BaseFolderPath+'\'+@name+'\FULL'
EXEC MASTER.dbo.xp_create_subdir @FullPath
SET @Baksql = 'BACKUP DATABASE ['+@name+'] TO DISK = '''+@FullPath+ '\'+@name+ '_FullBackup_'+@BackupDate+'.bak'' WITH INIT;'
Print @Baksql
EXEC (@Baksql)
FETCH NEXT FROM db_cursor INTO @name
END
CLOSE db_cursor
DEALLOCATE db_cursor
---------------------------------------------------------------------------------------------------------------------------
--robocopy source destination parameter
robocopy \\SGPPDBA005\g$\MSSQL_SQLWELDC\Backup\SGPPDBA005$SQLWELDC\WeldConsole_L9ZR\FULL\New G:\ /e
--------------------------------------------------------------------------------------------------------------------------
--Database level permission copy
DECLARE
@sql VARCHAR(2048)
,@sort INT
DECLARE tmp CURSOR FOR
/*********************************************/
/********* DB CONTEXT STATEMENT *********/
/*********************************************/
SELECT '-- [-- DB CONTEXT --] --' AS [-- SQL STATEMENTS --],
1 AS [-- RESULT ORDER HOLDER --]
UNION
SELECT 'USE' + SPACE(1) + QUOTENAME(DB_NAME()) AS [-- SQL STATEMENTS --],
1 AS [-- RESULT ORDER HOLDER --]
UNION
SELECT '' AS [-- SQL STATEMENTS --],
2 AS [-- RESULT ORDER HOLDER --]
UNION
/*********************************************/
/********* DB USER CREATION *********/
/*********************************************/
SELECT '-- [-- DB USERS --] --' AS [-- SQL STATEMENTS --],
3 AS [-- RESULT ORDER HOLDER --]
UNION
SELECT 'IF NOT EXISTS (SELECT [name] FROM sys.database_principals WHERE [name] = ' + SPACE(1) + '''' + [name] + '''' + ') BEGIN CREATE USER ' + SPACE(1) + QUOTENAME([name]) + ' FOR LOGIN ' + QUOTENAME([name]) + ' WITH DEFAULT_SCHEMA = ' + QUOTENAME([default_schema_name]) + SPACE(1) + 'END; ' AS [-- SQL STATEMENTS --],
4 AS [-- RESULT ORDER HOLDER --]
FROM sys.database_principals AS rm
WHERE [type] IN ('U', 'S', 'G') -- windows users, sql users, windows groups
UNION
/*********************************************/
/********* DB ROLE PERMISSIONS *********/
/*********************************************/
SELECT '-- [-- DB ROLES --] --' AS [-- SQL STATEMENTS --],
5 AS [-- RESULT ORDER HOLDER --]
UNION
SELECT 'EXEC sp_addrolemember @rolename ='
+ SPACE(1) + QUOTENAME(USER_NAME(rm.role_principal_id), '''') + ', @membername =' + SPACE(1) + QUOTENAME(USER_NAME(rm.member_principal_id), '''') AS [-- SQL STATEMENTS --],
6 AS [-- RESULT ORDER HOLDER --]
FROM sys.database_role_members AS rm
WHERE USER_NAME(rm.member_principal_id) IN (
--get user names on the database
SELECT [name]
FROM sys.database_principals
WHERE [principal_id] > 4 -- 0 to 4 are system users/schemas
and [type] IN ('G', 'S', 'U') -- S = SQL user, U = Windows user, G = Windows group
)
--ORDER BY rm.role_principal_id ASC
UNION
SELECT '' AS [-- SQL STATEMENTS --],
7 AS [-- RESULT ORDER HOLDER --]
UNION
/*********************************************/
/********* OBJECT LEVEL PERMISSIONS *********/
/*********************************************/
SELECT '-- [-- OBJECT LEVEL PERMISSIONS --] --' AS [-- SQL STATEMENTS --],
8 AS [-- RESULT ORDER HOLDER --]
UNION
SELECT CASE
WHEN perm.state <> 'W' THEN perm.state_desc
ELSE 'GRANT'
END
+ SPACE(1) + perm.permission_name + SPACE(1) + 'ON ' + QUOTENAME(SCHEMA_NAME(obj.schema_id)) + '.' + QUOTENAME(obj.name) --select, execute, etc on specific objects
+ CASE
WHEN cl.column_id IS NULL THEN SPACE(0)
ELSE '(' + QUOTENAME(cl.name) + ')'
END
+ SPACE(1) + 'TO' + SPACE(1) + QUOTENAME(USER_NAME(usr.principal_id)) COLLATE database_default
+ CASE
WHEN perm.state <> 'W' THEN SPACE(0)
ELSE SPACE(1) + 'WITH GRANT OPTION'
END
AS [-- SQL STATEMENTS --],
9 AS [-- RESULT ORDER HOLDER --]
FROM
sys.database_permissions AS perm
INNER JOIN
sys.objects AS obj
ON perm.major_id = obj.[object_id]
INNER JOIN
sys.database_principals AS usr
ON perm.grantee_principal_id = usr.principal_id
LEFT JOIN
sys.columns AS cl
ON cl.column_id = perm.minor_id AND cl.[object_id] = perm.major_id
--WHERE usr.name = @OldUser
--ORDER BY perm.permission_name ASC, perm.state_desc ASC
UNION
SELECT '' AS [-- SQL STATEMENTS --],
10 AS [-- RESULT ORDER HOLDER --]
UNION
/*********************************************/
/********* DB LEVEL PERMISSIONS *********/
/*********************************************/
SELECT '-- [--DB LEVEL PERMISSIONS --] --' AS [-- SQL STATEMENTS --],
11 AS [-- RESULT ORDER HOLDER --]
UNION
SELECT CASE
WHEN perm.state <> 'W' THEN perm.state_desc --W=Grant With Grant Option
ELSE 'GRANT'
END
+ SPACE(1) + perm.permission_name --CONNECT, etc
+ SPACE(1) + 'TO' + SPACE(1) + '[' + USER_NAME(usr.principal_id) + ']' COLLATE database_default --TO <user name>
+ CASE
WHEN perm.state <> 'W' THEN SPACE(0)
ELSE SPACE(1) + 'WITH GRANT OPTION'
END
AS [-- SQL STATEMENTS --],
12 AS [-- RESULT ORDER HOLDER --]
FROM sys.database_permissions AS perm
INNER JOIN
sys.database_principals AS usr
ON perm.grantee_principal_id = usr.principal_id
--WHERE usr.name = @OldUser
WHERE [perm].[major_id] = 0
AND [usr].[principal_id] > 4 -- 0 to 4 are system users/schemas
AND [usr].[type] IN ('G', 'S', 'U') -- S = SQL user, U = Windows user, G = Windows group
UNION
SELECT '' AS [-- SQL STATEMENTS --],
13 AS [-- RESULT ORDER HOLDER --]
UNION
SELECT '-- [--DB LEVEL SCHEMA PERMISSIONS --] --' AS [-- SQL STATEMENTS --],
14 AS [-- RESULT ORDER HOLDER --]
UNION
SELECT CASE
WHEN perm.state <> 'W' THEN perm.state_desc --W=Grant With Grant Option
ELSE 'GRANT'
END
+ SPACE(1) + perm.permission_name --CONNECT, etc
+ SPACE(1) + 'ON' + SPACE(1) + class_desc + '::' COLLATE database_default --TO <user name>
+ QUOTENAME(SCHEMA_NAME(major_id))
+ SPACE(1) + 'TO' + SPACE(1) + QUOTENAME(USER_NAME(grantee_principal_id)) COLLATE database_default
+ CASE
WHEN perm.state <> 'W' THEN SPACE(0)
ELSE SPACE(1) + 'WITH GRANT OPTION'
END
AS [-- SQL STATEMENTS --],
15 AS [-- RESULT ORDER HOLDER --]
from sys.database_permissions AS perm
inner join sys.schemas s
on perm.major_id = s.schema_id
inner join sys.database_principals dbprin
on perm.grantee_principal_id = dbprin.principal_id
WHERE class = 3 --class 3 = schema
ORDER BY [-- RESULT ORDER HOLDER --]
OPEN tmp
FETCH NEXT FROM tmp INTO @sql, @sort
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @sql
FETCH NEXT FROM tmp INTO @sql, @sort
END
CLOSE tmp
DEALLOCATE tmp
----------------------------------------------------------------------------------------------------------
Comments
Post a Comment