-2

I am trying to create a database in sql using the following

DECLARE @DatabaseName VARCHAR(50) = N'testdb';
EXECUTE ('IF DB_ID(' +@DatabaseName+') IS NULL BEGIN ' + 'CREATE DATABASE [' 
+@DatabaseName+'] END');

but it gives an error:

  Invalid column name 'testdb'

Any ideas on how i can fix this ?

UPDATE: DECLARE @username nvarchar(4000) DECLARE @DatabaseName nvarchar(4000) DECLARE @password nvarchar(4000)

set @username = 'username1'
set @DatabaseName = 'dbtest'
set @password = 'TEST12345'

DECLARE @t nvarchar(4000)
SET @t = 'If not Exists(select loginname from 
master.dbo.syslogins where name = ' + QUOTENAME(@username) + 
     ' BEGIN ' + 
         'CREATE LOGIN '  + QUOTENAME(@username) + ' WITH 
PASSWORD = ' + QUOTENAME(@password, '''') + ', default_database = 
' +
          QUOTENAME(@DatabaseName) + ', DEFAULT_LANGUAGE= 
 [us_english],CHECK_EXPIRATION=OFF,CHECK_POLICY=OFF ' + 
         'CREATE LOGIN '  + QUOTENAME(@username) + 'FOR LOGIN ' + 
 QUOTENAME(@username) + 
     ' END'      
 EXEC(@t)



  
Christy
  • 19
  • 6

2 Answers2

0

It's confusing the variable with a column because you are not enclosing @DatabaseName between '', and you need to escape them with '' or \'

DECLARE @DatabaseName VARCHAR(50) = N'testdb'; EXECUTE ('IF DB_ID('''
+@DatabaseName+''') IS NULL BEGIN ' + 'CREATE DATABASE [' +@DatabaseName+'] END');
EloyBJ
  • 1
  • 3
  • Potentially vulnerable to SQL injection. This makes a very bad example for less experienced developers, who may adopt this approach in an application that gets its input straight from the client. Use [QUOTENAME](https://learn.microsoft.com/en-us/sql/t-sql/functions/quotename-transact-sql) instead. – Ruud Helderman May 07 '23 at 21:23
0

The desired SQL statement is:

IF DB_ID('testdb') IS NULL CREATE DATABASE [testdb]

The T-SQL expression to build up this statement is:

N'IF DB_ID(' +
QUOTENAME(@DatabaseName, '''') +
N') IS NULL CREATE DATABASE ' +
QUOTENAME(@DatabaseName)

Notice the use of QUOTENAME to wrap testdb in quotes and in brackets.

  • QUOTENAME(@DatabaseName) generates [testdb]
  • QUOTENAME(@DatabaseName, '''') generates 'testdb'

QUOTENAME helps prevent potential SQL injection. Always use it in dynamic SQL, even when you are 100% certain your input is sanitized; for two reasons.

  1. Nothing is 100% certain. There's nothing wrong with a healthy dose of suspicion, even when it concerns your own code.
  2. Your code makes a bad example for other (less experienced) developers.

Unfortunately, the syntax of EXECUTE is rather limited; function calls are not allowed in the string expression. We need a temporary variable to build up the SQL statement.

DECLARE @DatabaseName nvarchar(50) = N'testdb';

DECLARE @creator nvarchar(200) =
    N'IF DB_ID(' +
    QUOTENAME(@DatabaseName, '''') +
    N') IS NULL CREATE DATABASE ' +
    QUOTENAME(@DatabaseName)

EXECUTE (@creator)

Dynamic SQL really hurts readability; it is all too easy to make mistakes. A good way to analyze bugs is to let the generated SQL statement be printed rather than executed.

  1. Temporarily replace EXECUTE (@creator) with PRINT @creator
  2. Run the SQL script in SSMS; the generated SQL statement will appear in the output pane of SSMS.
  3. Copy/paste the output into another query window. Now it's easy to spot the syntax errors; just look for red squiggly lines.
  4. Test the generated script, but fix the bugs in the generating script.
  5. Repeat the preceding steps until it works.

I strongly recommend to wrap the SQL script above in a stored procedure, as it allows you to properly manage the required permissions, as explained in the article Packaging Permissions in Stored Procedures by Erland Sommarskog.

CREATE PROCEDURE CreateDatabaseIfNotExists @DatabaseName nvarchar(50) AS
    DECLARE @creator nvarchar(200) =
        N'IF DB_ID(' +
        QUOTENAME(@DatabaseName, '''') +
        N') IS NULL CREATE DATABASE ' +
        QUOTENAME(@DatabaseName)
    EXECUTE (@creator)

You can now call the stored procedure from C#. As there is no dynamic SQL here, you do not need to worry about escaping characters in the database name.

using (var con = new SqlConnection(YourConnectionString))
using (var cmd = new SqlCommand("CreateDatabaseIfNotExists", con))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@DatabaseName", SqlDbType.VarChar).Value = YourDatabaseName;
    con.Open();
    cmd.ExecuteNonQuery();
}
Ruud Helderman
  • 10,563
  • 1
  • 26
  • 45
  • Thankyou @Ruud Helderman I have another quite complex query that i need to parameterize , in the question. Could you please help with that ? – Christy May 07 '23 at 22:20
  • Please post a new question for that. The situation is different; the SQL-generating code appears to be written in a different programming language, which will (hopefully) have its own SQL escaping function. – Ruud Helderman May 07 '23 at 22:31
  • I have updated the question. it is in sql . I gave the c# version, sorry about that . – Christy May 07 '23 at 22:46
  • You cannot parameterize `CREATE USER [SomeName]`, so please follow the same approach as with `CREATE DATABASE`. – Ruud Helderman May 07 '23 at 22:57
  • your approach for create database doesnt seem to work for databases that have an apostrophe, for example test'123 as a database – Christy May 07 '23 at 23:09
  • Works fine here. Of course, I am [escaping the quote](https://stackoverflow.com/questions/1586560/how-do-i-escape-a-single-quote-in-sql-server) in `DECLARE @DatabaseName nvarchar(50) = N'test''123'` – Ruud Helderman May 07 '23 at 23:16
  • It worked because you escaped it . But if a user types in test'123 in a ui .. then the dynamic sql generated wont work . – Christy May 08 '23 at 00:01
  • Escaping is _always_ necessary for [constants](https://learn.microsoft.com/en-us/sql/t-sql/data-types/constants-transact-sql); in this case the hard-coded `'testdb'` inside the script (as per your own example). When the name is entered through a UI, then it should be passed into the script (or better, a stored procedure) as a _parameter_ instead of a variable. The parameter value does not need to be escaped. – Ruud Helderman May 08 '23 at 07:15
  • I have tried that and it doesnt seem to escape the parameter. Would you by any chance have a working example in c# ? thankyou – Christy May 08 '23 at 21:09
  • If i run it from dynamic sql in c# , the output is as follows: exec sp_executesql N'EXECUTE (''IF DB_ID('''''' +@DatabaseName+'''''') IS NULL BEGIN '' + ''CREATE DATABASE ['' +@DatabaseName+''] END'');',N'@DatabaseName nvarchar(7)',@DatabaseName=N'abc''def' But i get an error , Incorrect syntax near 'def'. Any reason why this would happen ? – Christy May 08 '23 at 21:17
  • `exec sp_executesql N'EXECUTE (''`... You are wrapping dynamic SQL in dynamic SQL; please don't do that! I edited my answer to show you how to create a stored procedure and call it from C#. – Ruud Helderman May 09 '23 at 10:29
  • Besides, the code in your latest comment is not following my advice to use `QUOTENAME`. Not only is that the direct cause of the error, it also could have had [far graver consequences](https://xkcd.com/327/). – Ruud Helderman May 09 '23 at 11:03
  • would you be able to help with that please. having trouble getting it parameterized . – Christy May 11 '23 at 21:07
  • I assume you saw the sample code in my answer (both the stored procedure and the C# snippet). It's unclear to me what further help you need. Do you need more explanation about the effect of escaping characters and/or `QUOTENAME`? Do you need help with having more than one parameter in a stored procedure? Please consider asking a separate question if your troubles exceed the scope of the original question. – Ruud Helderman May 12 '23 at 19:41
  • The update is a large Query, so do i need to break it up by using QUOTENAME ? – Christy May 14 '23 at 20:57
  • As in my sample code, you need to split up your SQL script in 'static' parts (used as is) and 'dynamic' parts (quoted by QUOTENAME). Make sure the variable that holds the SQL statement is long enough. – Ruud Helderman May 15 '23 at 10:08
  • ok will give it a try and see what i come up with – Christy May 15 '23 at 23:27
  • I have updated the question. I have tried to parameterize and get the error , Incorrect syntax near keyword BEGIN. – Christy May 16 '23 at 07:37
  • Replace `EXEC(@t)` with `PRINT @t`. Run the SQL script in SSMS; the generated SQL statement will appear in the output pane of SSMS. Copy/paste the output into another query window. Now it's easy to spot the mistakes; just look for red squiggly lines. Fix the bugs in the SQL script. Rinse and repeat until the generated SQL is completely bug-free. (I will edit this trick into my answer.) – Ruud Helderman May 16 '23 at 09:02