0

I am getting some SQL statement that user inputs on UI:

select * from [table] where [col1] is not null and col2 <> 0

I need to verify that all column names (col1 and col2) are in brackets (like col1). And show some popup message if any column name is not in brackets (in this case, col2).

Is there some way to do such SQL verification in C#?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Ivan
  • 355
  • 2
  • 14
  • Sure, you just need an SQL parser - but stack overflow is not a good place to ask for that. Why do you even care if the user enters the brackets or not? – Luaan Dec 15 '15 at 10:43
  • 1
    So your user is allowed to write query with statements like DELETE, DROP etc? Do you think that this is wise from a security standpoint? – Steve Dec 15 '15 at 10:45
  • Got to ask. Why do you care if it has a [ or not. How are you validating it is valid SQL? – paparazzo Dec 15 '15 at 11:15

1 Answers1

3

Regarding parsing SQL, you can use the TSqlParser library that ships with Visual Studio. This parser accepts a visitor class that inherits from TSqlFragmentVisitor and overrides the visit method for ColumnReferenceExpression.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.SqlServer.TransactSql.ScriptDom;

namespace ConsoleApplication8
{
    public class QueryParser
    {
        public IEnumerable<string> Parse(string sqlSelect)
        {
            TSql100Parser parser = new TSql100Parser(false);
            TextReader rd = new StringReader(sqlSelect);
            IList<ParseError> errors;
            var columns = new List<string>();

            var fragments = parser.Parse(rd, out errors);
            var columnVisitor = new SQLVisitor();
            fragments.Accept(columnVisitor);
            columns = new List<string>(columnVisitor.Columns);

            return columns;
        }
    }


    internal class SQLVisitor : TSqlFragmentVisitor
    {
        private List<string> columns = new List<string>();

        private string GetNodeTokenText(TSqlFragment fragment) 
        { 
            StringBuilder tokenText = new StringBuilder(); 
            for (int counter = fragment.FirstTokenIndex; counter <= fragment.LastTokenIndex; counter++) 
            { 
                tokenText.Append(fragment.ScriptTokenStream[counter].Text); 
            }

            return tokenText.ToString(); 
        }


        public override void ExplicitVisit(ColumnReferenceExpression node)
        {
            columns.Add(GetNodeTokenText(node));
        }

        public IEnumerable<string>  Columns {
            get { return columns; }
        }
    } 

    public class Program
    {

        private static void Main(string[] args)
        {
            QueryParser queryParser = new QueryParser();
            var columns = queryParser.Parse("SELECT A,[B],C,[D],E FROM T  WHERE isnumeric(col3) = 1 Order by Id desc");
            foreach (var column in columns)
            {
                Console.WriteLine(column);
            }
        }
    }
}

However, I am not sure that parsing SQL that an user inputs in UI is a good idea. Even if you run the SQL statements in a sand boxed environment where the user only has read permissions (I do hope you are doing this), it is not very user friendly. As a user I would prefer to be able to select the columns I want using check boxes or by dragging and dropping them rather than having to write a SQL query.

Alex
  • 21,273
  • 10
  • 61
  • 73
  • Plus the whole concept of typing queries as text in UI is SQL injection friendly. What if they drop tables or databases from UI? – Alex Dec 15 '15 at 10:59
  • Jaco, thanks for your reply. I am trying to use the **statement.ScriptTokenStream** list to verify every Token. But if I have "**where is isnumeric(col3) == 1**" I can't distinguish **isnumeric** from **col3**, because for both of them TokeType is "**identifier**". – Ivan Dec 15 '15 at 14:58
  • Jaco, regarding usability I totally agree with you. – Ivan Dec 15 '15 at 15:02
  • I have added a code example more specific to your problem – Alex Dec 16 '15 at 13:24
  • Hi @Alex, this code gets the result but what if update statement with joins, how can I get the table name for that? – Rush.2707 Apr 17 '20 at 06:38