The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 2,418 other followers

ODAC undocumented exception ‘Need Oracle 8 Call Interface’

Posted by jpluimers on 2021/06/16

I got this when taking over maintenance of some legacy code that called a stored procedure in a :

---------------------------
Debugger Exception Notification
---------------------------
Project LegacyProject.exe raised exception class Exception with message 'Need Oracle 8 Call Interface'.
---------------------------
Break   Continue   Help   
---------------------------

The problem with finding more information about this, is that there are virtual no hits on Oracle 8 Call Interface, OCI 8 or Oracle Call Interface 8.

A good document is [WayBack] OCI: Introduction and Upgrading (found via [Archive.is] “OCI 7” – Google Search), but it never mentions 8, only these terms occur:

  • release 7.x OCI
  • OCI release 7
  • OCI version 7
  • Oracle release 7 OCI
  • Release 8.0 of the OCI
  • 7.x OCI
  • Later OCI
  • Post-release 7.x OCI

The error happens on the first instance of at least an [Archive.is] TOraStoredProc Class or [Archive.is] TOraPackage Class that prepares or executes a stored procedure.

One problem is that this is an instance of a plain [WayBack] Exception Class , not a [WayBack] EDatabaseError Class or [Archive.is] EDAError Class.

What it means is that the UseOCI7 sub-property was set to True in your TOraSession property Options:

var
  MainOraSession: TOraSession;
begin
  MainOraSession := TOraSession.Create(Self);

  MainOraSession.Name := 'MainOraSession';
  MainOraSession.Username := 'ExammpleUser';
  MainOraSession.Server := 'ExampleTnsName';
  MainOraSession.LoginPrompt := False;
  MainOraSession.Options.UseOCI7 := True;

One thing to remember is that an TOraSession instance does not allow you to get to the underlying TOCIConnection instance, which does allow setting NLS information directly; see for instance the error at [WayBack] Not connected to oracle error – Devart Forums and old code at [WayBack] OraClasses.pas … | source code search engine and [WayBack] Ora.pas in … | source code search engine; UniDac is relatively similar [WayBack] UniDAC v3.7 …:OraClassesUni.pas (another explanation is in [WayBack] Setting the Client Info for the TOraAlerter extra session – Devart Forums).

This is because the underlying connection can be both OCI and Direct depending on the TOraSession.Options.Direct value: [WayBack] About Connection.Ping method when Direct Mode – Devart Forums.

After the first occurrence, you will not get this error again, you get way more spurious errors, especially on stored procedures in packages, where the stored procedure has 2 or more parameters. There you get errors like this one for TOraStoredProc:

ORA-06550: line 2, column 36:
PLS-00103: Encountered the symbol ":" when expecting one of the following:

   ( - + case mod new not null <an identifier>
   <a double-quoted delimited-identifier> <a bind variable>
   continue avg count current exists max min prior sql stddev
   sum variance execute forall merge time timestamp interval
   date <a string literal with character set specification>
   <a number> <a single-quoted SQL string> pipe
   <an alternatively-quoted string literal with character set speci.

When logging using a [Archive.is] TOraSQLMonitor Class instance (use the [Archive.is] TCustomDASQLMonitor.OnSQL Event with the [Archive.is] TOnSQLEvent Procedure Reference signature) you can get the underlying information of the TOraStoredProc (see the example code below that raises this):

Class:Ora.TOraStoredProc;Component:sprSetNls;Flag:tfQPrepare,Text:Prepare: begin
  SYS.DBMS_SESSION.SET_NLS(:PARAM, :VALUE);
end;
:PARAM(VARCHAR[22],IN)='NLS_NUMERIC_CHARACTERS' 
:VALUE(VARCHAR[4],IN)=''.,''

It fails equally when doing any DBMS_APPLICATION_INFO calls, for which the .NET version of ODAC has a special [WayBack] SetDbmsApplicationInfo Method in the [WayBack] OracleConnection Class. Too bad the Delphi version omits that.

When using TOraPackage, you can get different errors, like for instance this EDatabaseError:

Exception class EDatabaseError with message 'Parameter 'VALUE' not found'.

It basically means that through OCI, the parameter list could not be obtained, so the error is caught on the Delphi side instead of the Oracle side.

Using a TOraPackage is easier at design time, as it allows you to select the [Archive.is] TOraPackage.PackageName Property property using the underlying TOraSession. Somehow a TOraStoredProc does not allow you to select the [Archive.is] TOraStoredProc.StoredProcName Property at design time.

Related:

–jeroen

Example code that raises the ORA-06550/PLS-00103 combo

Full code (since you need a session too):

var
  MainOraSession: TOraSession;
  DbmsSessionSetNlsOraStoredProc: TOraStoredProc;
begin
  MainOraSession := TOraSession.Create(Self);
  try
    MainOraSession.Name := 'MainOraSession';
    MainOraSession.Username := 'FOO';
    MainOraSession.Server := 'BAR';
    MainOraSession.LoginPrompt := False;
    MainOraSession.Options.UseOCI7 := True;
    MainOraSession.Open();
    DbmsSessionSetNlsOraStoredProc := TOraStoredProc.Create(Self);
    try
      DbmsSessionSetNlsOraStoredProc.Name := 'DbmsSessionSetNlsOraStoredProc';
      DbmsSessionSetNlsOraStoredProc.StoredProcName := 'SYS.DBMS_SESSION.SET_NLS';
      DbmsSessionSetNlsOraStoredProc.Session := MainOraSession;
      DbmsSessionSetNlsOraStoredProc.Debug := True;
      with DbmsSessionSetNlsOraStoredProc.ParamData.Add do 
      begin
        DataType := ftString;
        Name := 'PARAM';
        ParamType := ptInput;
        Value := nil;
      end;
      with DbmsSessionSetNlsOraStoredProc.ParamData.Add do
      begin
        DataType := ftString;
        Name := 'VALUE';
        ParamType := ptInput;
        Value := nil;
      end;
      DbmsSessionSetNlsOraStoredProc.ParamByName('PARAM').AsString := sParam;
      DbmsSessionSetNlsOraStoredProc.ParamByName('VALUE').AsString := sValue;
      DbmsSessionSetNlsOraStoredProc.Prepare();
      DbmsSessionSetNlsOraStoredProc.ExecProc();
    finally
      DbmsSessionSetNlsOraStoredProc.Free();
    end;
  finally
    MainOraSession();
  end;
end;

The below code (add a similar session) will also crash, but with a different error Exception class EDatabaseError with message 'Parameter 'VALUE' not found'.:

var
  DbmsSessionOraPackage: TOraPackage;
begin
  DbmsSessionOraPackage := TOraPackage.Create(Self);
  try
    DbmsSessionOraPackage.Name := 'DbmsSessionOraPackage';
    DbmsSessionOraPackage.Debug := True;
    DbmsSessionOraPackage.Session := dbSession;
    DbmsSessionOraPackage.PackageName := 'SYS.DBMS_SESSION';

    DbmsSessionOraPackage.ExecProcEx('SET_NLS', ['PARAM', 'NLS_NUMERIC_CHARACTERS', 'VALUE', '''.,''']);
  finally
    DbmsSessionOraPackage.Free();
  end;
end;

Using TOraSqlMonitor

The below piece of code saved me way too many times; just define EnableTOraSqlMonitor to enable it.

If you want you can output to [Archive.is] DBMonitor instead of using [WayBack]OutputDebugString.

procedure TSessionDataModule.DataModuleCreate(Sender: TObject);
begin
{$ifdef EnableTOraSqlMonitor}
  FOraSQLMonitor := TOraSQLMonitor.Create(Self);

  FOraSQLMonitor.Name := Name + '_OraSQLMonitor';
  FOraSQLMonitor.TraceFlags := [tfQPrepare, tfQExecute, tfQFetch, tfError, tfStmt, tfConnect, tfTransact, tfBlob, tfService, tfMisc, tfParams, tfObjDestroy, tfPool];
  FOraSQLMonitor.OnSQL := OraSQLMonitor_OnSQL;
{$endif EnableTOraSqlMonitor}
end;

procedure TSessionDataModule.DataModuleDestroy(Sender: TObject);
begin
{$ifdef EnableTOraSqlMonitor}
  FOraSQLMonitor.Free();
  FOraSQLMonitor := nil;
{$endif EnableTOraSqlMonitor}
end;

procedure TSessionDataModule.OraSQLMonitor_OnSQL(Sender: TObject; Text: string; Flag: TDATraceFlag);
var
  Name: string;
  Line: string;
begin
  // https://www.devart.com/sdac/docs/devart.dac.tdatraceflag.htm
  // TDATraceFlag = (tfQPrepare, tfQExecute, tfQFetch, tfError, tfStmt, tfConnect, tfTransact, tfBlob, tfService, tfMisc, tfParams, tfObjDestroy, tfPool);
  if Assigned(Sender) then
  begin
    Name := Sender.QualifiedClassName;
    if Sender is TComponent then
      Name := Format('%s;Component:%s', [Name, TComponent(Sender).Name]);
  end
  else
    Name := 'nil';
  Line := Format('Class:%s;Flag:%s,Text:%s', [Name, TEnum.GetName(Flag), Text]);
  OutputDebugString(PChar(Line));
end;

For my own reference: [WayBack] How to set OCI Unicode environment use in 9.2.5? – Devart Forums

Please send me results of the following queries:

CODE: SELECT ALL

SELECT USERENV ('language') from dual; 

SELECT * FROM V$NLS_PARAMETERS;

and:

–end–

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 
%d bloggers like this: