OleDB: невозможно связать параметр DBTYPE_WSTR - получена ошибка DB_E_UNSUPPORTEDCONVERSION - PullRequest
2 голосов
/ 26 июня 2011

Вот контекст для библиотеки OpenSource:

  • Я вызываю библиотеку OleDB напрямую из неуправляемого кода (Delphi);
  • Я связываю параметры, создаваяIAccessor в ICommandText экземпляр OleDB;
  • У меня нет проблем с простыми типами, такими как DBTYPE_I8 или DBTYPE_DATE;
  • Я хотел бы связать текстовые параметры как DBTYPE_WSTR type, т.е. всегда как Unicode.

Проблема заключается в том, что для типа DBTYPE_WSTR я получаю ошибку OLEDB Error 80040E1D (DB_E_UNSUPPORTEDCONVERSION), «Запрошенное преобразование не поддерживается», когдаCommand.Execute называется.

Конечно, я пробовал с или без DBTYPE_BYREF макета данных (т.е. установив FIELDTYPE2OLEDB[ftUTF8]=DBTYPE_WSTR or DBTYPE_BYREF в приведенном ниже коде и изменив макет): та же проблема ..работает с DBTYPE_WSTR, но не с DBTYPE_STR.

Но если я изменю тип параметра с DBTYPE_WSTR на DBTYPE_STR (т. е. установим FIELDTYPE2OLEDB[ftUTF8]=DBTYPE_STR or DBTYPE_BYREF в приведенном ниже коде), команда будет выполнена безлюбая проблема.Но вместо этого я хотел бы использовать DBTYPE_WSTR wType, чтобы гарантировать, что ни один символ не будет потерян из-за текущего набора символов Ansi.

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

Я подключен к экземпляру Microsoft SQL Server 2008 R2.Код работает без параметров в запросе SQL или с параметрами int или float, но не с параметрами TEXT.

Вот основной пример кода:

Query.Execute('select * from Person.Address where AddressLine1 like ?;',true,['% Drive']);

AddressLine1 столбец определен как nvarchar(60) в AdventureWorks2008R2 эталонной базе данных, поэтому он должен сопоставляться с DBTYPE_WSTR, согласно официальной документации MSDN .

Полный исходный код соответствующего модуля доступен из нашего репозитория исходного кода .Код в методе TOleDBStatement.Execute, как таковой:

const
  PARAMTYPE2OLEDB: array[TSQLDBParamInOutType] of DBPARAMIO = (
    DBPARAMIO_INPUT, DBPARAMIO_OUTPUT, DBPARAMIO_INPUT or DBPARAMIO_OUTPUT);
  FIELDTYPE2OLEDB: array[TSQLDBFieldType] of DBTYPE = (
    DBTYPE_EMPTY, DBTYPE_NULL, DBTYPE_I8, DBTYPE_R8, DBTYPE_CY, DBTYPE_DATE,
    DBTYPE_WSTR or DBTYPE_BYREF, DBTYPE_BYTES or DBTYPE_BYREF);

  (...)
  OleDBCheck((fSession as IDBCreateCommand).
    CreateCommand(nil,IID_ICommandText,ICommand(fCommand)));
  fCommand.SetCommandText(DBGUID_DEFAULT,pointer(Utf8DecodeToRawUnicodeUI(aSQL)));
  P := pointer(fParams);
  SetLength(fParamBindings,fParamCount);
  B := pointer(fParamBindings);
  for i := 1 to fParamCount do begin
    B^.iOrdinal := i; // parameter index (starting at 1)
    B^.eParamIO := PARAMTYPE2OLEDB[P^.VInOut]; // parameter direction
    B^.wType := FIELDTYPE2OLEDB[P^.VType];     // parameter data type
    // set additional fields
    case P^.VType of
    ftInt64, ftDouble, ftCurrency, ftDate: begin
      // those types match the VInt64 binary representation :)
      B^.cbMaxLen := sizeof(Int64);
      B^.dwPart := DBPART_VALUE;
      B^.obValue := PAnsiChar(@P^.VInt64)-pointer(fParams);
    end;
    ftUTF8, ftBlob: begin
      // sent as DBTYPE_BYREF mapping directly the VRawByteString content
      B^.dwPart := DBPART_VALUE or DBPART_LENGTH or DBPART_STATUS;
      B^.obValue := PAnsiChar(@P^.VRawByteString)-pointer(fParams);
      B^.cbMaxLen := sizeof(Pointer);
      Len := length(P^.VRawByteString);
      if P^.VType=ftUTF8 then
        Len := Len shr 1; // expect length in WideChar count, excluding #0 
      P^.VInt64 := Len; // store length in unused VInt64 property
      B^.obLength := PAnsiChar(@P^.VInt64)-pointer(fParams);
      B^.obStatus := B^.obLength+4;
    end;
    end;
    inc(P);
    inc(B);
  end;
  OleDBConnection.OleDBCheck((fCommand as IAccessor).CreateAccessor(
    DBACCESSOR_PARAMETERDATA,fParamCount,Pointer(fParamBindings),0,
    fDBParams.HACCESSOR,nil));
 OleDBConnection.OleDBCheck(fCommand.Execute(
    nil,IID_IRowset,fDBParams,nil,@fRowSet),ParamsStatus);

Поэтому мой вопрос:

Как связать параметр B^.wType=DBTYPE_WSTR для столбца nvarchar()без ошибки DB_E_UNSUPPORTEDCONVERSION в fCommand.Execute.

Я подозреваю, что есть какое-то свойство для установки экземпляра ISession или ICommand или какой-либо флаг / опция для установки, но я не былсмог найти какой из документации MSDN.Любая помощь приветствуется!

1 Ответ

1 голос
/ 26 июня 2011

ОК ... спустя еще несколько часов, я думаю, что узнал.

Проблема была не в моем коде AFAIK, а в поставщике Microsoft SQL Server OleDB от Microsoft. Использование DBTYPE_WSTR, как говорится в официальной документации, просто не работает. Я не говорю, что MS писала ошибки, но я был действительно смущен документацией , которая ясно указала в той же строке: nvarchar DBTYPE_WSTR.

Я только что, наконец, попробовал с DBTYPE_BSTR, используя Ole WideString ... и он работал как положено!

Я просто предполагаю, что Microsoft и большинство потребителей OleDB просто используют DBTYPE_BSTR.

Вот некоторый рабочий код :

    ftUTF8: begin
      // mapping directly the WideString VText content
      B^.wType := DBTYPE_BSTR; // DBTYPE_WSTR just doesn't work :(
      B^.obValue := PAnsiChar(@P^.VText)-pointer(fParams);
      B^.cbMaxLen := sizeof(Pointer);
    end;

Что хорошо с WideString, так это то, что провайдер может изменить его размер в случае параметра Output или Input / Output (при вызове хранимых процедур).

...