var
  fileQueryList, sql: String;
  QueryList: TStringList;
  VarValue: Variant;

// Инициализация кеша запросов
procedure QueryInitialization(Xml: Variant = null);
begin
  QueryList := TStringList.Create;
  if VarIsNull(Xml) then
    Exit;
  fileQueryList := ExtractFilePath(Xml.documentElement.
    getAttribute('INI_FILE')) + 'QueryList.tmp';
  if not StrToBool(Xml.documentElement.getAttribute('PACKETFIRST')) and
    FileExists(fileQueryList) then
      QueryList.LoadFromFile(fileQueryList);
end; {End QueryInitialization}

// Деинициализация кеша запросов
procedure QueryFinalization(Xml: Variant = null);
begin
  if not VarIsNull(Xml) then
    if StrToBool(Xml.documentElement.getAttribute('PACKETLAST')) and
      FileExists(fileQueryList) then
      DeleteFile(fileQueryList)
    else if not StrToBool(Xml.documentElement.getAttribute('PACKETLAST')) then
      QueryList.SaveToFile(fileQueryList);
  QueryList.Free;
end; {End QueryFinalization}

// Функция низжего уровня (основа для остальных) - возвращает строку
// sqlText - SQL запрос формата 'Select .. From .. Where .. = ?'
// parValue - список параметров подставляемые вместо ? в sqlText например [..,..,..,]
function QueryStr(sqlText: String; parValue: Variant = Null): String;
var
  i: Integer;
  str: String;
  function parValueStr(parValue: Variant): String;
  begin
    Result := '';
    if VarIsNull(parValue) then
      Exit;
    if VarIsArray(parValue) then
    begin
      for i := 0 to Length(parValue) - 1 do
        Result := Result + parValueStr(parValue[i]);
    end
    else
      Result := VarToStr(parValue);
  end;
  procedure parValueAdd(parValue: Variant);
  begin
    if VarIsNull(parValue) then
      Exit;
    if VarIsArray(parValue) then
    begin
      for i := 0 to Length(parValue) - 1 do
        parValueAdd(parValue[i]);
    end
    else
        // exchange 2690
        // здесь вместо проверки значения даты проверямем тип variant
        // и только если это дата - только тогда значение даты.
        // если этого не делать а проверять, как раньше, только значение
        // даты, то вещественное число, подходяшее по формату ДД.ММ,
        // воспринимается как дата.
        // например сумма 18.03 (если разделитель в строке даты - точка),
        // воспринимается как дата 1900-01-17. преобразование выполняется
        // неявно через строку: 18.03 число -> ValidDate( 18.03) =true
        // что в результате приводит к типу дата.
        if (VarType(parValue) <> varDate)  //ValidDate(parValue)
          then Query.AddStr( VarToStr(parValue))
          else
            if ValidDate(parValue)
              then Query.AddDate( parValue)
              else Query.AddStr( VarToStr(parValue));
  end;
begin
  str := MD5String(sqlText + parValueStr(parValue));
  if QueryList.IndexOfName(str) >= 0 then
  begin
    Result := QueryList.ValueFromIndex(QueryList.IndexOfName(str));
    Exit;
  end;
  parValueAdd(parValue);
  Result := Query.Run(sqlText);
  QueryList.Add(str + '=' + result);
end; {End QueryStr}

// Функция обёртка над QueryStr, отличие возвращает XML
// При отсутствие результата выборки  - Result.documentElement.childNodes.length = 0
function QueryXml(sqlText: String; parValue: Variant = Null): Variant;
begin
  Result := CreateOleObject('Microsoft.XmlDom');
  Result.LoadXML(QueryStr(sqlText, parValue));
end; {End QueryXml}

// Функция обёртка над QueryXml, отличие  - возвращает значение поля
// Добавлен параметр fldName - наименование поля, в случае
function QueryField(sqlText: String; parValue: Variant; fldName: String): String;
var
  Xml: Variant;
begin
  Xml := QueryXml(sqlText, parValue);
  if Xml.documentElement.ChildNodes.length = 0 then
  begin
    Result := Null;
    Exit;
  end;
  result := Xml.documentElement.firstChild.getAttribute(fldName);
    if result = null then
      raise('Поля: ' + fldName + ' нет в результирующей XML по запросу: ' + SQLText);
end; {End QueryField}

begin
end.