Click here to view and discuss this page in DocCommentXchange. In the future, you will be sent there automatically.

SQL Anywhere 11.0.1 (Deutsch) » SQL Anywhere Server - Programmierung » SQL Anywhere Datenzugriff-APIs » Unterstützung für externe Umgebungen in SQL Anywhere

 

Die externen ESQL- und ODBC-Umgebungen

SQL Anywhere besaß für einige Zeit die Fähigkeit, kompilierte native Funktionen, die in C oder C++ geschrieben waren, aufzurufen. Wenn diese Prozeduren vom Server ausgeführt wurden, wurde die dynamische Verknüpfungsbibliothek (DLL) oder das gemeinsam genutzte Objekt (Shared Object) immer vom Datenbankserver geladen und die Aufrufe der nativen Funktion wurden immer vom Datenbankserver durchgeführt. Der native Aufruf durch den Datenbankserver ist zwar am effizientesten, kann aber schwerwiegende Konsequenzen haben, wenn eine native Funktion Probleme verursacht. Dies gilt besonders, wenn die native Funktion in eine Endlosschleife gerät, weil dann der Datenbankserver hängen würde, oder wenn die native Funktion einen Fehler bewirkt, weil dann der Datenbankserver abstürzen würde. Aus diesem Grund haben Sie die Möglichkeit, kompilierte native Funktionen außerhalb des Datenbankservers in einer externen Umgebung auszuführen. Es gibt einige wichtige Vorteile, eine kompilierte native Funktion in einer externen Umgebung auszuführen:

  1. Der Datenbankserver hängt nicht oder stürzt nicht ab, wenn eine native Funktion Probleme verursacht.

  2. Die native Funktion kann so geschrieben werden, dass sie ODBC, Embedded SQL (ESQL) oder die SQL Anywhere C API verwendet und in der Lage ist, serverseitige Aufrufe zurück zum Datenbankserver durchzuführen, ohne eine Verbindung herstellen zu müssen.

  3. Die native Funktion kann an den Datenbankserver eine Ergebnismenge zurückgeben.

  4. In einer externen Umgebung kann ein 32-Bit-Datenbankserver mit einer kompilierten nativen 64-Bit-Funktion kommunizieren, bzw. umgekehrt. Beachten Sie, dass dies nicht möglich ist, wenn die kompilierten nativen Funktionen direkt in den Adressenbereich des Datenbankservers geladen werden. Eine 32-Bit-Bibliothek kann nur durch einen 32-Bit-Datenbankserver und eine 64-Bit-Bibliothek kann nur durch einen 64-Bit-Datenbankserver geladen werden.

Das Ausführen einer kompilierten nativen Funktion in einer externen Umgebung statt im Datenbankserver führt zu einer gewissen Performanceeinbuße.

Auch muss die kompilierte native Funktion die API für native Funktionsaufrufe verwenden, um Informationen an die native Funktion zu übergeben und Informationen von ihr zu erhalten. Eine Beschreibung dieser API finden Sie unter API der externen Funktionen in SQL Anywhere.

Um eine kompilierte native C-Funktion in einer externen Umgebung statt im Datenbankserver auszuführen, wird die gespeicherte Prozedur oder Funktion mit der Klausel EXTERNAL NAME definiert, gefolgt vom Attribut LANGUAGE, das C_ESQL32, C_ESQL64, C_ODBC32 oder C_ODBC64 angibt.

Anders als bei den externen Perl-, PHP- und Java-Umgebungen müssen Sie keinen Quellcode oder kompilierte Objekte in der Datenbank installieren. Dadurch müssen Sie keine INSTALL-Anweisungen ausführen, bevor Sie die externen ESQL- und ODBC-Umgebungen verwenden.

Hier ist ein Beispiel einer in C++ geschriebenen Funktion, die im Datenbankserver oder in einer externen Umgebung ausgeführt werden kann.

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extfnapi.h"
    
BOOL APIENTRY DllMain( HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
    )
{
    return TRUE;
}

// Note: extfn_use_new_api used only for
// execution in the database server

extern "C" __declspec( dllexport )
a_sql_uint32 extfn_use_new_api( void )
{
    return( EXTFN_API_VERSION );
}
    
extern "C" __declspec( dllexport )
void SimpleCFunction(
    an_extfn_api *api,
    void *arg_handle )
{
    short               result;
    an_extfn_value      arg;
    an_extfn_value      retval;
    int *               intp;
    int                 i, j, k;
    
    j = 1000;
    k = 0;
    for( i = 1; i <= 4; i++ )
    {
        result = api->get_value( arg_handle, i, &arg );
        if( result == 0 || arg.data == NULL ) break;
        if( arg.type & DT_TYPES != DT_INT ) break;
        intp = (int *) arg.data;
        k += *intp * j;
        j = j / 10;
    }
    retval.type = DT_INT;
    retval.data = (void*)&k;
    retval.piece_len = retval.len.total_len = 
       (a_sql_uint32) sizeof( int );
    api->set_value( arg_handle, 0, &retval, 0 );    
    return;
}

Wenn sie in eine dynamische Verknüpfungsbibliothek oder in ein gemeinsam genutztes Objekt kompiliert wird, kann diese Funktion von einer externen Umgebung aufgerufen werden. Ein ausführbares Image namens dbexternc11 wird vom Datenbankserver gestartet und lädt die dynamische Verknüpfungsbibliothek oder das gemeinsam genutzte Objekt. In SQL Anywhere stehen verschiedene Versionen dieses Programms zur Verfügung. Unter Windows gibt es beispielsweise sowohl 32-Bit-Programme als auch 64-Bit-Programme.

Beachten Sie, das die 32-Bit- oder die 64-Bit-Version des Datenbankservers verwendet werden kann und beide Versionen die 32-Bit- oder die 64-Bit-Versionen von dbexternc11 starten können. Dies ist einer der Vorteile bei der Verwendung der externen Umgebung. Beachten Sie, dass dbexternc11, nachdem es vom Datenbankserver gestartet wurde, erst beendet wird, wenn die Verbindung beendet oder die Anweisung STOP EXTERNAL ENVIRONMENT (mit dem korrekten Umgebungsnamen) ausgeführt wird. Jede Verbindung, die einen externen Umgebungsaufruf ausführt, erhält ihre eigene Kopie von dbexternc11.

Um die kompilierte native Funktion SimpleCFunction aufzurufen, wird ein Wrapper folgendermaßen definiert:

CREATE FUNCTION SimpleCDemo( 
  IN arg1 INT, 
  IN arg2 INT, 
  IN arg3 INT, 
  IN arg4 INT )
RETURNS INT
EXTERNAL NAME 'SimpleCFunction@d:\\c\\extdemo.dll' 
LANGUAGE C_ODBC32;

Dies ist beinahe identisch mit der Art der Beschreibung einer kompilierten nativen Funktion, wenn sie in den Adressenbereich des Datenbankservers geladen wird. Der einzige Unterschied ist die Verwendung der LANGUAGE C_ODBC32-Klausel. Diese Klausel gibt an, dass SimpleCDemo eine Funktion ist, die in einer externen Umgebung ausgeführt wird und 32-Bit-ODBC-Aufrufe verwendet. Die Sprachenspezifikation C_ESQL32, C_ESQL64, C_ODBC32 oder C_ODBC64 teilt dem Datenbankserver mit, ob die externe C-Funktion 32-Bit- bzw. 64-Bit-ODBC-, ESQL- oder SQL Anywhere C API-Aufrufe ausgibt, wenn serverseitige Anforderungen erfolgen.

Wenn die native Funktion keine ODBC-, ESQL- oder SQL Anywhere C API-Aufrufe verwendet, um serverseitige Anforderungen durchzuführen, kann entweder C_ODBC32 oder C_ESQL32 bei 32-Bit-Anwendungen und entweder C_ODBC64 oder C_ESQL64 bei 64-Bit-Anwendungen verwendet werden. Dies ist in der oben gezeigten externen C-Funktion der Fall. Sie verwendet keine dieser APIs.

Zum Ausführen der kompilierten nativen Beispielfunktion verwenden Sie die folgende Anweisung.

SELECT SimpleCDemo(1,2,3,4);

Für eine serverseitige ODBC-Anbindung muss der C-/C++-Code die Standarddatenbankverbindung verwenden. Um ein Handle für die Datenbankverbindung zu erhalten, rufen Sie get_value mit einem EXTFN_CONNECTION_HANDLE_ARG_NUM-Argument auf. Das Argument teilt dem Datenbankserver mit, die aktuelle externe Umgebungsverbindung zurückzugeben, anstatt eine neue zu öffnen.

#include <windows.h>
#include <stdio.h>
#include "odbc.h"
#include "extfnapi.h"
    
BOOL APIENTRY DllMain( HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
    )
{
    return TRUE;
}
    
extern "C" __declspec( dllexport ) 
void ServerSideFunction( an_extfn_api *api, void *arg_handle )
{
    short               result;
    an_extfn_value      arg;
    an_extfn_value      retval;
    SQLRETURN           ret;

    ret = -1;
    // set up the return value struct
    retval.type = DT_INT;
    retval.data = (void*) &ret;
    retval.piece_len = retval.len.total_len =
        (a_sql_uint32) sizeof( int );

    result = api->get_value( arg_handle,
                    EXTFN_CONNECTION_HANDLE_ARG_NUM,
                    &arg );
    if( result == 0 || arg.data == NULL )
    {
        api->set_value( arg_handle, 0, &retval, 0 );
        return;
    }

    HDBC dbc = (HDBC)arg.data;
    HSTMT stmt = SQL_NULL_HSTMT;
    ret = SQLAllocHandle( SQL_HANDLE_STMT, dbc, &stmt );
    if( ret != SQL_SUCCESS ) return;
    ret = SQLExecDirect( stmt,
            (SQLCHAR *) "INSERT INTO odbcTab "
                "SELECT table_id, table_name "
                "FROM SYS.SYSTAB", SQL_NTS );
    if( ret == SQL_SUCCESS )
    {
        SQLExecDirect( stmt,
            (SQLCHAR *) "COMMIT", SQL_NTS );
    }
    SQLFreeHandle( SQL_HANDLE_STMT, stmt );
    
    api->set_value( arg_handle, 0, &retval, 0 );
    return;
}

Wenn der vorstehende ODBC-Code in der Datei extodbc.cpp gespeichert ist, kann er für Windows unter Verwendung der folgenden Befehle kompiliert werden (unter der Annahme, dass sich die SQL Anywhere-Software im Ordner c:\sa11 befindet und Microsoft Visual C++ installiert ist).

cl extodbc.cpp /LD /Ic:\sa11\sdk\include odbc32.lib

Das folgende Beispiel erstellt eine Tabelle, definiert eine gespeicherte Prozedur als Wrapper für die kompilierte native Funktion und ruft diese ann auf, um die Tabelle mit Daten zu füllen.

CREATE TABLE odbcTab(c1 int, c2 char(128));

CREATE FUNCTION ServerSideODBC( )
RETURNS INT
EXTERNAL NAME 'ServerSideFunction@extodbc.dll'
LANGUAGE C_ODBC32;

SELECT ServerSideODBC();

// The following statement should return two identical rows
SELECT COUNT(*) FROM odbcTab 
UNION ALL 
SELECT COUNT(*) FROM SYS.SYSTAB;

In diesem Sinne gilt: Für eine serverseitige ESQL-Anbindung muss der C-/C++-Code die Standarddatenbankverbindung verwenden. Um ein Handle für die Datenbankverbindung zu erhalten, rufen Sie get_value mit einem EXTFN_CONNECTION_HANDLE_ARG_NUM-Argument auf. Das Argument teilt dem Datenbankserver mit, die aktuelle externe Umgebungsverbindung zurückzugeben, anstatt eine neue zu öffnen.

#include <windows.h>
#include <stdio.h>

#include "sqlca.h"
#include "sqlda.h"
#include "extfnapi.h"

BOOL APIENTRY DllMain( HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
    )
{
    return TRUE;
}

EXEC SQL INCLUDE SQLCA;
static SQLCA *_sqlc;
EXEC SQL SET SQLCA "_sqlc";
EXEC SQL WHENEVER SQLERROR { ret = _sqlc->sqlcode; };

extern "C" __declspec( dllexport ) 
void ServerSideFunction( an_extfn_api *api, void *arg_handle )
{
    short               result;
    an_extfn_value      arg;
    an_extfn_value      retval;

    EXEC SQL BEGIN DECLARE SECTION;
    char *stmt_text =
        "INSERT INTO esqlTab "
            "SELECT table_id, table_name "
            "FROM SYS.SYSTAB";
    char *stmt_commit =
        "COMMIT";
    EXEC SQL END DECLARE SECTION;

    int ret = -1;

    // set up the return value struct
    retval.type = DT_INT;
    retval.data = (void*) &ret;
    retval.piece_len = retval.len.total_len =
        (a_sql_uint32) sizeof( int );

    result = api->get_value( arg_handle,
                    EXTFN_CONNECTION_HANDLE_ARG_NUM,
                    &arg );
    if( result == 0 || arg.data == NULL )
    {
        api->set_value( arg_handle, 0, &retval, 0 );
        return;
    }
    ret = 0;
    _sqlc = (SQLCA *)arg.data;

    EXEC SQL EXECUTE IMMEDIATE :stmt_text;
    EXEC SQL EXECUTE IMMEDIATE :stmt_commit;

    api->set_value( arg_handle, 0, &retval, 0 );
}

Wenn der vorstehende Embedded SQL-Code in der Datei extesql.cpp gespeichert ist, kann er für Windows unter Verwendung der folgenden Befehle kompiliert werden (unter der Annahme, dass sich die SQL Anywhere-Software im Ordner c:\sa11 befindet und Microsoft Visual C++ installiert ist).

sqlpp extesql.sqc extesql.cpp
cl extesql.cpp /LD /Ic:\sa11\sdk\include c:\sa11\sdk\lib\x86\dblibtm.lib

Das folgende Beispiel erstellt eine Tabelle, definiert eine gespeicherte Prozedur als Wrapper für die kompilierte native Funktion und ruft diese ann auf, um die Tabelle mit Daten zu füllen.

CREATE TABLE esqlTab(c1 int, c2 char(128));

CREATE FUNCTION ServerSideESQL( )
RETURNS INT
EXTERNAL NAME 'ServerSideFunction@extesql.dll'
LANGUAGE C_ESQL32;

SELECT ServerSideESQL();

// The following statement should return two identical rows
SELECT COUNT(*) FROM esqlTab 
UNION ALL 
SELECT COUNT(*) FROM SYS.SYSTAB;

Wie in den vorherigen Beispielen muss der C/C++-Code die Standard-Datenbankverbindung benutzen, um SQL Anywhere C API-Aufrufe zu verwenden. Um ein Handle für die Datenbankverbindung zu erhalten, rufen Sie get_value mit einem EXTFN_CONNECTION_HANDLE_ARG_NUM-Argument auf. Das Argument teilt dem Datenbankserver mit, die aktuelle externe Umgebungsverbindung zurückzugeben, anstatt eine neue zu öffnen. Das folgende Beispiel zeigt das Framework, um den Verbindungshandle zu erhalten, die C API-Umgebung zu initialisieren und den Verbindungshandle in ein Verbindungsobjekt (a_sqlany_connection) umzuwandeln, das mit der SQL Anywhere C API verwendet werden kann.

#include <windows.h>
#include "sacapidll.h"
#include "extfnapi.h"

BOOL APIENTRY DllMain( HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
    )
{
    return TRUE;
}

extern "C" __declspec( dllexport )
void ServerSideFunction( an_extfn_api *extapi, void *arg_handle )
{
    short               result;
    an_extfn_value      arg;
    an_extfn_value      retval;
    unsigned            offset;
    char                *cmd;
    
    SQLAnywhereInterface  capi;
    a_sqlany_connection * sqlany_conn;
    unsigned int          max_api_ver;

    result = extapi->get_value( arg_handle,
                    EXTFN_CONNECTION_HANDLE_ARG_NUM,
                    &arg );

    if( result == 0 || arg.data == NULL )
    {
        return;
    }
    if( !sqlany_initialize_interface( &capi, NULL ) )
    {
        return;
    } 
    if( !capi.sqlany_init( "MyAPP",
            SQLANY_CURRENT_API_VERSION,
            &max_api_ver ) )
    {
        sqlany_finalize_interface( &capi );
        return;
    }
    sqlany_conn = sqlany_make_connection( arg.data );

    // processing code goes here

    capi.sqlany_fini();

    sqlany_finalize_interface( &capi );
    return;
}

Wenn der vorstehende C-Code in der Datei extcapi.cpp gespeichert ist, kann er für Windows unter Verwendung der folgenden Befehle kompiliert werden (unter der Annahme, dass sich die SQL Anywhere-Software im Ordner c:\sa11 befindet und Microsoft Visual C++ installiert ist).

cl /LD /Tp extcapi.c /Tp c:\sa11\SDK\C\sacapidll.c /Ic:\sa11\SDK\Include d:\sa11\SDK\Lib\X86\dbcapi.lib

Das folgende Beispiel definiert eine gespeicherte Prozedur als Wrapper für die kompilierte native Funktion und ruft diese dann auf.

CREATE FUNCTION ServerSideC()
RETURNS INT
EXTERNAL NAME 'ServerSideFunction@extcapi.dll'
LANGUAGE C_ESQL32;

SELECT ServerSideC();

Das LANGUAGE-Attribut im Beispiel oben gibt C_ESQL32 an. Bei 64-Bit-Anwendungen würden Sie C_ESQL64 verwenden. Sie müssen das Embedded SQL-Sprachattribut verwenden, weil die SQL Anywhere C API auf derselben Ebene (Bibliothek) wie ESQL kompiliert ist.

Wie bereits erwähnt, erhält jede Verbindung, die einen externen Umgebungsaufruf ausführt, ihre eigene Kopie von dbexternc11. Dieses Programm wird automatisch vom Datenbankserver geladen, wenn zum ersten Mal ein externer Umgebungsaufruf durchgeführt wird. Sie können allerdings die Anweisung START EXTERNAL ENVIRONMENT verwenden, um dbexternc11 vorauszuladen. Dies ist nützlich, wenn Sie die geringe Verzögerung umgehen wollen, die auftritt, wenn ein externer Umgebungsaufruf zum ersten Mal ausgeführt wird. Hier ist ein Beispiel für die Anweisung.

START EXTERNAL ENVIRONMENT C_ESQL64

Ein weiterer Fall, bei dem ein Vorausladen von dbexternc11 nützlich ist, liegt vor, wenn Sie eine Fehlersuche in Ihrer externen Funktion durchführten wollen. Sie können den Debugger so einsetzen, dass er sich an den laufenden dbexternc11 anhängt und Breakpoints in Ihre externe Funktion setzt.

Die Anweisung STOP EXTERNAL ENVIRONMENT ist nützlich, wenn Sie eine dynamische Verknüpfungsbibliothek oder ein gemeinsam genutztes Objekt aktualisieren. Sie beendet das native Bibliotheksladeprogramm dbexternc11 bei der laufenden Verbindung und gibt dadurch den Zugriff auf die dynamische Verknüpfungsbibliothek oder das gemeinsam genutzte Objekt frei. Wenn mehrere Verbindungen die dynamische Verknüpfungsbibliothek oder das gemeinsam genutzte Objekt verwenden, müssen alle ihre Kopien von dbexternc11 beendet werden. Der entsprechende externe Umgebungsname muss in der Anweisung STOP EXTERNAL ENVIRONMENT angegeben werden.. Hier ist ein Beispiel für die Anweisung.

STOP EXTERNAL ENVIRONMENT C_ESQL64

Um eine Ergebnismenge von der externen Funktion zurückzugeben, muss die kompilierte native Funktion die API für native Funktionsaufrufe verwenden. Eine umfassende Beschreibung dieser API finden Sie unter API der externen Funktionen in SQL Anywhere. Im Folgenden finden Sie wichtige Hinweise über die Rückgabe von Ergebnismengen.

Das folgende Codefragment zeigt, wie Sie eine Informationsstruktur für Ergebnismengen einrichten. Es enthält eine Spaltenzählung, einen Zeiger auf ein Array von Spalteninformationsstrukturen und einen Zeiger auf ein Array von Spaltendatenwert-Strukturen. Das Beispiel verwendet auch die SQL Anywhere C-API.

an_extfn_result_set_info    rs_info;

int columns = capi.sqlany_num_cols( sqlany_stmt );

an_extfn_result_set_column_info *col_info =
    (an_extfn_result_set_column_info *)
    malloc( columns * sizeof(an_extfn_result_set_column_info) );

an_extfn_result_set_column_data *col_data =
    (an_extfn_result_set_column_data *)
    malloc( columns * sizeof(an_extfn_result_set_column_data) );

rs_info.number_of_columns   = columns;
rs_info.column_infos        = col_info;
rs_info.column_data_values  = col_data;

Das folgende Codefragment zeigt, wie Sie die Ergebnismenge beschreiben. Es verwendet die SQL Anywhere C-API, um Spalteninformationen für eine SQL-Abfrage zu erhalten, die vorher durch die C-API ausgeführt wurde. Die Informationen, die von der SQL Anywhere C-API für jede Spalte bezogen werden, werden in einen Spaltennamen-, Typ-, Breiten-, Index- und Nullwert-Indikator umgewandelt, der verwendet wird, um die Ergebnismenge zu beschreiben.

a_sqlany_column_info        info;
for( int i = 0; i < columns; i++ )
{
    if( sqlany_get_column_info( sqlany_stmt, i, &info ) )
    {
        // set up a column  description
        col_info[i].column_name  = info.name;
        col_info[i].column_type  = info.native_type;
        switch( info.native_type )
        {
            case DT_DATE:       // DATE is converted to string by C API
            case DT_TIME:       // TIME is converted to string by C API
            case DT_TIMESTAMP:  // TIMESTAMP is converted to string by C API
            case DT_DECIMAL:    // DECIMAL is converted to string by C API
                col_info[i].column_type  = DT_FIXCHAR;
                break;
            case DT_FLOAT:      // FLOAT is converted to double by C API
                col_info[i].column_type  = DT_DOUBLE;
                break;
            case DT_BIT:        // BIT is converted to tinyint by C API
                col_info[i].column_type  = DT_TINYINT;
                break;
        }
        col_info[i].column_width = info.max_size;
        col_info[i].column_index = i + 1; // column indices are origin 1
        col_info[i].column_can_be_null = info.nullable;
    }
}
// send the result set description
if( extapi->set_value( arg_handle,
                    EXTFN_RESULT_SET_ARG_NUM,
                    (an_extfn_value *)&rs_info,
                    EXTFN_RESULT_SET_DESCRIBE ) == 0 )
{
    // failed
    free( col_info );
    free( col_data );
    return;
}

Sobald die Ergebnismenge beschrieben wurde, können die Ergebnismengenzeilen zurückgegeben werden. Das folgende Codefragment zeigt, wie die Zeilen der Ergebnismenge zurückgegeben werden. Es verwendet die SQL Anywhere C-API, um die Zeilen für eine SQL-Abfrage abzurufen, die vorher durch die C API ausgeführt wurde. Die von der SQL Anywhere C API zurückgegebenen Zeilen werden einzeln an die aufrufende Umgebung zurückgesendet. Das Array von Spaltendatenwert-Strukturen muss aufgefüllt werden, bevor die einzelnen Zeilen zurückgegeben werden. Die Spaltendatenwert-Struktur besteht aus einem Spaltenindex, einem Zeiger auf einen Datenwert, einer Datenlänge und einem Append-Parameter (Einfügeindikator).

a_sqlany_data_value *value = (a_sqlany_data_value *)
    malloc( columns * sizeof(a_sqlany_data_value) );

while( capi.sqlany_fetch_next( sqlany_stmt ) )
{
    for( int i = 0; i < columns; i++ )
    {
        if( capi.sqlany_get_column( sqlany_stmt, i, &value[i] ) )
        {
            col_data[i].column_index = i + 1;
            col_data[i].column_data  = value[i].buffer;
            col_data[i].data_length  = (a_sql_uint32)*(value[i].length);
            col_data[i].append  = 0;
            if( *(value[i].is_null) )
            {
                // Received a NULL value
                col_data[i].column_data = NULL;
            }
        }
    }
    if( extapi->set_value(  arg_handle,
                        EXTFN_RESULT_SET_ARG_NUM,
                        (an_extfn_value *)&rs_info,
                        EXTFN_RESULT_SET_NEW_ROW_FLUSH ) == 0 )
    {
        // failed
        free( value );
        free( col_data );
        free( col_data );
        extapi->set_value( arg_handle, 0, &retval, 0 );
        return;
    }
}

Weitere Hinweise finden Sie unter API der externen Funktionen in SQL Anywhere.

Weitere Hinweise zur Durchführung von serverseitigen Anforderungen und zur Rückgabe von Ergebnismengen durch eine externe Funktionen finden Sie in den Beispielen unter Beispielverzeichnis\SQLAnywhere\ExternalEnvironments\ExternC.