tags:

views:

118

answers:

3

Hi all,

I intended to provide my view/business layer with the possibility to send HQL queries in strings to my data layer.

However, the data layer needs to analyze and manipulate these queries (in particular, add a criterion in the where clause).

The supported forms of HQL queries are any combination of the following:

from ...
where ...
order by ...

I think this kind of simplified HQL query should be regex-able, and this is the Regex I defined:

public const string HqlRegex = @"^(\bfrom\b\s*(?<FromPart>.+)\s*)?"
    + @"(\bwhere\b\s*(?<WherePart>.+)\s*)?"
    + @"(\border\b\s+\bby\b\s*(?<OrderByPart>.+))?$";

Regex re = new Regex(Novartis.MapAdmeBs.NHibernateDAO.DAOFactory.HqlRegex,
    RegexOptions.Singleline);

Update: I've even tried with the non-greedy modifier:

public const string HqlRegex = @"^(\bfrom\b\s*(?<FromPart>.+?)\s*)?"
    + @"(\bwhere\b\s*(?<WherePart>.+?)\s*)?"
    + @"(\border\b\s+\bby\b\s*(?<OrderByPart>.+?))?$";

However, when I try to match a string containing the where and the order by clause, the "order by" keyword is regarded as part of the where clause:

Match m = re.Match("where (e.Name not like '%X') and e.StatusID not in (7, 8, 9)"
                 + " order by e.DateCreated desc");
Console.WriteLine(m.Groups["WherePart"].Value);

gives

(e.Name not like '%X') and e.StatusID not in (7, 8, 9) order by e.DateCreated desc

Any of the following help is appreciated:

  • How to fix the regexp?
  • Is there a regexp for HQL? (Googling leads to regexp-features of the HQL language)
  • Better idea which is still simple enough to implement in a day or less?
+2  A: 

There isn't a regular expression for HQL because HQL isn't a regular language.

To make a quick fix you could use the non-greedy modifier ? for each .+:

string hqlRegex = @"^(\bfrom\b\s*(?<FromPart>.+?)\s*)?"
    + @"(\bwhere\b\s*(?<WherePart>.+?)\s*)?"
    + @"(\border\b\s+\bby\b\s*(?<OrderByPart>.+?))?$";

Regex re = new Regex(hqlRegex);
Match m = re.Match("where (e.Name not like '%X') and e.StatusID not in (7, 8, 9)"
         + " order by e.DateCreated desc");
string wherePart = m.Groups["WherePart"].Value;
Console.WriteLine(wherePart);

Result:

(e.Name not like '%X') and e.StatusID not in (7, 8, 9)

I'd also like to mention that it is pointless to include the a word-boundary in for example order\b\s+\bby. There must always be a word boundary between r and a whitespace character so there is no need to specify it.

If you wish to do this with ANTLR then you could look at the article Working with ANTLR: HQL Grammar.

Mark Byers
yeah this article looks great
PierrOz
I tried the ungreedy-modifier now, but it didn't help unfortunately. (see edit)
chiccodoro
(BTW: Yes, the article looks great indeed, will have a look at it.)
chiccodoro
@chiccodoro: It works fine for me - see my updated answer for a runnable example that gives the correct result on your examples. Maybe the error you have made is in some code that you haven't posted.
Mark Byers
@Mark: You're right, I misinterpreted my last result. I've now also removed the mentioned word boundaries, they were a relict.
chiccodoro
+1  A: 

I'm not sure the regex are the best tool for that. I'd rather try ANTLR and define a small grammar to parse your HQL-lite queries.

PierrOz
+1  A: 

I would not try to use Regex for anything as complex as HQL because HQL isn't a regular language.

See When not to use Regex in C# (or Java, C++, etc.)

As you can in control of both ends, I would consider representing your query as a tree of objects, that you then convert to/from xml or json. That way you will not have to write any string parsing code yourself.

Otherwise, someone somewhere must have written a parser for HQL queries, look at the source code for any open source projects that use HQL.

Ian Ringrose
Hi Ian. I've also thought about the XML tree. But isn't XML a regular language? For that would mean I can do the same things with XML as I can do with a simplified, regular HQL version.
chiccodoro
@chiccodore, yet but you don't have to write the code yourself to parse XML!
Ian Ringrose
@Ian, that's true in turn and would especially be true if I needed to completely analyze the query. In my case I only split it in 3 parts (from, where, order by) and after some modification, merge them together again. The actual parsing is done by NHibernate. I'll still keep your advice in mind if the requirements should change some day...
chiccodoro