有没有一个工具可以从Delphi表单文件中提取所有SQL命令字符串?

发布于 2024-08-26 16:00:12 字数 91 浏览 13 评论 0原文

为了进行文档记录和进一步检查,我想对许多项目中的所有 DFM 文件运行“提取字符串”以查找所有 SQL 语句。有命令行工具可以做到这一点吗? DFM 文件均为文本格式。

For documentation and further inspection, I would like to run an 'extract strings' on all DFM files in many projects to find all SQL statements. Are there command line tools which can do this? The DFM files are all in text format.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

百变从容 2024-09-02 16:00:12

这是 Felix Colibri 的 DFM 解析器

DFM 解析器

这里是一个有趣的工具,可以做这样的事情

YACC

Here is a DFM Parser from Felix Colibri

DFM Parser

Here is an interesting tool for doing stuff like this

YACC

红墙和绿瓦 2024-09-02 16:00:12

根据您使用的查询组件的类型,我想您可以在项目文件夹上使用命令行 grep (或任何其他文本搜索工具)来完成此操作。在 DFM 中,对于普通的类似 TQuery 的组件,您将拥有类似

   SQL.Strings=( 'select * from mytable' )

或可能的东西(我没有 Delphi 来检查,在家的乐趣!)

   SQL.Text=( 'select * from mytable' )

考虑到这些字符串如何传播DFM 内有几条“行”,并且考虑到您可能需要检查多种变化,我个人会编写一小段 Delphi 来执行此操作。

基本想法是;迭代给定目录中的所有文件/子文件夹,查找所有 DFM 文件,对于每个文件,将其读入 TStringList,检查您感兴趣的任何 TQuery(等)SQL 属性,然后编写结果(组件名称、文件名、实际 SQL 字符串)输出到结果文件。实际上最多不应该超过一两个小时。

如果您有存储过程,并且使用 TQuery 类型组件以外的其他组件进行调用,则必须首先查看 DFM 内部并查看 SQL 的显示方式;它可能会沿着

   CommandText=( 'exec mysproc :id, :value' )

等的

思路编辑:
根据评论中的讨论,以下是我的一个 DFM 中的示例;

    (other properties)
    SQL.Strings = (
      'SELECT D.*, '
      'C.DESCRIPTION AS CLASS_DESCRIPTION, '
      'C.CHQ_NUM_THRESHOLD AS CLASS_CHQ_NUM_THRESHOLD,'
      'C.CREDIT_LIMIT AS CLASS_CREDIT_LIMIT,'
      'A.REF AS ACCOUNT_REF,'
      'A.SORT_CODE AS ACCOUNT_SORT_CODE,'
      'A.ACCOUNT_NUMBER AS ACCOUNT_ACCOUNT_NUMBER,'
      'A.PREFERRED_ACCOUNT AS ACCOUNT_PREFERRED_ACCOUNT'
      'FROM '
      'DRAWER_ACCOUNTS A LEFT JOIN DRAWERS D '
      'ON D.REF=A.DRAWER_REF'
      'LEFT JOIN REF_DRAWER_CLASSES C'
      'ON D.DRAWER_CLASS = C.CLASS_ID'
      'WHERE A.SORT_CODE=:PSORT AND A.ACCOUNT_NUMBER=:PACC')
    (other properties)

因此,我真正需要做的就是找到 SQL.Strings = ( 位,然后读取该行的其余部分和所有后续行,删除前导和尾随 ' ,直到到达以 ')' 结尾的行 - 此时我就完成了。事实上,每行引号内可能包含任何有趣的 SQL(和注释),这实际上是无关紧要的。您想要阅读您感兴趣的每一行,并剪掉每行第一个和最后一个引用之间的文本。这必须有效,因为我看不到 Delphi 本身如何以其他方式流式传输,它不可能“读取”字符串内容 - 它只是在字符串列表(可能)分成行和每行的基础上工作在 DFM 中用开始和结束 ' 进行分隔,整个字符串列表内容本身包含在一对括号内。

这有道理吗,还是我还缺少一些东西? :-)

Depending on the kind of query component you're using, I would imagine you could do this using a command-line grep (or any other text-searching tool) on your project folders. In the DFM, for normal TQuery-alike components, you're going to have something along the lines of

   SQL.Strings=( 'select * from mytable' )

or possibly (I've no Delphi to hand to check, the joys of being at home!)

   SQL.Text=( 'select * from mytable' )

Given how these strings can spread over several 'lines' inside the DFM, and given how there might be several variations you'd need to check for, personally I'd write a small piece of Delphi to do this.

The basic idea would be; iterate through all the files/subfolders in a given directory, looking for all the DFM files, and for each one, read it into a TStringList, check for any of the TQuery (etc) SQL properties you're interested in, and write the results (component name, filename, actual SQL string) out to a results file. Really shouldn't be more than an hour or two's work at the most.

If you have stored procs, that you call using something other than a TQuery-type component, you'll have to have a peek inside a DFM first and see how the SQL appears; it will probably be along the lines of

   CommandText=( 'exec mysproc :id, :value' )

etc.

edit:
Following the discussion in the comments, here's a sample from one of my DFMs;

    (other properties)
    SQL.Strings = (
      'SELECT D.*, '
      'C.DESCRIPTION AS CLASS_DESCRIPTION, '
      'C.CHQ_NUM_THRESHOLD AS CLASS_CHQ_NUM_THRESHOLD,'
      'C.CREDIT_LIMIT AS CLASS_CREDIT_LIMIT,'
      'A.REF AS ACCOUNT_REF,'
      'A.SORT_CODE AS ACCOUNT_SORT_CODE,'
      'A.ACCOUNT_NUMBER AS ACCOUNT_ACCOUNT_NUMBER,'
      'A.PREFERRED_ACCOUNT AS ACCOUNT_PREFERRED_ACCOUNT'
      'FROM '
      'DRAWER_ACCOUNTS A LEFT JOIN DRAWERS D '
      'ON D.REF=A.DRAWER_REF'
      'LEFT JOIN REF_DRAWER_CLASSES C'
      'ON D.DRAWER_CLASS = C.CLASS_ID'
      'WHERE A.SORT_CODE=:PSORT AND A.ACCOUNT_NUMBER=:PACC')
    (other properties)

So all I really need to do is to spot the SQL.Strings = ( bit, then read the rest of the line and all subsequent lines, removing the leading and trailing ', until I get to a line that ends in ')' - at which point I'm done. Whatever interesting SQL (and comments) might have been contained within the quotes on each line is irrelevant, really. You want to read each line that you're interested in and cut out the text between the first and last quote on each line. This must work because I can't see how Delphi would stream this any other way itself, it can't possibly 'read' the string contents - it's just working on the basis that the stringlist is (possibly) broken into lines and each line is delimited in the DFM with an opening and closing ', and the whole stringlist contents themselves are contained within a pair of brackets.

Does that make sense, or am I still missing something? :-)

紅太極 2024-09-02 16:00:12

我创建了自己的 DFM 解析器,并使用来自 RAD Studio 和我工作的公司的 600 个源文件进行了测试。解析器是用 Go 编写的。

https://github.com/gonutz/dfm

您可以用它解析 DFM 文件并检查 in - 递归地访问内存对象,查找 dfm.String 类型的属性。

I have created my own DFM parser, tested with 600 source files from both RAD Studio and the company I work at. The parser is written in Go.

https://github.com/gonutz/dfm

You can parse DFM files with it and inspect the in-memory object recursively, looking for properties of type dfm.String.

硪扪都還晓 2024-09-02 16:00:12

由于 DFM 基本上采用 name=value 格式,因此您只需加载到 tStringlist 中,然后循环遍历每一行,查找您感兴趣的特定属性名称:

var
  slDfm : tStringList;
  Item : String;
  ix : integer;
begin
  slDFM := tStringlist.create;
  try
    slDFM.LoadFromFile( filename );
    for ix := 0 to slDfm.Count-1 do
      begin
        slDfm.Strings[ix] := Trim(slDfm.Strings[ix]);
        if SameText(Trim(slDfm.Names[ix]),'CommandText') then
          memo1.Lines.Add('"'+Trim(slDfm.ValueFromIndex[ix])+'"');
      end;
  finally
    slDFM.free;
  end;
end;

Since the DFM is basically in name=value format, you could just load into a tStringlist, then loop through each line looking for the specific property names your interested in:

var
  slDfm : tStringList;
  Item : String;
  ix : integer;
begin
  slDFM := tStringlist.create;
  try
    slDFM.LoadFromFile( filename );
    for ix := 0 to slDfm.Count-1 do
      begin
        slDfm.Strings[ix] := Trim(slDfm.Strings[ix]);
        if SameText(Trim(slDfm.Names[ix]),'CommandText') then
          memo1.Lines.Add('"'+Trim(slDfm.ValueFromIndex[ix])+'"');
      end;
  finally
    slDFM.free;
  end;
end;
故事和酒 2024-09-02 16:00:12

非常感谢您的回答!我将尝试的另一个解决方案是“GNU Gettext for Delphi and C++ Builder”中包含的“提取字符串”工具”。

.po 文件不仅包含所有组件文本,还包含所有资源字符串(存储 SQL 命令的另一个位置),并包含对源的引用(哪个 pas 或 dfm 文件,哪个组件属性名称),这是一个非常简单的“名称=值”已经列出。

使用 .po 文件,可以轻松地整理出 .pas 文件中的所有 SQL.Text 以及所有文件中名称如“SQL_...”的所有资源字符串。

Many thanks for the answers! Another solution which I will try is the 'extract strings' tool included in "GNU Gettext for Delphi and C++ Builder".

The .po files include not only all component text for but also all resourcestrings (another place where SQL commands are stored), complete with references to the origin (which pas or dfm file, which component property name) and it is a very simple "name=value" list already.

With a .po file it will be easy to sort out all SQL.Text from .pas files and all resourcestrings with names like 'SQL_..." in all files.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文