Reputation: 132
I want to develop very small application by C# which get some SQL query from user and execute it against specified SQL Server.
SQL Server and database must specified by user thus every things can change. My problem is user can enter various type of SQL queries, and every king of it should run in its own way.
For example
SELECT * FROM mytable
and
UPDATE mytable
SET city = "NY"
WHERE name = "tom"
can not execute in the same way.
I think I need to recognize user query type in my code, Is there any way to recognize it or any better way to run any possible query?
Upvotes: 0
Views: 324
Reputation: 1
I would use TSqlPerser.ParseStatementList
method from Microsoft.SqlServer.TransactSql.ScriptDom
namespace (reference | download #1 - Nuget download #2 - Microsoft® SQL Server® 2014 Transact-SQL ScriptDom ):
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.TransactSql.ScriptDom;
using System.IO;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
TextReader rdr = new StreamReader(new MemoryStream(
Encoding.UTF8.GetBytes(
@"/* comment */
WITH CteUpdate
AS
(
SELECT * FROM Table1
WHERE Col1 = 1
)
UPDATE CteUpdate SET city = ""NY"" WHERE name = ""tom""")
));
TSql110Parser parser = new TSql110Parser(true);
IList<ParseError> errors;
StatementList stmtList = parser.ParseStatementList(rdr, out errors);
// Process errors
foreach(TSqlStatement stmt in stmtList.Statements)
{
Console.WriteLine("Statement type {0}", stmt.GetType());
if (stmt is SelectStatement)
{
//Process SELECT statment
}
else if (stmt is UpdateStatement)
{
//Process UPDATE statment
UpdateStatement stmtUpdate = (UpdateStatement)stmt;
NamedTableReference tabRef = (NamedTableReference)stmtUpdate.UpdateSpecification.Target;
Console.Write(" > UPDATE statement > target object {0}", tabRef.SchemaObject.BaseIdentifier.Value);
}
else //Process other statments
{
throw new NotImplementedException();
}
}
}
}
}
Output:
Statement type Microsoft.SqlServer.TransactSql.ScriptDom.UpdateStatement > UPDATE statement > target object CteUpdate
Upvotes: 2
Reputation: 46233
You don't need to create different query types in your code. The SqlCommand ExecuteReader method can be used for all DML
and DDL
statements. If no results are returned by a statement, the SqlDataReader Read method will return false. You also need to invoke the SqlDataReader.NextResult method until it returns false to return the results from multiple statements.
var reader = command.ExecuteReader();
do{
while(reader.Read())
{
//process resultset here
}
}while(reader.NextResult());
Upvotes: 2
Reputation: 82504
I would simply give the user 2 execute buttons, one for non-query and one for reader.
If you trust the user to write sql statements, you can trust the user to know what button to click.
The reason for not attempting to anayze the sql statement yourself is very simple:
it's possible to write something like exec stp_doesThisProcedureSelectsOrUpdates
, so you simply can't tell...
Upvotes: 0
Reputation: 22651
(This answer assumes you use the classes available in the System.Data.SqlClient
namespace.)
The simplest, but not fool-proof method would be to check the first word - if it is (case insensitive) equal to SELECT
, you can use ExecuteReader()
- if it is not, you can use ExecuteNonQuery()
.
Another option is just to ExecuteNonQuery()
anyway. This will return -1 if you pass a SELECT statement as CommandText
. Upon detecting this, you could call ExecuteReader()
. However, other queries like BEGIN TRANSACTION
will also return -1.
Upvotes: 2