Jonathan Groff
Tri-County Metropolitan Transportation District of Oregon
4012 SE 17th Avenue
Portland, Oregon 97202
BUILDING DATABASE GATEWAYS WITH
INTER-APPLICATION COMMUNICATION (IAC).
Abstract: With the Inter-Application Communication
functionality introduced in ArcInfo V7.0, it is
now possible to construct gateways to any database
management system that supports a CLI (Call-Level
Interface) or some form of API (Application Programming
Interface). Most DBMS provide such an environment.
Using the 'aiserver' stubs included as part of the
ArcInfo V7.0 distribution, the appropriate DBMS
function calls are inserted to correspond to the
IAC procedure numbers desired. The 'aiserver' is
linked with the DBMS function library and is run as
a background process. Using AML (Arc Macro Language),
it is possible to access this server from any
workstation on the network without cumbersome
middleware. The construction of the ArcInfo to IBM
DB2 (SQL/DS on VM) gateway developed at Tri-Met will
be discussed.
INTRODUCTION:
The IAC mechanism provided in V7.0 is a welcome step
forward in providing open system connectivity to ArcInfo.
This functionality is provided by utilizing RPC
(Remote Procedure Call), a tried and true component of the
UNIX and now MS Windows environment. Most of the painful
details of implementing an RPC application are shielded from
the user by the IAC mechanism and the supplied server stubs.
On the server side, the 'aiserver' is a series of C
functions which can be dispatched by an AML IAC function call.
By creating generic AML routines and some special AML connection
modules, any number of database environments can be accessed from
server processes running on diverse platforms.
At Tri-Met, this manifests itself in an ArcInfo to SQL/DS
(DB2/VM) gateway. At this writing the server is in production
running on an IBM RS/6000 590. IBM DDCS (Distributed Database
Connection Services) operating over a 16Mb/s token-ring SNA
(System Network Architecture) channel to the ES/9000 Mainframe
provides the connection to DB2/VM. Currently, three ArcInfo
workstations are accessing the server from AIX, DEC OSF/1, and
SOLARIS. By connecting to the RPC over the network
from these UNIX workstations, queries and data can be sent and
received via the RS/6000 server, then over the SNA channel to the
DB2/VM environment. Figures 1, 2, and 3 illustrate the physical,
network, and database connections.
DESIGN GOALS:
The primary objective of this project was to build a
server that could be adapted to whatever DBMS the user would
encounter. In addition, no supplemental software (middleware)
should be necessary; the system should work with ArcInfo and
the target DBMS 'out of the box', regardless of platform,
from anywhere on the corporate LAN, WAN, and even the Internet.
In addition, the system should support a moderate level of
security. Access to remote system passwords should be hidden
from the user, and a means to securely establish and keep a
connection should exist. The server should also incur little
overhead even under heavy loads. Other criteria include the
ability to monitor connections, disconnections and transactions,
and easily bring the servers up and down. Some degree of
isolation should also exist wherein the peccadillos of one
user do not adversely affect another.
SUPPLIED ArcInfo STUBS:
A brief description of the components supplied with
ArcInfo and the manner in which they are utilized may serve
to illuminate. The primary elements are the 'aiserver' stubs.
This C library is compiled and linked as an RPC application.
A header file called 'prognum.h' contains the 8 digit
hexadecimal number that is the RPC registration number of
the module. The registration number along with the server
name is used by the AML function [iacconnect] to establish a
connection with that RPC, on that server. By changing this
header and re-compiling the stubs, multiple servers can be
constructed. Appendix A contains a version of the aiserver
stubs with pseudocode added for the IAC database gateway
functions.
DESCRIPTION OF IAC SERVER FUNCTIONS:
Making a Connection:
The initial step is to establish a connection to the IAC
server using the [iacconnect] AML function. The syntax of this
function looks like this:
&sv iachandle = [iacconnect SERVER_NAME RPC_HEX_PROGNUM ~
RPC_VERSION_NUMBER ~
RETURN_STATUS]
Conversely, the disconnect is as follows:
&sv dum = [iacdisconnect %iachandle%]
After establishing a connection to the server, a
connection to the database is then attempted. In order to gain
access to the database it is necessary to call function 80 in the
IAC Server (Functions begin at 80 in the hope that if there are
aiserver functions already in use no conflicts will arise).
In order to obtain a successful return from the database connect,
it is necessary to specify a profile name as the function argument.
This profile name is located in the file /usr/ddbprofile and
contains the profile name, RPC PROGNUM, database name, user name
on the remote system, and the remote system password. The file
should have read-write permissions for user only, and the owner
should be root. This allows only root to view/modify it,
providing a modicum of security as the user never sees the
remote system login name or password. By using the &encode
&encrypt and &echo &off arc commands on the module where
[iacconnect] is called, the user can be prevented
from seeing the profile fields, thereby enhancing security.
A line of the /usr/ddbprofile file looks like this:
PROFILE:0x42000000,database_name,user_name,password
Each RPC registration number is unique to a server module.
Each profile name uniquely identifies a server module and denotes
the database name, login user and password. The same database and
user name can be used by multiple profiles but the nature of the
DB2 CLI's allocation and deallocation of global handles demand
that connection to each server be by one user and one database.
Of course, multiple connections to the same or different databases
can be established through several profiles/servers. There is a
benefit in that each user is encapsulated within a process and
therefore can be easily manipulated by standard system
administration tools.
Server Function Invocation:
The server functions are called by the [iacrequest] AML
function. The ordinal number of the function in the 'aiserver'
stubs is passed as one of the arguments. You may pass an argument
to the numbered function. The result of the call is placed in
the variable on the left side of the statement. The format of
the [iacrequest] function is:
&sv res = [iacrequest %iachandle% IAC_FUNCTION_ORDINAL ~
IAC_ARGUMENT ~
RETURN_STATUS ~
OPTIONAL_TIMEOUT_IN_SECONDS]
So, using our connection handle from [iacconnect], we
connect to the database as follows:
&sv stmt_handle = [iacrequest %iachandle% 80 PROFILE rstat]
Where the variable 'stmt_handle' is a statement
handle returned from the IAC Server. This ten character hex value
serves the dual functions of providing authentication to the
server and denoting the statement handle in use. The handle is
encoded at the beginning of each request to the Server. Up to
100 connections/statement handles can be allocated per server
process.
A highly recommended practice is to free a statement handle
after use. Invoke function 99 of the IAC Server as follows:
&sv dum = [iacrequest %iachandle% 99 %stmt_handle% rstat]
In the working model, an AML was built to facilitate
connection and disconnection. Let's look at iac_connect in its
entirety:
/*------------------
/* IAC Server connection module
/* &r iac_connect server .rsvc
/* &r iac_connect handle .rsvc .handle
/* &r iac_connect free .rsvc .handle
/* &r iac_connect disconnect .rsvc .handle
/* ..
/* (C) J. Groff/Tri-Met 8/95
/*-------------------
&args .iop .ichandl .ishandl
/* be quiet: for security
&echo &off
/* these vars define the server name, rpc registry, and
/* profile name
&sv server = bigbop
&sv rpcreg = 42000000
&sv profile = PROFILE
/*
&select %.iop%
&when server
&do
&sv %.ichandl% = [iacconnect %server% %rpcreg% 1 rstat]
&return
&end
&when handle
&do
&sv %.ishandl% = ~
[iacrequest [value %.ichandl%] 80 %profile% rstat]
&if [extract 1 [quote [value %.ishandl%]]] = ERROR &then
&return &error Could Not Get Statement Handle...
&return
&end
&when free
&do
&sv dum = ~
[iacrequest [value %.ichandl%] 99 ~
[value %.ishandl%] rstat]
&dv %.ishandl%
&return
&end
&when disconnect
&do
&sv dum = [iacrequest [value %.ichandl%] 97~
[value %.ishandl%] rstat]
&sv dum = [iacdisconnect [value %.ichandl%]]
&dv %.ishandl% %.ichandl%
&return
&end
&otherwise
&return &error Unknown Operation.....
&end
Executing a Remote SQL Statement:
Once the server connection and statement handle have been
acquired, the desire may arise to do something with them. A
popular pursuit is the execution of a SQL statement on the
remote database. Luckily, the IAC server has a function to
fulfill this desire and that function is number 90.
Depending on the DBMS and its CLI, the 'SQL direct execute'
function may have different capabilities. In the case of DB2,
everything goes except a COMMIT or ROLLBACK, which require separate
functions in the IAC server.
The format of the SQL direct execute server function is as
follows:
&sv sqlstat = ~
[iacrequest %iac_handle% 90~
[quote %stmt_handle%[unquote %SQL%]] rstat]
Observe the manner in which the statement handle is
encoded with the statement. Server function 90 decodes this
statement and uses the handle for verification. Once verified,
the statement is assigned the handle and executed, and if the
variable sqlstat contains ERROR, the remainder of the variable
contains the error message.
The code for the iac_exec AML is illustrated below:
/*------------------
/* iac_exec .servce_var .handle_var %stmt%
/* Direct Execution of Statement on Server
/* svc and hndl are passed variable names for service
/* and handle
/* stmt is SQL statement (not a variable..)
/* (C) J. Groff/Tri-Met 8/95
/*-------------------
&args .isvc .ihndl .istmt
&sv .sqlstat = [iacrequest [value %.isvc%] 90~
[quote [value %.ihndl%][unquote %.istmt%]] rstat 1000]
&if [extract 1 [quote %.sqlstat%]] = ERROR &then
&do
&return &error Error %.sqlstat% executing %.istmt%
&end
&return
Obtaining Data From the Selected Set:
Upon successful completion of IAC function 90, we
have a selected set, and in distributed database parlance a
'Remote Unit Of Work'. A default cursor is issued by the CLI
for the selected set. This cursor is advanced one row by our
next IAC function; number 91:
&sv res = [iacrequest %iac_handle% 91 %stmt_handle% rstat]
This function returns either SUCCESS if data was
obtained, ERROR if an error occurred, or DONE when the
end of the selected set is reached. The AML to encapsulate
this function is described below:
/*------------------
/* iac_fetch .service_var .handle_var .result_var
/* fetch next row from result set
/* args passed are variable for server, statement handle
/* and variable to receive result.
/* return SUCCESS if good, DONE when end of set is reached, or ERROR
/* (C) J. Groff/Tri-Met 8/95
/*-------------------
&args .isvc .ihndl .ires
&sv %.ires% = [iacrequest [value %.isvc%] 91~
[quote [value %.ihndl%]] rstat]
&return
Once the data has been 'fetched', the columns within the
row can be extracted by our next function. Suprisingly, it's 92:
&sv res = ~
[iacrequest %iac_handle% 92 ~
[quote %stmt_handle%%column%] rstat]
Again, the statement handle is encoded with the column
desired, and the actual data is returned in the variable 'res'.
The AML module to make this less painful is illustrated thusly:
/*------------------
/* iac_getdata .service_var .handle_var %column% .result_var
/* get data for named column of current fetch
/* args passed are variable for server, statement handle
/* column number to get (based on order in SELECT stmt)
/* and variable to receive result.
/* (C) J. Groff/Tri-Met 8/95
/*-------------------
&args .isvc .ihndl .icol .ires
&sv %.ires% = [iacrequest [value %.isvc%] 92~
[quote [value %.ihndl%]%.icol%] rstat]
&if [quote [value %.ires%]] = ERROR &then
&return &error GetData Error Column %.icol%...
&return
Constructing a Real-Life Example:
With a great sigh of relief we have finally have the
functionality we need to connect to a remote database, acquire
a statement handle, execute a SQL statement, fetch a row, and
extract a column. At this point we can write a useful AML.
Since bus stops are a big part of life here at Tri-Met, there are
many legacy systems that access and maintain the tables on the
IBM ES/9000. Access to these tables from the GIS was a primary
motivation for this project.
The production component of the GIS supplies spatial data
to these tables. In the past, updating was done with a
problematic upload/download/logging process. These tables are
now accessed directly via the IAC server.
The structure of the first table is a 'Locations' table
containing the absolute position of each Stop in the system.
The 'Routes' table contains a list of the locations by route
and direction. The 'Locations' table is joined to the'Routes'
table in a many-to-one relation. The schema for these tables
follows:
Locations Table, Column Name, and Description
�����������������������������������������������������
LOC_ID The unique identification number of a
Location
LOCATION The street that a Location is actually "located" on
INTERSECTION The cross-street or landmark where a
Location is actually "located"
Spatial elements added by the GIS (RASDA):
X_COORD The X-coordinate of the location based
on the State Plane measurement in feet
Y_COORD The Y-coordinate of the location based
on the State Plane measurement in feet
LATITUDE Decimal latitude of the location
LONGITUDE Decimal longitude of the location
JURISDICTION The city where a location is
(or Null county if the location is not
within any city limits)
ZIP The zip code of the locatiion
Routes Table, Column Name, and Description
�����������������������������������������������������
RTE A code representing an individual bus route
DIR The direction of the Route "1" - Inbound,
"0" - Outbound
STOP A number that identifies the order the stops generally occur on the Route
LOC_ID The unique identification number of a
Location
Not surprisingly, the first program constructed was one
to display the bus stops in a particular route and direction.
The AML to draw the coordinates from the Route and Stop tables:
/*------------------
/* distributed location draw
/* gets x,y's from remote DB based on
/* route, direction, and effective date
/* (C) J. Groff/Tri-Met 8/95
/*-------------------
&args .rout .dir
/* make nice for drawing
markersymbol 10
units map
/*
/* define the remote DB names
/*
&sv .rds_stop_loc = db01.sad001_location
&sv .rds_stop_rte = db01.sad003_rtestop
/*
/* set effective date
/*
&sv .eff_date = 09/01/1995
/*
/* connect to IAC server
/*
&r iac_connect server .rsvc
/*
/* connect to DB, obtain handle
/*
&r iac_connect handle .rsvc .hndl
/*
/* set up select statement
/*
&sv resel = ~
select rte, dir, stop,~
rte_stop_beg_date, rte_stop_end_date,~
,a.loc_id, x_coord, y_coord, ~
from %.rds_stop_rte% a,~
%.rds_stop_loc% b~
where a.loc_id = b.loc_id and ~
[quote %.eff_date%] between ~
rte_stop_beg_date and rte_stop_end_date and~
rte = %.rout% and dir = %.dir%~
order by rte, dir, stop, rte_stop_beg_date
/*
/* do the exec
/*
&r iac_exec .rsvc .hndl [quote %resel%]
/*
/*---------main fetch/getdata loop
/* grab columns 7 and 8 from above select
/*
&r iac_fetch .rsvc .hndl .res_var
/* Fetch the data
&do &while %.res_var% = SUCCESS
&r iac_getdata .rsvc .hndl 7 .xcoor
&r iac_getdata .rsvc .hndl 8 .ycoor
marker %.xcoor% %.ycoor%
&r iac_fetch .rsvc .hndl .res_var
&end
/*
/* free up and leave
/*
&r iac_connect disconnect .rsvc .hndl
&return
Ancillary Functions:
Several other functions were added to the server to make
programming life more fulfilling. The number of columns in
the selected set may be returned by function 82 as illustrated
in the following AML:
/*------------------
/* iac_numresult .service_var .handle_var .result_var
/* return number of result columns from select
/* args passed are variable for server, statement handle
/* and variable to receive result.
/* (C) J. Groff/Tri-Met 8/95
/*-------------------
&args svc hndl res
&sv %res% = [iacrequest [value %svc%] 82~
[quote [value %hndl%]] rstat]
&if [quote [value %res%]] = ERROR &then
&return &error NumResult Error...
&return
If the need arises to name the default cursor, as in
the case of an 'UPDATE WHERE CURRENT', this can be
accomplished by function 92 as shown:
/*------------------
/* iac_declare .service_var .handle_var %cursor%
/* Declare cursor for current result set of .handle_var
/* args passed are variable for server, statement handle
/* name of cursor
/* (C) J. Groff/Tri-Met 8/95
/*-------------------
&args svc hndl cursor
&sv .sqlstat = [iacrequest [value %svc%] 92~
[quote [value %hndl%]%cursor%] rstat]
&if %.sqlstat% = ERROR &then
&return &error Could Not Declare Cursor %cursor%...
&return
To list the databases available to the logged in user ID,
function 81:
/*------------------
/* iac_dbinfo .service_var .handle_var .result_var
/* fetch next row from DBInfo buffer, these are
/* the databases available to current user ID
/* although access to only the one defined by login profile
/* is allowed, I guess this is like staring thru the bars...
/* args passed are variable for server, statement handle
/* and variable to receive result.
/* return END when end of set is reached or ERROR or data
/* (C) J. Groff/Tri-Met 8/95
/*-------------------
&args svc hndl res
&sv %res% = [iacrequest [value %svc%] 81~
[quote [value %hndl%]] rstat]
&if [quote [value %res%]] = ERROR &then
&return &error DBInfo Error...
&return
To obtain the column name, size, length, and type of a
specific column from the selected set. Call function 83 with
the desired column:
/*------------------
/* iac_colinfo .service_var .handle_var .column .result_var
/* return column info on selected columns
/* args passed are variable for server, statement handle
/* column requested and variable to receive result.
/* (C) J. Groff 8/95
/*-------------------
&args svc hndl col res
&sv %res% = [iacrequest [value %svc%] 83~
[quote [value %hndl%]%col%] rstat]
&if [quote [value %res%]] = ERROR &then
&return &error ColumnInfo Error...
&return
That about wraps up our server functions. After building
these modules, the RASDA system was re-tooled to use distributed
access and no local copies of tables need to exist.
FUTURE DIRECTIONS:
Apparently, ArcView V2.1 supports an 'RPC Client'
function call that could be utilized to access this server from
that application. In addition to that, there has been a lot of
ballyhou over you-know-whose ODBC standard. The former would
allow desktop GIS greater flexibility while the latter would
require enhancing the server to deliver increased support for
the standard, which really has nothing to do with GIS, but
addresses the larger issue of agency connectivity. In that
same vein, there are many Software Development Kits (SDK's)
that support RPC development under various OS's and platforms.
These tools could be utilized to produce platform-independent
client modules that could be linked with other applications.
Another avenue of research is connecting to these servers from
WAN's/Internet. As an example, the regional MPO has a need to
access up-to-the-minute Route and Stop information and this server
could provide that kind of access.
APPENDIX A. IAC Server Pseudocode:
/*********************************************************
**
** IAC Database Server - Inter-Application Communication
** Server
** Access is through RPC -> Call Level Interface -> DDCS ->
** Database
** Direct execution of an SQL statement.
** Getdata is used to retrieve information from the
** result set.
**
** Access is thru connect using a profile name. A uuid
** is issued for each 'connection'. This corresponds to
** separate statement handles.
**
** Because of the way the CLI works, only 1 connection
** handle and environ
** handle can be in use by an app. This translates to 1
** user ID / database connection per server, so when
** connection via profile,the /usr/ddbprofile
** entry for this server is checked for the profile. File
** has structure:
** profile:server_rpc_hex,database,user,passwd.
** At startup the profile for this server is loaded from
** file. Up to 100 connections (max stmt handles) can be made
** to this server
** (C) J. Groff/Tri-Met 8/95
**********************************************************/
/* FUNCTIONS PSEUDOCODED FOR BREVITY */
/* Standard 'C' library header includes */
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "sys/types.h"
#include "unistd.h"
#include "time.h"
/* include for Call Level Interface of your favorite DBMS */
#include "sqlcli1.h"
/* include the header that defines the RPC registry for this
module */
#include "prognum.h"
/* Maximum statement length - a condition of AML */
#define MAX_STMT_LEN 1024
/* here is where to define prototypes and structures */
#define MAXHANDLE 100
/* this is max num of stmnt handles per app */
/************************************************************
** clockout
** convert time struc to formatted out string MM/DD/YY
** DAY HH:MM:SS
*************************************************************/
char* clockout(stim* timstr)
{
convert_the_current_time
return_pointer_to_string
}
/***********************************************************
** verify_Slot
** find the slot in uuid and verify it, return slot number
** or -1 the uuid contains a slot number that corresponds
** to a SQL statement handle, the uuid in the slot and the
** uuid specified in inlin must match
***********************************************************/
int verify_Slot(char* inlin)
{
get_uuid_from_inlin
verify_uuid_in_range
find_slot_of_uuid
verify_uuid_against_uuid_in_slot
return_slot_or_error
}
/**********************************************************
** initialize
** - Find available server slot
** if not connected...
** - allocate environment handle
** - allocate connection handle
** - connect to server
**********************************************************/
int initialize(int* Sslot)
{
if_not_connected
open_/usr/ddbprofile_read_only
search_file_for_profile_name
compare_PROGNUMs
if_not_found
error_exit
else
set_up_remote_userid_password_profile_name
endif
log_time
look_for_server_slot_for_connection_handle_and_
statement_handle
if not_found
error_NO_SERVER_SLOTS_exit
(this means more than 100 statement
handles allocated...)
setup_server_slot
print_connect_message:
print ** LOGIN ** Server PID slot X
print Attempted connect for PROGNUM @ Current_Time
Allocate_environment_handle_if_not_already
Allocate_connection_handle_if_not_already
Issue_SQL_Connect
if error
error_message_then_exit
Allocate_statement_handle
if error
freak_out_and_exit
Log_time
print_connect_message:
print PROGNUM connected @ Current_time
return(SQL_SUCCESS);
}/* end initialize */
/**********************************************************
** terminate
** - 'disconnect'
** we don't actually disconnect from the DB, just free the
** statement handle.
**********************************************************/
void terminate(int* Sslot)
{
Verify_Slot
Free_statement_handle
Free_slot
return
}/* end terminate */
/**********************************************************
** terminate_all
** - 'disconnect'
** we don't actually disconnect from the DB, just free all
** the statement handles. This occurs after COMMIT
** or ROLLBACK...
** when they all go bye-bye invalid
***********************************************************/
void terminate_all(void)
{
For_all_server_slots
if connected
Free_statement_handle
End_for
return
}/* end terminate_all */
/**********************************************************
** disconnect
** - 'disconnect'
** hard disconnect from the DB, not just free the
** statement handle.
**********************************************************/
void disconnect(int* Sslot)
{
Verify_slot
Disconnect_from_database
Free_connection_handle
Free_environment_handle
Call_terminate_all();
Log_disconnect
print PROGNUM disconnected @ Current_time
return
}/* end disconnect */
/*-----------------------------------------------------
* Purpose:
* This file contains the stubs for server functions.
* These functions are invoked from aiserver_svc_proc.c.
* These functions simply transfer
* control to a user written function, where they are
* defined, and return
* a "Procedure not defined" string otherwise.
*-----------------------------------------------------
* Arguments:
* {{instr,outstr}}
* {instr Input === (char *) Input string argument,
* if any}
* {outstr Output === (char *) Results of the
* operation}
*-----------------------------------------------------
* Ravi Narasimhan [4/27/94] Original coding.
*-----------------------------------------------------
*/
void Function1 (instr,outstr)
char *instr, *outstr;
{
/* Exponent (instr,outstr);*/
}
void Function2 (instr,outstr)
char *instr, *outstr;
{
/* LogE (instr,outstr);*/
}
void Function3 (instr,outstr)
char *instr, *outstr;
{
/* Log10 (instr,outstr);*/
}
..... Function4 - Function79 As Above .....
/*----------------------------------------
;
; Start of CLI functions interspersed 80-99
; 80 - Connect: pass profile name,Reads profile file,connect
; to db,user,pass
; 81 - Datasource: semi-private since db connections is based
; 1/profile
; 82 - Number Results: Columns involved in select
; 83 - Column Description: various facts about specific column
; 85 - Declare Cursor: to passed uuid/stmnt handle
; 90 - Exec Direct:
; 91 - Fetch Next Record: returns SUCCESS, ERROR, DONE
; 92 - Retrieve data from passed column: returns data or ERROR
; 93 - Retrieve data, strip single quote
; 97 - Hard Disconnect
; 98 - Rollback, terminate connect
; 99 - Commit, terminate connect
;-----------------------------------------*/
void Function80 (instr,outstr)
char *instr, *outstr;
{
Call_initialize(slot);
If Cant_initialize_or_profile_name_not_found
Call_terminate(slot);
return_ERROR_in_outstr
Endif
Set_slot_connected
return_SUCCESS_in_outstr
}
/*-------------------------------------
** 81 - list available servers
**-------------------------------------*/
void Function81 (instr,outstr)
char *instr, *outstr;
{
For_each_available_data_source
build_data_base_and_description_string
if_not_end
return_data_base_and_description_
string_in_outstr
End_for
return_END
}
/*-------------------------------------
** 82 - return number result columns from select
**-------------------------------------*/
void Function82 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
convert_number_result_columns_to_ascii
return_number_in_outstr
}
/*-------------------------------------
** 83 - return column metrics
**-------------------------------------*/
void Function83 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
Call_CLI_routine_to_get_Column_name_size_length_type
switch_on_column_type
{
On_case_of_switch,_translate_to_column_description
}
return_name_size_length_type_description_in_outstr
}
void Function84 (instr,outstr)
char *instr, *outstr;
{
strcpy (outstr,"Procedure 84 not implemented");
}
/*----------------------------------------
** 85 - Declare cursor
** pass uuid in instr, this will select stmnt
** handle to assign named cursor
**----------------------------------------*/
void Function85 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
Call_CLI_to_set_cursor_name_to_passed_name
return_SUCCESS_in_outstr
}
void Function86 (instr,outstr)
char *instr, *outstr;
{
strcpy (outstr,"Procedure 86 not implemented");
}
void Function87 (instr,outstr)
char *instr, *outstr;
{
strcpy (outstr,"Procedure 87 not implemented");
}
void Function88 (instr,outstr)
char *instr, *outstr;
{
strcpy (outstr,"Procedure 88 not implemented");
}
void Function89 (instr,outstr)
char *instr, *outstr;
{
strcpy (outstr,"Procedure 89 not implemented");
}
/*--------------------------------
** 90 - Exec SQL stmt
** returns SUCCESS or ERROR
**-------------------------------*/
void Function90 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
Call_CLI_Execute_Direct_SQL_statement
if_error
return_ERROR_in_outstr
else
return_SUCCESS_in_outstr
}
/*---------------------
** 91 - Fetch next record in selected set
** return SUCCESS, ERROR, or DONE
**---------------------*/
void Function91 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
Call_CLI_to_fetch_next_record
if_error
if_No_Data_Found
return DONE_in_outstr
else
return_ERROR_in_outstr
else
return_SUCCESS_in_outstr
}
/*---------------------
** 92 - GetData - pass column to fetch in instr
** return the value in outstr
**---------------------*/
void Function92 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
Call_CLI_to_get_data_from_column_encoded_in_instr
if_error
return_ERROR_in_outstr
else
return_data_from_passed_column_in_outstr
}
/*-----------------------------
** 93 - GetData sans single quote
**-----------------------------*/
void Function93 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
Call_CLI_to_get_data_from_column_encoded_in_instr
if_error
return_ERROR_in_outstr
else
strip_single_quotes
return_data_in_outstr
endif
}
void Function94 (instr,outstr)
char *instr, *outstr;
{
strcpy (outstr,"Procedure 94 not implemented");
}
void Function95 (instr,outstr)
char *instr, *outstr;
{
strcpy (outstr,"Procedure 95 not implemented");
}
void Function96 (instr,outstr)
char *instr, *outstr;
{
strcpy (outstr,"Procedure 96 not implemented");
}
/*----------------------------
** 97 - Hard Disconnect
**----------------------------*/
void Function97 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
Call_CLI_to_do_COMMIT
if error
Call_CLI_to_do_ROLLBACK
Call_disconnect(slot);
return_SUCCESS_in_outstr
}
/*----------------------------
** 98 - close up with rollback
**---------------------------*/
void Function98 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
Call_CLI_to_do_ROLLBACK
if error
print_error
Call_terminate_all();
return_SUCCESS_in_outstr
}
/*----------------------------
** 99 - close it up
**----------------------------*/
void Function99 (instr,outstr)
char *instr, *outstr;
{
verify_Slot(instr);
Call_CLI_to_do_COMMIT
if error
Call_CLI_to_do_ROLLBACK
return_ERROR_in_outstr
Call_terminate_all();
return_SUCCESS_in_outstr
}
REFERENCES:
IBM, 1993. "Call Level Interface Guide and Reference."
Esri, 1995. ArcInfo 7.0 Distribution; "$ARCHOME/aiserver/README"
Esri, 1995. ArcInfo 7.0 Distribution; Online Documentation
AML->FUNCTIONS->INTER-APPLICATION COMMUNICATION.
Bloomer, John, 1992. "Power Programming with RPC." O'Reilly &
Associates: 0-937175-77-3.