Есть ли способ заставить FireDAC Delphi распознавать позиционные параметры PostgreSQL, сгенерированные FireDAC? - PullRequest
0 голосов
/ 08 июля 2019

Я выполняю запросы с именованными параметрами из FireDAC в PostgreSQL 11, используя собственный драйвер FireDAC Postgres.Во время оператора подготовки FireDAC преобразует названные параметры в позиционные параметры, что является правильным.Однако, если я попытаюсь присвоить значения этим параметрам, FireDAC выдает исключение «Аргумент вне диапазона».Похоже, что FireDAC не распознает сгенерированные им позиционные параметры.Например, если исходный текст SQL выглядел примерно так:

SELECT * FROM account WHERE accountid = :accid;

при вызове метода Prepare для FDQuery, FireDAC преобразовал этот запрос в следующий:

SELECT * FROM account WHERE accountid = $1;

Но когда япопробуй присвоить значение параметру, получаю ошибку.Назначение выглядит примерно так:

FDQuery1.Params[0].AsString = strID;

, где strID - строковое значение, а accountid - текстовое поле.Кроме того, если я использую что-то вроде следующего, он возвращает 0.

ShowMessage( IntToStr( FDQuery1.Params.Count ) );

Я значительно упростил этот код, но проблемы те же.Как заставить FireDAC распознавать сгенерированные им позиционные параметры?


Обновление : как я уже упоминал, приведенный выше код значительно упрощен.На самом деле происходит то, что в нашей инфраструктуре у нас есть один набор подпрограмм, которые присваивают значения макросам FireDAC, а затем мы генерируем инструкцию SQL, подготавливая запрос и затем читая свойство Text FDQuery.Затем этот оператор SQL присваивается свойству SQL.Text другого FDQuery (также динамически создаваемого), и именно здесь происходит сбой запроса.Итак, вот очень простой пример того, что происходит внутри кода:

var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create( nil );
  Query.Connection := PGConnection;
  // In reality, the SQL statement below was generated earlier,
  // from a function call where the SQL was created by the FireDAC
  // SQL preprocessor, as opposed to being a literal expression
  Query.SQL.Text := 'SELECT * FROM tablename WHERE field2 = $1;';
  Query.Params[0].AsString := '4'; // BANG! Argument out of range

Я подумал, что это может быть связано с расширением макроса FireDAC, поэтому я добавил следующие две строки после создания экземпляра FDQuery:

Query.ResourceOptions.MacroCreate := False;
Query.ResourceOptions.MacroExpand := False;

Нет.Это тоже помогло.Я предполагаю, что FireDAC просто не распознает, что $ 1 является допустимым позиционным параметром в PostgreSQL.

Ответы [ 2 ]

0 голосов
/ 11 июля 2019

Хорошо, возможно, есть способ решить эту проблему, используя свойства FireDAC, но я пока не нашел его. Однако для этой конкретной ситуации, когда SQL-запрос готовится одним методом, а затем присваивается другому FDQuery из другого метода, я нашел ответ. Поскольку PostgreSQL разрешает именованным параметрам использовать цифры, такие как: 1, я заменил символы $ на символы:. Как в

var
  SQLStmt: String;
  FDQuery: TFDQuery;
begin
  SQLStmt :=  'SELECT * FROM tablename WHERE field2 = $1;';
  FDQuery := TFDQuery.Create( nil );
  FDQuery.Connection := FDConnection1;
  FDQuery.SQL.Text := SQLStmt.Replace( '$', ':' );
  FDQuery.Params[0].AsString := 'SomeValue'); // Works!
  ...

И, да, если ваш запрос включает более одного экземпляра именованного параметра, FireDAC заменяет его одной и той же цифрой.

Если я найду другое решение, я опубликую его. Если кто-то придумает решение, использующее свойства FireDAC, я выберу этот ответ в качестве правильного.

0 голосов
/ 10 июля 2019

Вам необходимо поместить символьный идентификатор параметра (в вашем случае :accid) в строку SQL.Text, а не позиционный код ($1)

Я только что протестировал эти два варианта.Первый работает, второй нет.

var
  MyQ : tfdquery;
begin
  MyQ := Tfdquery.Create(nil);
  MyQ.Connection := dm1.dbMain;
  MyQ.SQL.Text := 'Select * from person where lastname = :lname;';
  MyQ.Params[0].AsString := 'Brodzinsky';
  MyQ.Open();
  ShowMessage('Records found = '+MyQ.RecordCount.ToString);
  MyQ.Close;
  MyQ.Free;
end;

Следующий пытается использовать позиционный.Конечно, FireDac не видит двоеточие, поэтому не знает, что есть параметр для создания

var
  MyQ : tfdquery;
begin
  MyQ := Tfdquery.Create(nil);
  MyQ.Connection := dm1.dbMain;
  MyQ.SQL.Text := 'Select * from person where lastname = $1;';
  MyQ.Params[0].AsString := 'Brodzinsky';
  MyQ.Open();
  ShowMessage('Records found = '+MyQ.RecordCount.ToString);
  MyQ.Close;
  MyQ.Free;
end;

В первом случае 11 записей в таблице person возвращаются с моей фамилией;во втором генерируется ошибка Argument Out Of Range, так как в тексте SQL не указан параметр.Примечание: я обращаюсь к базе данных MySQL, но проблема здесь заключается в предварительной обработке кода Delphi & FireDac кода для отправки на сервер базы данных.

...