/* FBDatabaseConnection.h created by geert on Thu 11-Dec-1997 */

#import <Foundation/Foundation.h>

@class FBMetaData;
@class FBErrorMetaData;
#import "FBBlobHandle.h"

//////////////////////////////////////////////////////////////////////
//                                                                  //
//  Introduction                                                    //
//                                                                  //
//////////////////////////////////////////////////////////////////////
//
//  To get started, please find below a very terse illustraive
//  introduction to this framework:
//
//  To get a connection to a database on a given host and execute
//  some SQL:
//
//  FBDatabaseConnection *fbdc;
//  
//  fbdc = [FBDatabaseConnection connectToDatabaseNamed:@"DB0"
//                                               onHost:@"hostname"];
//  if (fbdc == nil) {
//      NSLog(@"Unable to connect: %@", 
//          [FBDatabaseConnection errorMessage]);
//  } else {
//      FBMetaData *md;
//  
//      md = [fbdc createSession:@"MySession" sessionUser:@"_system"
//                      password:@"" systemUser:@"MyUsername"];
//      if ([md errorCount] > 0) {
//          NSLog(@"Unable to create session: %@", 
//              [[fbdc errorMetaData] allErrorMessages]);
//      } else {
//          md = [fbdc executeDirectSQL: @"select * from information_schema.tables"];
//      
//          if ([md errorCount] > 0) {
//              NSLog(@"Error executing SQL: %@",
//                  [[fbdc errorMetaData] allErrorMessages]);
//          } else {
//              NSLog(@"Row Count = %d, Column Count = %d",
//                  [md rowCount], [md columnCount]);
//              NSArray* row;
//              while ( row = md.g=fetchRow) {
//                  NSLog(@"Row = %@", row);
//               }
//          }
//      }
//      [fbdc close];
//  }
//  
@interface FBDatabaseConnection : NSObject
{
   void*       connection;
   FBMetaData* newestMetaData;
   int         hCount;
   void**      fetchMetaData;    // Is a FBMetaData** but the ARC complains
}

+ (void)setDocumentRoot:(NSString*)documentRoot;
+ (NSString*)documentRoot;

+ (void)setConnectTimeout:(double)timeout;
+ (double)connectTimeout;

//////////////////////////////////////////////////////////////////////
//                                                                  //
//  Agent Management                                                //
//                                                                  //
//////////////////////////////////////////////////////////////////////

// Creates an agent object which controls the access to the actual
// FrontBase server.  An agent can have many such connections or
// sessions, of which only one can be active at a given time. Agent
// objects are cached by FBDatabaseConnection by default, i.e. multiple
// calls to +connectToDatabaseNamed:onHost: will return the same agent
// object (no need to do retain/release).

// The following url syntax is accepted
//    <url>       :== file:<host-part>[<path-name>]
//                    file:<path-name>
//                    frontbase:<host-part>
//    <host-part> :== //[<user-part>][<host-name>][:<database-name>]/
//    <user-part> :== <user-name>[:[<database-password>:]<user-password>]@
//
// The only meaningful hostname in the file schema is localhost.


+ (FBMetaData *)connectToURL:(NSString*)url 
            databasePassword:(NSString*)databasePassword
                    userName:(NSString*)userName
                    password:(NSString*)password
                   sessionId:(NSString*)sessionId;

// Create a connection base on the URL. If the URL does not specify a databasePassword, userName, or password
// the corresponding arguments are used. You can specify nil in which case the user name is _system, the
// passwords are empty and the session id is the base name of the executable and the pid.

+ (FBMetaData *)connectToURL:(NSString*)url;
// Call with nil user name, passwords and sessionId.

+ (FBDatabaseConnection *)connectToDatabaseNamed:(NSString*)databaseName 
                                        pathName:(NSString*)pathName 
                                        password:(NSString*)password 
                                           error:(NSString**)msg;

+ (FBDatabaseConnection *)connectToDatabaseNamed:(NSString *)dbName
                                          onHost:(NSString *)host
                                        password:(NSString *)password
                                           error:(NSString**)msg;

// A shorthand for the above with @"" as password
+ (FBDatabaseConnection *)connectToDatabaseNamed:(NSString *)dbName
                                          onHost:(NSString *)host
                                           error:(NSString**)msg;

+ (FBDatabaseConnection *)connectToDatabaseUsingPort:(unsigned)portNumber
                                              onHost:(NSString *)host
                                            password:(NSString *)password
                                               error:(NSString**)msg;

+ (NSString *)errorMessage;

+ (FBDatabaseConnection *)connectToDatabaseNamed:(NSString *)dbName
                                          onHost:(NSString *)host
                                        password:(NSString *)password;

+ (FBDatabaseConnection *)connectToDatabaseNamed:(NSString *)dbName
                                          onHost:(NSString *)host;

+ (FBDatabaseConnection *)connectToDatabaseUsingPort:(unsigned)portNumber
                                              onHost:(NSString *)host
                                            password:(NSString *)password;


// Access to the database and host names
@property(readonly) NSString*        databaseName;
@property(readonly) NSString*        hostName;
@property(readonly) NSString*        pathName;
@property(readonly) FBMetaData*      newestMetaData;
@property(readonly) FBErrorMetaData* errorMetaData;
@property(retain)   NSString*        sessionID;
@property(readonly) NSString*        errorMessage;
@property(readonly) BOOL             isLocal;

// Returns YES if this agent is properly connected to a FrontBase server
@property(readonly) BOOL connected;

// An example of stopping the server:
//
// fbdc = [FBDatabaseConnection connectToDatabaseNamed:@"DB0"
//                                              onHost:@"hostname"];
// meta = [fbdc createSession:@"MySession" sessionUser:@"_system"
//          password:@"" systemUser:@"MyUsername"];
// if ([meta errorsFound] == NO) {
//     meta = [fbdc stopDatabase];
//     if ([meta errorsFound] == NO)
//         /* We are done */
- (FBMetaData*) stopDatabase;

// Sets the database password. This requires that a connection/session
// has been established with _SYSTEM as the session user.
- (FBMetaData *)setDatabasePassword:(NSString *)newPassword;


//////////////////////////////////////////////////////////////////////
//                                                                  //
//  Connection/session management                                   //
//                                                                  //
//////////////////////////////////////////////////////////////////////

// Creates a new session/connection (within this current agent).  This
// implements the CONNECT TO <str1> AS <str2> USER <str3> statement (part
// of SQL 92).
- (FBMetaData *)createSession:(NSString *)sessionId
                  sessionUser:(NSString *)sessionUser
                     password:(NSString *)password
                   systemUser:(NSString *)systemUser;


// Shorthand for -createSession:sessionUser:password:systemUser where
// the default session ID is used.
- (FBMetaData *)setSessionUser:(NSString *)sessionUser
                      password:(NSString *)password
                    systemUser:(NSString *)systemUser;


// Shorthand for -setSessionUser:password:systemUser where
// @"" is used for password.
- (FBMetaData *)setSessionUser:(NSString *)sessionUser
                    systemUser:(NSString *)systemUser;

// DISCONNECT methods
- (FBMetaData *)disconnect:(NSString *)sessionId;
- (FBMetaData *)disconnectAll;
- (FBMetaData *)disconnectDefault;
- (FBMetaData *)disconnectCurrent;


// SET CONNECTION methods
- (FBMetaData *)setConnection:(NSString *)sessionId;
- (FBMetaData *)setConnectionToDefault;

- (NSString *)agentID;

// Sets the password for the given user. If the session user is
// _SYSTEM, the old password is ignored.
- (FBMetaData *)setPasswordForUser:(NSString *)userName
                                to:(NSString *)newPassword
                               old:(NSString *)oldPassword;


//////////////////////////////////////////////////////////////////////
//                                                                  //
//  Executing SQL                                                   //
//                                                                  //
//////////////////////////////////////////////////////////////////////

// Sends the SQL command to the FrontBase server for execution.  An
// autoreleased FBMetaData object is always returned.  Via the FBMetaData
// object, information relating to the execution of the SQL statement
// (error count, row count, column count, etc.) can be obtained.
// Combine the options with || to fetch data immediately and commit when the
// fetch is done

typedef enum { FBFetch = 1, FBCommit = 2 } FBCExecuteFlags;

- (FBMetaData *)executeDirectSQL:(NSString *)sql flags:(unsigned int)flags;
- (FBMetaData *)executeDirectSQL:(NSString *)sql;

// To execute sql and fetch
//
//   FBMetaData* md = [connection executeDirectSQL:@"select ..."];
//   FBArray*    row;
//   while ( row = [md fetchRow])
//    {
//        ...
//    }


//////////////////////////////////////////////////////////////////////
//                                                                  //
// Fetching                                                         //
//                                                                  //
//////////////////////////////////////////////////////////////////////

// To save one round trip to the server, instead of:
//     meta = [fbdc executeDirectSQL:@"SELECT ..."];
//     data = [fbdc fetch:rowCount handle:[meta fetchHandle]];
// Do:
//     meta = [fbdc executeDirectSQL:@"SELECT ..." preFetch:rowCount
//                into:&data];
//
// Please note that the NSData object returned is autoreleased,
// ie., you have to consider retaining it.
//
// In both cases, data will be nil if errors were detected during
// execution of the SQL or if no rows can be fetched.

- (FBMetaData *)executeDirectSQL:(NSString *)sql preFetch:(unsigned)rowCount into:(NSData **)rawData;
- (FBMetaData *)executeDirectSQL:(NSString *)sql preFetch:(unsigned)rowCount into:(NSData **)rawData autoCommit:(BOOL)flag;

- (NSData*)fetch:(unsigned)rowCount handle:(NSString *)handleID;
- (FBMetaData *)fetch:(unsigned)rowCount handle:(NSString *)handleID data:(NSData **)fetchedData;
- (FBMetaData *)cancelFetch:(NSString *)handleID;
- (NSArray *)buildRowsFrom:(NSData *)rawData;
- (NSArray *)buildRowsFrom:(NSData *)rawData with:(FBMetaData *)metaData;

//////////////////////////////////////////////////////////////////////
//                                                                  //
//  BLOBS and CLOBS                                                 //
//                                                                  //
//////////////////////////////////////////////////////////////////////
// API for the socalled one-shot BLOBs, i.e. BLOBs (or CLOBs) that will
// be either stored or retrieved in their entirety. The returned handle
// is to be used in the SQL in place of the actual BLOB value. Example:
//
// bhandle = [fbdc writeBLOB:someData];
// ...
// sql = [NSString stringWithFormat:@"INSERT INTO T0 VALUES(%@,...)",
//           [bhandle handle], ...];
- (FBBlobHandle*)writeBLOB:(NSData *)blobData;
- (NSData *)readBLOB:(FBBlobHandle *)aHandle;


// Fetches the complete BLOB

- (NSData *)readBLOB:( FBBlobHandle *)aHandle offset:(unsigned)offset
    chunkSize:(unsigned *)chunkSize;


// Fetches a chunk of the BLOB starting from offset, chunkSize is updated
// to indicate actual size read

- (FBBlobHandle *)writeCLOB:(NSString *)aString;
- (NSString *)readCLOB:( FBBlobHandle *)aHandle;


// Very large CHARACTERs can optionally be transferred to the server
// in a way similar to CLOBs (saving a copy into the SQL string).
- ( FBBlobHandle *)writeCHARACTER:(NSString *)aString;


// BLOBs and files.
// Create a blob from the content of the named file.
// Get the blob handle with the FBMetaData method - blobHandle;
- (FBMetaData*)writeBLOBStreaming:(NSString*)path;

// Copy the content of the blob to the file.
- (FBMetaData*)readBLOB:(FBBlobHandle*)handle into:(NSString*)path;

- (void)close;

//////////////////////////////////////////////////////////////////////
//                                                                  //
//  Miscellaneous                                                   //
//                                                                  //
//////////////////////////////////////////////////////////////////////

// Shorthand for [fbdc executeDirectSQL:@"COMMIT;]";
- (FBMetaData *)commit;

// Shorthand for [fbdc executeDirectSQL:@"ROLLBACK;"];
- (FBMetaData *)rollback;

// Stopping some agent in the server (terminating its connection)
- (FBMetaData *)stopAgent:(NSString *)agentID;

// Asking the agent to quit what its doing (without terminating)
- (FBMetaData *)stopAgentsBusyCondition:(NSString *)agentID;

- (void)interruptAgent;

@end

// The extensions are included here to ensure that they are included in a library
// version

@interface NSCharacterSet (FBExtensions)

+ (NSCharacterSet *)whitespaceAndNewlineAndCarriageReturnCharacterSet;
+ (NSCharacterSet *)nonWhitespaceCharacterSet;

@end

@interface NSString (FBExtensions)

+ (NSString *)stringWithUTF8:(const char*)utf8;

- (NSString *)stringByRemovingLeadingWhitespace;
- (NSString *)stringByRemovingTrailingWhitespace;
- (NSString *)stringByRemovingSurroundingWhitespace;
- (NSString *)stringByCompressingWhitespace;

- (unsigned long long)versionValue;
- (unsigned long long)rowIndexValue;
- (unsigned long long)rowCountValue;
- (unsigned int)sizeValue;

@end

@interface NSTimeZone (FBExtensions)

+ (NSTimeZone *)timeZoneWithString:(NSString *)aString;
+ (NSTimeZone *)timeZoneWithDisplacementString:(NSString *)displacementString;

@end
