2

Morning all Riddle me this. Trying to find a way to parse sql scripts stored in a text field in a database. These sql statements are to determine system health and also perform intricate data manipulation at certain times during the month or after certain events. The statements can contain anything from simple select statements to large CRUDs containing recursion, sp calls, variables, temp tables, etc.

I have now cobbled together a solution that sort of does what I need I can parse the sql for syntax validity, check. I can check the sql if columns referenced exists. check. I can report back to the user any error that exists at "design time" i.e. syntactically

The problem is that it is not picking up if a table referenced does not exist. There are a bunch of cases that the parser will only pick up at "run time" when sql is executed.

My proposed solution would be (and this is where I need the communities feedback) 1. Create a .net SQL Connection, transaction and command object 2. Hook the Tx onto the connection and command 3. Execute the SQL in the command and address errors that come up. 4. Irrespective always roll back the Tx

DISCLAIMER: Please do not go on about best practice, I know it isn't, but need to provide usable feedback to the GUI and as far as I can determine, this is the only way to achieve the requirement.

I already use NOEXEC, PARSEONLY and Microsoft.SqlServer.TransactSql.ScriptDom, but again that is only for syntax not actual validity.

Your thoughts and feedback would be greatly appreciated.

Thank you

8
  • My question is, what would the possible implications be to execute statements as describes in a transaction and then roll it back Commented Sep 17, 2014 at 10:28
  • Can you give an example of the type of errors that you don't get via NOEXEC? Commented Sep 17, 2014 at 10:35
  • re rollback: not everything can be rolled back - extended stored procedure - xp_cmdshell for example. Commented Sep 17, 2014 at 10:38
  • SET NOEXEC ON; SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = 'MasterCurrencyConversions'; SELECT * FROM [INFORMATION_SCHEMA].[TABLE] WHERE [TABLE_NAME] = 'MasterCurrencyConversion'; SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = 'MasterCurrencyConversions'; SET NOEXEC OFF; Commented Sep 17, 2014 at 10:39
  • Well, there's always SET FMTONLY ON - but that is kinda deprecated; annoyingly, the replacements only process as far as the first grid, and don't work with your example. The advantage of FMTONLY over using a transaction is that it has no IO costs and no blocking - but it still could suffer from things like extended stored procedures (you cannot roll back an xp_cmdshell) Commented Sep 17, 2014 at 10:44

1 Answer 1

2

My question is, what would the possible implications be to execute statements as describes in a transaction and then roll it back

The main issue there is that not everything can be rolled back. If the scripts are using things like xp_cmdshell, expect pain. But that kinda goes with the territory.

A better alternative to a transaction is: FMTONLY. This is kinda similar, but rather than do work and then undo it, it simply short-circuits everything that would involve rows. This means that it has no IO cost, no rollback cost, and won't cause blocking. It does, however, suffer from the same issues re things like xp_cmdshell. Additionally, note that MSDN has the caveat:

Do not use this feature. This feature has been replaced by sp_describe_first_result_set (Transact-SQL), sp_describe_undeclared_parameters (Transact-SQL), sys.dm_exec_describe_first_result_set (Transact-SQL), and sys.dm_exec_describe_first_result_set_for_object (Transact-SQL).

Unfortunately, as the names suggest: these only work for the first result set. If we take your [INFORMATION_SCHEMA].[TABLE] example from the comments, and try that with SQL-Server's suggested option:

EXEC sp_describe_first_result_set N'
SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = ''MasterCurrencyConversions'';
SELECT * FROM [INFORMATION_SCHEMA].[TABLE] WHERE [TABLE_NAME] = ''MasterCurrencyConversion'';
SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = ''MasterCurrencyConversions'';
';

then it works, returning:

is_hidden column_ordinal name            
--------- -------------- ----------------(snipped)
0         1              TABLE_CATALOG   
0         2              TABLE_SCHEMA    
0         3              TABLE_NAME      
0         4              TABLE_TYPE      

etc - it does not tell us about the problem in the second SELECT, because it stopped at the first. Conversely, using the now-deprecated FMTONLY option:

SET FMTONLY ON;
SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = 'MasterCurrencyConversions';
SELECT * FROM [INFORMATION_SCHEMA].[TABLE] WHERE [TABLE_NAME] = 'MasterCurrencyConversion';
SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = 'MasterCurrencyConversions';
SET FMTONLY OFF;

Gives us:

TABLE_CATALOG                                          
---------------------------------------------(snipped)

(0 row(s) affected)

Msg 208, Level 16, State 1, Line 3
Invalid object name 'INFORMATION_SCHEMA.TABLE'.

So we get our error, but we're using an officially "do not use" feature :(

Additionally, note that it doesn't execute DDL commands; this means that otherwise valid operations can fail - for example, here @Foo is fine but #Bar fails:

SET FMTONLY ON;
DECLARE @Foo TABLE (id int not null identity(1,1))
SELECT * FROM @Foo
CREATE TABLE #Bar (id int not null identity(1,1))
SELECT * FROM #Bar
SET FMTONLY OFF;

which outputs:

id
-----------

(0 row(s) affected)

Msg 208, Level 16, State 0, Line 11
Invalid object name '#Bar'.
Sign up to request clarification or add additional context in comments.

1 Comment

Good to know, thank you for the exhaustive examples and thorough explanation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.