Alter Schema Transfer fails even when I'm a db-owner - sql-server

I asked this on GitHub here, but I think we can turn into a more generic question to get some helpful ideas faster.
First, I ran the script here, which shows that I am a member of the db-owner group.
This is on the install script for an older tool called AutoEdit. Despite the issue below, I'm able to turn on AutoAudit for specific tables and it's working okay.
The supplied script stores a few stored proc under a schema with my username, then tries to transfer them to the "audit" schema later. The AutoEdit concepts and stored procs are all actually working, but now I need to clean it up and put in another environment.
Error is:
Cannot find the object 'pAutoAudit', because it does not exist or you do not have permission.
It created the stored proc as
"Corp\myuserid.pAutoAudit"
I added two print statements to help debug:
-- This is the line of code (the EXEC below) that is causing the issue:
print Concat('#AuditSchema=', #AuditSchema)
SET #Sql = 'ALTER SCHEMA ' + quotename(#AuditSchema) + ' TRANSFER dbo.pAutoAudit'
print Concat('#Sql=', #Sql)
EXEC (#Sql)
Above shows:
#AuditSchema=Audit
#Sql=ALTER SCHEMA [Audit] TRANSFER dbo.pAutoAudit
The schema Audit exists, and it has one stored proc in it: pAutoAuditArchive.
I have added a related question here: SQL Server setting that changes schema from dbo

To fix this specific problem, I saw the correct syntax from the Lauren answer here, like this:
ALTER SCHEMA NewSchema TRANSFER [OldSchema].[TableName]
The following script temporarily fixes the issue. I have to study more how to fix the original script if indeed it has an error:
use myDbname
Declare #AuditSchema varchar(32)
Declare #OldSchema varchar(32)
Declare #SQL varchar(max)
Set #AuditSchema = 'Audit'
Set #OldSchema = 'Corp\myuser'
-- ALTER SCHEMA NewSchema TRANSFER [OldSchema].[TableName]
SET #SQL = 'ALTER SCHEMA ' + quotename(#AuditSchema) + ' TRANSFER ' + quotename(#OldSchema) + '.pAutoAudit'
print Concat('#SQL=', #SQL)
EXEC (#SQL)
SET #SQL = 'ALTER SCHEMA ' + quotename(#AuditSchema) + ' TRANSFER ' + quotename(#OldSchema) + '.pAutoAuditAll'
EXEC (#SQL)
SET #SQL = 'ALTER SCHEMA ' + quotename(#AuditSchema) + ' TRANSFER ' + quotename(#OldSchema) + '.pAutoAuditDrop'
EXEC (#SQL)
SET #SQL = 'ALTER SCHEMA ' + quotename(#AuditSchema) + ' TRANSFER ' + quotename(#OldSchema) + '.pAutoAuditDropAll'
EXEC (#SQL)
SET #SQL = 'ALTER SCHEMA ' + quotename(#AuditSchema) + ' TRANSFER ' + quotename(#OldSchema) + '.pAutoAuditRebuild'
EXEC (#SQL)
SET #SQL = 'ALTER SCHEMA ' + quotename(#AuditSchema) + ' TRANSFER ' + quotename(#OldSchema) + '.pAutoAuditRebuildAll'
EXEC (#SQL)
SET #SQL = 'ALTER SCHEMA ' + quotename(#AuditSchema) + ' TRANSFER ' + quotename(#OldSchema) + '.pAutoAuditSetTriggerState'
EXEC (#SQL)
SET #SQL = 'ALTER SCHEMA ' + quotename(#AuditSchema) + ' TRANSFER ' + quotename(#OldSchema) + '.pAutoAuditSetTriggerStateAll'
EXEC (#SQL)
However, to fix the "AutoEdit" script referenced in the question, the problem can be corected by adding this code to AutoEdit or running it before AutoEdit. Apparently, it assumes you are in the "dbo" default_schema. You might have to set the value back to the original value if needed after the script.
DECLARE #SQL2 VARCHAR(max)
SET #SQL2 = 'ALTER USER ' + quotename(current_user) + ' WITH DEFAULT_SCHEMA = dbo'
print Concat('#SQL2=', #SQL2)
exec (#SQL2)
The better fix for when I moved it to another environment, was to change all the "Create Proc xxxx" to "Create Proc dbo.xxxx".

Related

How do I format a url string to pass into stored procedure

I created the below stored procedure in sql server that requires 3 parameters: Date, URL, & Table Name:
ALTER PROCEDURE [stg].[usp_Delete_Data]
(#DateLookBack date,
#siteUrl nvarchar(100),
#tableName SYSNAME)
AS
BEGIN
SET QUOTED_IDENTIFIER ON
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'DELETE FROM ' + CONCAT('[stg].[',#tableName,']') +
'WHERE date = ' + FORMAT(#DateLookBack, 'yyyyMMdd') +
'AND siteUrl = ' + #siteUrl
EXEC sp_executesql #Sql
END
When I pass in a url, like 'https://stackoverflow.com', I get an error message:
Incorrect syntax near 'https:'
How do I format the url string so that it can pass into the query successfully?
I'd strongly advise against this method. Having so many tables of the same structure that it requires a single procedure where the table name is dynamic is a code smell in itself.
If you must use dynamic sql though, at least use parameters as much as possible and only inject your table name, i.e.
SET #sql = CONCAT(N'DELETE FROM [stg].' QUOTENAME(#tableName),
' WHERE Date = #Date AND SiteUrl = #SiteUrl;');
EXECUTE sp_executesql #sql, N'#Date date, #SiteUrl nvarchar(100)', #date, #SiteUrl;
To find such issue, all you need is to PRINT the query before you use it! You could examine the query which is executed, if you printed it first.
Replace the commend Exec sp_executesql #Sql with the command PRINT #Sql and examine the query you get.
In your case, after you do it, then when you execute the procedure using the following command, then I can see all the issues.
EXECUTE dbo.[usp_Delete_Data]
#DateLookBack = '2022-02-27' ,#siteUrl = 'https://stackoverflow.com' , #tableName = 'c'
GO
The printed text which we get is: DELETE FROM [stg].[c]WHERE date = 20220227and siteUrl = https://stackoverflow.com
Now we can go over the errors (yes there are multiple errors here) one by one
(1) Notice that the 'WHERE date = ' missing a space before the "where" which might combine the word "where" with the table name that comes before it. You need to add space like ' WHERE date = '
same with the part after the and siteUrl - missing space before the and
(2) Notice this part: siteUrl = https://stackoverflow.com. in the query you are building you do not have quotation marks around the text of the URL => this lead to the error message.
instead of 'and siteUrl = ' + #siteUrl it should be: 'and siteUrl = ''' + #siteUrl + ''''
(3) same issue you have with the date - you do not have quotation marks around the text of the date
instead of ' WHERE date = ' + format(#DateLookBack,'yyyyMMdd') it should be ' WHERE date = ''' + format(#DateLookBack,'yyyyMMdd') + ''''
So, after adding these fixes, you get the following SP (I use PRING instead of execute but you can change this back)
CREATE OR ALTER PROCEDURE [usp_Delete_Data] (
#DateLookBack date,#siteUrl nvarchar(100), #tableName SYSNAME
) AS BEGIN
SET QUOTED_IDENTIFIER ON
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'DELETE FROM ' + CONCAT('[stg].[',#tableName,']')
--+ ' WHERE date = ' + format(#DateLookBack,'yyyyMMdd')
+ ' WHERE date = ''' + format(#DateLookBack,'yyyyMMdd') + ''''
+ ' and siteUrl = ''' + #siteUrl + ''''
--+ 'and siteUrl = ' + #siteUrl
PRINT #Sql
--Exec sp_executesql #Sql
END
and now if I execute the same query
EXECUTE dbo.[usp_Delete_Data]
#DateLookBack = '2022-02-27' ,#siteUrl = 'https://stackoverflow.com' , #tableName = 'c'
GO
It will print something that looks like:
DELETE FROM [stg].[c] WHERE date = '20220227'and siteUrl = 'https://stackoverflow.com'
BUT! NOW WE CAN GO TO THE MOST PROBLEMATIC ISSUE! Your procedure is open to SQL Injection! You should NOT use such code.
You should use parameters whenever you can when you use sp_executesql and not combine text text. Read the documentation of sp_executesql on how to use parameters as input: https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql

Invalid Column name on parameter input

Really wrecking my head here and as with many sql mess up I know it is probably something silly and stupid but I just cant seem to get it to work.
I have a stored procedure which is this..
ALTER PROCEDURE [dbo].[RETURN_DATA](#TABLE_param VARCHAR(7),#COUNTRY_param VARCHAR(2),#FILEDATE_param int,#TTKT_param VARCHAR(6))
AS
BEGIN
SET NOCOUNT ON;
SELECT #SQL = 'Select * from ' + #TABLE_param + ' WHERE COUNTRY = ' + #COUNTRY_param + ' AND MONTH(Fil_Dte) = ' + cast(#FILEDATE_param as varchar(20)) + ' AND TRNN = '+ #TKTT_param
EXECUTE(#SQL)
END
I'm using it in a vb.net windows form app so applying the parameters there. But trying to run it in SSMS with this
exec RETURN_DATA #COUNTRY_param='GB',#FILEDATE_param=4,#TABLE_param='table30',#TTKT_param='000000'
Returns the error
Invalid column name 'GB'. which i find strange as I never called for a column called GB but called for rows with GB in the column COUNTRY in my where clause?
I know this hopefully is a simple fix so any help would be greatly appreciated and also even if you think theres a better way to go about writing the SP!
Thanks in advance guys.
I'd recommend parameterising the SQL which will guard against SQL injection and you don't have to worry about escaping quotes as below
ALTER PROCEDURE [dbo].[RETURN_DATA](#TABLE_param VARCHAR(7),#COUNTRY_param VARCHAR(2),#FILEDATE_param int,#TTKT_param VARCHAR(6))
AS
BEGIN
SET NOCOUNT ON;
SELECT #SQL = 'Select * from ' + #TABLE_param + ' WHERE COUNTRY = ''' + #COUNTRY_param + ''' AND MONTH(Fil_Dte) = ' + cast(#FILEDATE_param as varchar(20)) + ' AND TRNN = '''+ #TKTT_param +''''
EXECUTE(#SQL)
END
Use sp_executesql to run dynamic sql
DECLARE #SQL NVARCHAR (4000);
SET #SQL = '
Select *
from ' + QUOTENAME(#TABLE_param) + '
WHERE COUNTRY = #COUNTRY_param
AND MONTH(Fil_Dte) = #FILEDATE_param
AND TRNN = #TTKT_param
';
EXEC sp_executesql #SQL,
N'#COUNTRY_param VARCHAR(2), #FILEDATE_param int, #TTKT_param VARCHAR(6)',
#COUNTRY_param, #FILEDATE_param, #TTKT_param;
sp_executesql

How to create a table inside of recently created database with dynamic sql?

I want to populate a set of tables and procedures into new databases programmatically.
Then I prepared a Initialize Script and I will use it inside a procedure who must created new databases.
I tried with a simple example and didn't work:
CREATE PROCEDURE PROCEDURETOREPLICATECOMMONSCHEMA
#databaseName NVARCHAR(40)
AS
BEGIN
DECLARE #sqlCreation NVARCHAR(MAX);
SET #sqlCreation = '
USE MASTER;
EXEC(''CREATE DATABASE ' + #databaseName + ''');
EXEC(''USE ' + #databaseName + ''');
CREATE TABLE Testing
(
TestPk int,
TestDescription nvarchar(80)
);
';
PRINT #sqlCreation;
EXEC sp_executesql #sqlCreation;
END
GO
When I execute this procedure, it creates the table Testing inside master database instead of TestDatabase1.
EXEC PROCEDURETOREPLICATECOMMONSCHEMA 'TestDatabase1'
GO
First create the database, then create the table (two separate statements):
CREATE PROCEDURE PROCEDURETOREPLICATECOMMONSCHEMA
#databaseName NVARCHAR(40)
AS
BEGIN
DECLARE #sqlCreation NVARCHAR(MAX);
SET #sqlCreation = 'USE MASTER;
EXEC(''CREATE DATABASE ' + #databaseName + ''');';
EXEC sp_executesql #sqlCreation;
SET #sqlCreation = 'EXEC(''USE ' + #databaseName + ''');
CREATE TABLE ' + #databaseName + '.dbo.Testing
(
TestPk int,
TestDescription nvarchar(80)
);';
EXEC sp_executesql #sqlCreation;
END
GO
Please make use of the below code. Its working fine with SQL Server 2012.
CREATE PROCEDURE PROCEDURETOREPLICATECOMMONSCHEMA
#databaseName NVARCHAR(40)
AS
BEGIN
BEGIN
EXEC('USE MASTER;CREATE DATABASE ' + #databaseName + ';')
EXEC('CREATE TABLE ' + #databaseName + '.dbo.Testing(TestPk int,TestDescription nvarchar(80));')
END
END
GO

How to create a table on a linked server with a variable server name?

I've seen some solutions on how to create tables on linked servers using EXEC('some sql') AT [LINKED_SERVER_NAME] and they work manually.
I can use DML queries such as
EXEC ( 'select * from [' + #DbServerName + '].[' + #DbName + '].dbo.someTable' )
how can I do something similar for DDL queries like
EXEC ( 'CREATE TABLE [' + #DbServerName + '].[' + #DbName + '].dbo.someTable ( id int null) ' )
I've toyed around with select * from openquery(linkedservername, query) and exec(sql) AT [linkedservername], but every time I try to make the server name a variable it fails for me.
I can run all these commands manually at in Query Analyzer, but whenever I try to make the linked server name a variable they fail for me. What I'm trying to do is something like this...
DECLARE #LinkedServerName nvarchar(100)
DECLARE #LinkedDbName nvarchar(100)
SET #LinkedServerName = 'SVR2'
SET #LinkedDbName = 'DB2'
DECLARE #DDL_QUERY nvarchar(1000)
SET #DDL_QUERY = 'CREATE TABLE [' + #LinkedDbName + '].dbo.T1 ( id int null )'
-- Current failed ideas
EXEC( #DDL_QUERY ) AT #LinkedServerName
SELECT * FROM OPENQUERY(#LinkedServerName, #DDL_QUERY)
EXEC( 'CREATE TABLE [' + #LinkedServerName + '].[' + #LinkedDbName + '].dbo.T1( id int null )'
Is it possible to dynamically create a table when the linked server name, and database name on that linked server are both declared variables?
Assuming the linked server is also SQL Server (or possibly Sybase):
DECLARE #table NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #table = N'CREATE TABLE dbo.T1(id INT NULL);';
SET #sql = N'EXEC ' + QUOTENAME(#LinkedServerName) + N'.'
+ QUOTENAME(#LinkedDbName) + N'.sys.sp_executesql #table;';
EXEC sys.sp_executesql #sql, N'#table NVARCHAR(MAX)', #table;
Slightly less verbose syntax:
DECLARE #sql NVARCHAR(MAX), #exec NVARCHAR(MAX);
SET #sql = N'CREATE TABLE dbo.T1(id INT NULL);';
SET #exec = QUOTENAME(#LinkedServerName) + N'.'
+ QUOTENAME(#LinkedDbName) + N'.sys.sp_executesql';
EXEC #exec #sql;

Creating Stream DATABASE in a remote server

Whith the assistance of a very good fellow from this forum (Mr. DJHnz) i solve my first issue regarding the creation of a stream database
Now i'm facing another issue
I'm giving you the code:
USE [master]
GO
/****** Object: StoredProcedure [dbo].[sp_AddStreamDB] Script Date: 12/21/2009 09:55:47 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_AddStreamDB](
-- Add the parameters for the stored procedure here
#DPath varchar(MAX),
#DBName varchar(50),
#Qchar varchar(1) = "'"
) AS
BEGIN_TRY:
SET QUOTED_IDENTIFIER ON;
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE
#ErrMsg nvarchar(4000),
#DBName1 varchar(50),
#DBName2 varchar(50),
#DBNamefs varchar(50),
#DBNamelog varchar(50),
#FileGroupN varchar(100),
#DATName varchar(MAX),
#LOGName varchar(MAX),
#FSName varchar(MAX),
#CreateSdb nvarchar(MAX),
#Statement nvarchar(MAX)
SET #DBName1 = (#DBName + '1')
SET #DBName2 = (#DBName + '2')
SET #DBNamefs = (#DBName + 'fs')
SET #DBNamelog = (#DBName + 'log')
SET #FileGroupN = (#DBname + 'StreamGroup')
SET #DATName = (#Qchar + #DPath + #DBName +'_dat.mdf' + #Qchar)
SET #LOGName = (#Qchar + #DPath + #DBName +'_log.ldf' + #Qchar)
SET #FSName = (#Qchar + #DPath + #DBName + '_fs' + #Qchar)
SET #CreateSdb =('CREATE DATABASE ' + #DBName + ' ON PRIMARY (NAME = ' + #DBName1 + ', FILENAME = ' + #DATName + '), FILEGROUP ' + #FileGroupN + ' CONTAINS FILESTREAM (NAME = ' + #DBNamefs + ', FILENAME = ' + #FSName + ') LOG ON (NAME = ' + #DBNamelog + ', FILENAME = ' + #LOGName + ')')
SET #Statement = ' '
BEGIN_CATCH:
SELECT ERROR_MESSAGE() as ErrorMessage;
SELECT #ErrMsg = ERROR_MESSAGE()
EXEC master.sys.sp_executesql #CreateSdb, #Statement
RAISERROR (#ErrMsg,1,1)
RETURN 0
END_CATCH:
END_TRY:
So far to point everything works fine until the remote server tries to create the neccessary files for the stream DB
then he gives the following error:
Unable to open the physical file "C:\sqlDATA\RemoteDB_fs". Operating system error -2147024891: "0x80070005(Access is denied.)".
The name of the drive C:\ lies on the remote machine (a machine near to me in the same network with Windows server 2003; later i will run the program for my ISP machine)
the subfolder sqlDATA\ is already there i have created manually as it should be.
In my local machine the all package works fine and the DATA BASE created fine but the issue starts when i use remote server.
NOW I NEED THE HELP:
Why i receive this ERROR?
The SQL Server service account does not have rights on C:\SQLData
As #gbn explained and also:
C:\sqlDATA directory must exist.
The \RemoteDB_fs sub-directory for file-stream must not exist.

Resources