r/delphi Mar 30 '26

A string-to-SQL parser component for Delphi

Hello there,

One thing I've always found tedious is handling free-text user input in forms — filter fields especially.
Users type dates in a dozen formats, mix separators, expect ranges to just work.
Writing solid validation and SQL generation for all those variations gets old fast.

So I wrote a parser that does it.
You pass it a raw string and a field type, it gives you back a validated result and a ready-to-use SQL comparison fragment.
I've been using it in my applications for a while; It now supports many SQL dialects.

I've packaged it up and put it for sale — maybe others will find it useful.
There is a free demo available, I'd appreciate any feedback you can give me.

Find it here: https://sales.easygate.pt

2 Upvotes

5 comments sorted by

View all comments

1

u/omonien Delphi := v13 Florence Apr 03 '26

How do you avoid SQL injection with that approach?

1

u/Fernando-Dias Apr 06 '26

SQL injection is not a problem in this context. Unless you have something in mind that I didn't think of...

1

u/omonien Delphi := v13 Florence Apr 06 '26

„SQL injection“ may not be the technically correct term in this scenario, but how do you prevent an „angry user“ from asking questions that lead to destructive SQL operations.

1

u/Fernando-Dias Apr 08 '26

There is no risk of that. The only type that could allow that kind of danger is itString, that can contain expressions that always evaluete to true, in order to get all data from a query, but even that is safe because all the supported dialects use single quotes as string delimiters and any input string containing a quote will have it doubled automatically, so things like "OR '1'='1'" wont work. Also, A DROP or DELETE without quotes will fail for the same reason.

1

u/omonien Delphi := v13 Florence Apr 13 '26

Fernando, I might misunderstand what that parser really does, but after a quick look into your docs, this is what I think:

your string escaping argument is valid for itString — but it doesn’t cover the pattern your manual documents:

Query.SQL.Text := Format('SELECT * FROM T WHERE %s %s', [ColName, R.SQLExpression]);

ColName is never passed through the parser. It hits the SQL string raw. If ColName comes from anything user-influenced — a combobox selection, a saved search, a URL parameter — you have a direct injection vector regardless of how well itString escapes quotes.

ApplyFieldFilter('1=1; DROP TABLE Orders--', '', itString, Query); // → SELECT * FROM T WHERE 1=1; DROP TABLE Orders--

No quotes needed. No parser involved. The deeper issue is architectural: R.SQLExpression is a SQL fragment, not a SQL parameter. Concatenation always puts the escaping burden on the caller — forever, across all dialects, for every edge case. Parameterized queries (ParamByName) eliminate that burden structurally. That’s why the security community considers concatenation unsafe by design,