//This console mode app shows how to create a few tables of different types.
//How to create an Advantage Data Dictionary and add the newly created
//tables the data dictionary file.
//I'm using various table types using ADS RDDs for DBF/NTX, DBF/VFP, ADT/ADI
//
//After that it atttempts to connect to the dd, create a new table and then
//open the new table as a free table.
//In short- it creates tables and dd, opens dd bound tables and free tables
//at the same time.
//
//The sample code uses pure rdds + some ACE calls.
//No GUI dependency as only a console mode app is being built, which should
//build just the same under Windows as well as under Linux.
//
#include "ads.ch"
#xcommand DEFAULT <uVar1> := <uVal1> ;
[, <uVarN> := <uValN> ] => ;
<uVar1> := iif( <uVar1> == nil, <uVal1>, <uVar1> ) ;;
[ <uVarN> := iif( <uVarN> == nil, <uValN>, <uVarN> ); ]
REQUEST DBFCDX
REQUEST ADS //Make sure rddads gets linked to our app.
REQUEST hb_gt_win //needed for console mode app.
//Static variable cPath will contain the path where tables and DD will be created.
//After connecting to an existing DD, there is no need to use the file path as the
//DD saves the relative path of each table as part of its metadata.
STATIC cPath
*-----------------------------------------------------------------------------------------------------
FUNCTION Main()
LOCAL afiles
SetMode(25,80) //25 lines by 80 columns console
rddRegister( "ADS", 1 )
rddSetDefault( "ADS" )
adsSetServerType( ADS_LOCAL_SERVER )
//NON-compatible locking mode. If other NON-ADS clients are going to
//to work on the same tables, then make sure to use compatible locking mode.
AdsLocking( .t. )
AdsRightsCheck( 0 )
//Path for existing .dbf tables maybe sent as a parameter
//on the command line. This is just one idea. It could be saved
//on a .ini file or hardcoded.
cPath := hb_ArgV( 1 )
//in case no path was sent on command line, assume current path.
DEFAULT cpath := ""
//sanitize cPath contents.
//IF !EMPTY( cPath ) .and. RIGHT( cPath, 1 ) != '' ;cPath += '' ;ENDIF
//if you need to create some new tables other than existing tables look
//at function CreateTables(). CreateTables shows how to create
//adt tables, vfp tables, and dbf-ntx tables.
//
//CreateTables() creates an array with table names and structure for each.
//It then creates the tables. After being created they will be added to a newly
//created data dictionary using function CreateDictionary().
//If your .dbf tables are alreay present, then you won't need to execute
//CreateTables, instead you could simply execute a Directory() harbour
//function to retreive existing table names. By storing the names of these tables
//into afiles variable, you can then execute CreateDictionary() in exactly the same
//manner.
afiles := CreateTables()
IF !EMPTY( afiles ) ;CreateDictionary( afiles ) ;ENDIF
//now that we have a dd created, we will attempt to connect to it and
//use the tables as dd bound tables.
IF !AdsConnect60( cPath + "test_dd.add", ADS_LOCAL_SERVER, "user1", "password1" )
Alert( "Cannot connect to test_dd" + Str( AdsGetLastError() ) )
QUIT
ENDIF
//Now we create a dbfcdx table to be used as a free table.
CreateAndUseFreeTable()
RETURN NIL
//--------------------------------------------------------------------------------------
//function CreateTables() will create three tables using stnd harbour func dbCreate
//Each table structure is saved on an array following the std returned by dbStruct()
//Each element on the array is an array describing the field. Thus, the array
//for a table with 3 fields would look like this:
//aStructure := { { cFieldName1, cFieldType1, nFldWidth, nDecimals },;
// { cFieldName2, cFieldType2, nFldWidth, nDecimals },;
// { cFieldName3, cFieldType3, nFldWidth, nDecimals } }
//
STATIC FUNCTION CreateTables()
LOCAL cAlias := "MyAlias"
LOCAL afiles := {}
LOCAL cFileName, e
//---------------------- customers.adt --------------------------------------------
LOCAL aStruc := { { "cust_id" , "C", 10, 0 },;
{ "Sequence" , "AutoInc", 07, 0 },; //ADT extended field type autoincrement
{ "customer_name" , "C", 25, 0 },;
{ "Start_date" , "TimeStamp", 01, 0 },; //ADT extended field type
{ "Notes" , "M", 10, 0 } }
AdsSetFileType( ADS_ADT )
TRY
dbCreate( ( cFileName := cPath + "customers.adt" ), aStruc,, .t., cAlias )
( cAlias )->( OrdCreate( cPath + "customers", "cust_id", "cust_id" ) )
( cAlias )->( OrdCreate( cPath + "customers", "Start_date", "Start_date" ) )
( cAlias )->( dbclosearea() )
AADD( afiles, { cFileName, "customers.adi" } )
//------------------------ sales.dbf -------------------------------------------------
aStruc := { { "cust_id" , "C", 10, 0 },;
{ "invoice" , "C", 15, 0 },;
{ "s_date" , "TimeStamp", 01, 0 },; //VFP extended field type
{ "item_id" , "C", 15, 0 },;
{ "Units" , "N", 03, 0 },;
{ "Price" , "Money", 09, 2 },; //VFP extended field type
{ "Notes" , "M", 10, 0 } }
AdsSetFileType( ADS_VFP )
dbCreate( ( cFileName := cPath + "sales.dbf" ), aStruc,, .t., cAlias )
( cAlias )->( OrdCreate( cPath + "sales", "invoice", "invoice" ) )
( cAlias )->( OrdCreate( cPath + "sales", "cust_id", "cust_id" ) )
( cAlias )->( dbclosearea() )
aadd( afiles, { cFileName, "sales.cdx" } )
//------------------------ items.dbf -------------------------------------------------
aStruc := { { "item_id" , "C", 15, 0 },;
{ "Desc" , "C", 25, 0 },;
{ "Price" , "N", 07, 2 } }
AdsSetFileType( ADS_NTX )
dbCreate( ( cFileName := cPath + "items.dbf" ), aStruc,, .t., cAlias )
( cAlias )->( OrdCreate( cPath + "itm_id",, "item_id" ) )
( cAlias )->( OrdCreate( cPath + "itm_desc",, "Desc" ) )
( cAlias )->( dbclosearea() )
aadd( afiles, { cFileName, "itm_id.ntx;itm_desc.ntx" } )
CATCH e
ShowError( cFileName, e )
afiles := {}
END
RETURN afiles
/*--------------------------------------------------------------------------------------------------------
CreateDictionary creates an Advantage Data Dictionary based on already existing tables
--------------------------------------------------------------------------------------------------------*/
STATIC FUNCTION CreateDictionary( afiles )
LOCAL cExt AS CHARACTER := ""
LOCAL cDD := cPath + "test_dd.add"
IF !ADSDDCREATE( cDD,, "Sample data dictinoary" )
ALERT( "AdsCreate() of " + cDD + " failed. Error:" + Str( AdsGetLastError() ) )
RETURN NIL
ENDIF
//Calling ACE function AdsDDSetDatabaseProperty()
AdsDDSetDatabaseProperty( ADS_DD_ENABLE_INTERNET, .t. )
AdsDDSetDatabaseProperty( ADS_DD_INTERNET_SECURITY_LEVEL, ADS_DD_LEVEL_2 )
AdsDDSetDatabaseProperty( ADS_DD_DEFAULT_TABLE_PATH, cPath )
AdsDDSetDatabaseProperty( ADS_DD_LOG_IN_REQUIRED, .t. )
//ACE Function ADsDDCreateUser() creates a dd user.
//When a new dd is created user ADSSYS is auto-created as the dd administrator
//it is a good idea to create other users to limit access and permissions.
//
AdsDDCreateUser( , "user1", "password1", "User named userd1 with password password1" )
AdsDDCreateUser( , "user2", "password2", "Description of user2" )
AdsDDCreateUser( , "user3", "password3", "Optional description of user3" )
//array aFiles contains the names of the tables to be added to the newly created
//data dictionary.
//element 1 on the array is the table name,
//element 2 on the array is the index file name.
//Since .dbf tables could be vfp, cdx, or ntx type tables,
//we are using the extension on the index file name to "guess" the .dbf
//file type. If this is already known beforehand, then the code below
//could be much simpler. I'm using AEVAL only because I love it albeit it is
//more cryptic. The same can be done on a FOR EACH (FOR/REPEAT/WHILE) loop
AEVAL( aFiles, ;
{ |e| hb_FNameSplit( iif( len( e ) > 1, e[ 2 ], e[ 1 ] ), , , @cExt ), ;
AdsSetFiletype( iif( lower( cExt ) $ ".adt,.adi", ADS_ADT, ;
iif( lower( cExt ) == ".ntx", ADS_NTX, ADS_VFP ) ) ),;
addfiletoDD( e, cPath ), ;
SetTableProp( e ), ;
QOut( "Adding table to DD", ;
e[ 1 ], ;
AdsSetFileType(), ;
cExt, ;
ADSGetLastError() ) } )
//assign the password "password" to the ADSSYS user of the DD. Just as default.
AdsDDSetDatabaseProperty( ADS_DD_ADMIN_PASSWORD, "password" )
Wait
return Nil
//-------------------------------------------------------------------------------------------------------
//This is the code that adds the table to the DD.
STATIC FUNCTION AddFiletoDD( aFile, cPath )
LOCAL cIndexs := ""
IF LEN( afile ) > 1 .AND. LEFT( afile[ 2 ], 1 ) <> "."
AEVAL( afile, { |e| cIndexs += e +";" }, 2 )
cIndexs := LEFT( cIndexs, LEN( cIndexs ) -1 )
ENDIF
RETURN( AdsDDAddTable( left( afile[ 1 ], at(".", afile[1] )-1 ), ;
cPath + aFile[ 1 ], ;
cIndexs ) )
//--------------------------------------------------------------------------------------------------------
//You may use this function to assign certain properties to all tables.
//The only table property I'm assigning is the auto_create property.
//Many others could also be applied. Look at ads.h include file for
//more constants.
STATIC FUNCTION SetTableProp( aFile )
LOCAL cFileName
hb_FNameSplit( afile[ 1 ],, @cFileName )
AdsDDSetTableProperty( cFileName, ADS_DD_TABLE_AUTO_CREATE, .t. )
// AdsDDSetTableProperty( cFileName, ADS_DD_TABLE_ENCRYPTION, .t. )
RETURN NIL
*-------------------------------------------------------------------------------------------------------------------------------
STATIC FUNCTION ShowError( cMsg, oErr )
LOCAL cErr
cErr := STR( AdsGetLastError() )
ALERT( "Error : " + ALLTRIM( cErr ) + " " + ;
cMsg + " " + oErr:Subsystem + " " + ;
STR( oErr:subCode ) + " " + oErr:operation + " " + oErr:description )
dbCloseAll()
RETURN NIL
//-----------------------------------------------------------------------------------------------------
STATIC FUNCTION CreateAndUseFreeTable()
LOCAL aStruct := { ;
{ "CHARACTER", "C", 25, 0 }, ;
{ "NUMERIC" , "N", 8, 0 }, ;
{ "DOUBLE" , "N", 8, 2 }, ;
{ "DATE" , "D", 8, 0 }, ;
{ "LOGICAL" , "L", 1, 0 }, ;
{ "MEMO1" , "M", 10, 0 }, ;
{ "MEMO2" , "M", 10, 0 } ;
}
DbCreate( "testdbf", aStruct, "DBFCDX", .T., "MYALIAS" )
Browse()
RETURN NIL