/** * sql_server.h, ABr * * SQL Server interface classes. All low-level access to SQL server * is hidden in the sql_server.cpp implementation file. This policy * makes it easier to segregate the library from other third-party * tools. Keep in mind that vendor-specific database header files * are notorious for causing strange compile and link errors due to * namespace collisions and/or macro redefinitions. * * Accessing SQL Server directly using the extended stored procedure * support functions is time-consuming, error-prone, and extremely * messy. This implementation file exists to wrap access to the * low-level functions in such a way that the application developer * can concentrate on business-level logic rather than on the gritty * underlying functions. * * All the major functions are supported through this interface. It's * easy to define dynamic result sets, support input and output * variables, and send logging messages back to SQL Server using these * classes. * * Copyright (c) 2003-05 softwareAB Co. * All rights reserved * * SOFTWAREAB MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SOFTWAREAB * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. * * Developed by softwareAB, Inc. * 6806 Silver Ann Drive * Lorton, VA 22079 */ #ifndef _ABR_SQL_SERVER_H #define _ABR_SQL_SERVER_H // softwareAB #include #ifdef __cplusplus extern "C" { #endif /// wrappers for SQL Server (not in namespace so C functions can /// see them) /// opaque type so that callers do not need to #include /// in order to access a SRV_PROC object. typedef void abrSRV_PROC ; /// custom type so that callers do not need to #include /// in order to access return code types. typedef int abrSRVRETCODE ; /// macro to define a logical error (avoids need for srv.h) #define abrSRV_ERROR 0 /// macro to define statement conclusion (avoids need for srv.h) #define abrSRV_DONE 1 /// macro to define a SQL Server datatype (avoids need for srv.h) #define abrSRV_DATATYPE 2 /// macro to define a SQL Server event (avoids need for srv.h) #define abrSRV_EVENT 4 #ifdef __cplusplus } #endif namespace ABR { /** * SQL Server Utility functions. */ class SqlServerUtils { public: /** * Is the listed type an INT class? * @param nDataType SQL Server data type. */ static bool is_int( int ) ; /** * Is the listed type a FLOAT class? * @param nDataType SQL Server data type. */ static bool is_float( int ) ; /** * Is the listed type a CHAR class? * @param nDataType SQL Server data type. */ static bool is_char( int ) ; /** * Is the listed type a STRING class? * @param nDataType SQL Server data type. */ static bool is_string( int ) ; /** * Is the listed type a VARCHAR string? * @param nDataType SQL Server data type. */ static bool is_varchar( int ) ; /** * Is the listed type an INT4? * @param nDataType SQL Server data type. */ static bool is_int4( int ) ; /** * Is the listed type an INTN? * @param nDataType SQL Server data type. */ static bool is_intn( int ) ; /// Is the listed type a binary? (either BINARY or VARBINARY) static bool is_binary( int ) ; /// Is the listed type a date? static bool is_datetime( int ) ; } //SqlServerUtils ; /** * Wrapper object for opaque database datetime; this * object is returned by SqlServerDatetime below and is the * object that should be passed to rs_col_setdata. This is * because one *must* pass persistent data to rs_col_setdata, * since raw pointers are stored. */ class SqlServerDatetime_raw { public: SqlServerDatetime_raw() ; virtual ~SqlServerDatetime_raw() ; void *data() { return m_data ; } const void *data() const { return m_data ; } protected: void *m_data ; private: /// disallowed SqlServerDatetime_raw( const SqlServerDatetime_raw & ) ; SqlServerDatetime_raw &operator=( const SqlServerDatetime_raw & ) ; } //SqlServerDatetime_raw ; /// useful cast so that allocated raw pointers are properly released typedef std::auto_ptr SqlServerDatetime_rawPtr ; /** * Wrapper object for SQL Server datetime */ class SqlServerDatetime { public: SqlServerDatetime( int _year = 1900 , int _month = 1 , int _day = 1 , int _hour = 0 , int _second = 0 , int _minute = 0 , int _millis = 0 ) : m_year( _year ) , m_month( _month ) , m_day( _day ) , m_hour( _hour ) , m_minute( _minute ) , m_second( _second ) , m_millis( _millis ) { } //ctor SqlServerDatetime( const time_t & ) ; SqlServerDatetime( const SqlServerDatetime &_o ) : m_year( _o.year() ) , m_month( _o.month() ) , m_day( _o.day() ) , m_hour( _o.hour() ) , m_minute( _o.minute() ) , m_second( _o.second() ) , m_millis( _o.millis() ) { } //ctor SqlServerDatetime &operator=( const SqlServerDatetime &_o ) { if( this != &_o ) { year( _o.year() ) ; month( _o.month() ) ; day( _o.day() ) ; hour( _o.hour() ) ; minute( _o.minute() ) ; second( _o.second() ) ; millis( _o.millis() ) ; } //if return *this ; } //ctor virtual ~SqlServerDatetime() {} /// accessors int year() const { return m_year ; } void year( int _x ) { m_year = _x ; } int month() const { return m_month ; } void month( int _x ) { m_month = _x ; } int day() const { return m_day ; } void day( int _x ) { m_day = _x ; } int hour() const { return m_hour ; } void hour( int _x ) { m_hour = _x ; } int minute() const { return m_minute ; } void minute( int _x ) { m_minute = _x ; } int second() const { return m_second ; } void second( int _x ) { m_second = _x ; } int millis() const { return m_millis ; } void millis( int _x ) { m_millis = _x ; } /// printable version std::string to_string() const { return ABR::debug_printf( "%04d-%02d-%02d %02d:%02d:%02d:%03d%" , year(), month(), day(), hour(), minute(), second(), millis() ) ; } //to_string /// Pass this to rs_col_setdata to update ResultSet data. /// The rs_col_setdata function requires a raw pointer when binding /// data back to SQL Server. However, for DATETIME fields you will /// be using this SqlServerDatetime structure. This function supports /// the "glue" required between these two seemingly incompatible /// objectives. ///

/// When binding a SQL Server DATETIME object, use the following example /// as a reference: ///

	///   // a callback function in our DLL (note the abrSRV_PROC)
	///   extern "C"
	///   abrSRVRETCODE xp_my_function( abrSRV_PROC *pSrv ) {
	///     // construct a SQL Server object
	///     ABR::SqlServer srv( pSrv ) ;
	///     
	///     // get file information about "C:\\FOO.TXT"
  ///     struct _stat fileinfo ;
  ///     _stat( "C:\\FOO.TXT", &fileinfo ) ;
  ///     struct _finddata_t fd ;
  ///     memset( &fd, 0, sizeof( fd ) ) ;
  ///     long lHandle = _findfirst( sPath.c_str(), &fd ) ;
  ///     if( 1 != lHandle ) {
  ///       _findclose( lHandle ) ;
  ///     } //if
	///
	///     // xlat to SQL Server DATETIME
	///     ABR::SqlServerDatetime dtFileDate( fileinfo.st_ctime ) ;
	///
	///     // get a self-managing raw pointer to the DATETIME
	///     ABR::SqlServerDatetime_rawPtr dtFileDate_ptr = dtFileDate.raw_ptr() ;
	///
	///     // define the result set for the caller
  ///     srv.rs_col_describe(
  ///       "dFileDate"
  ///       , ABR::SqlServerCol::type_datetime
  ///       , ABR::SqlServerCol::data_length( ABR::SqlServerCol::type_datetime )
	///     ) ;
	///
	///     // bind the file date back to the column. note that the column
	///     // number is one-based (not zero-based). also notice the pointer
	///     // dereference.
	///     srv.rs_col_setdata( 1, *dtFileDate_ptr ) ;
	///
	///     // send the row back to SQL Server
	///     srv.rs_send() ;
	///
	///     // tell SQL Server we are done
	///     srv.senddone( abrSRV_DONE ) ;
	///   } //xp_my_function
	///   
	/// 
SqlServerDatetime_rawPtr raw_ptr() const ; protected: int m_year, m_month, m_day, m_hour, m_minute, m_second, m_millis ; } //SqlServerDatetime ; /// stl support inline bool operator<( const SqlServerDatetime &lhs, const SqlServerDatetime &rhs ) { if( lhs.year() < rhs.year() ) return true ; else if( lhs.year() > rhs.year() ) return false ; if( lhs.month() < rhs.month() ) return true ; else if( lhs.month() > rhs.month() ) return false ; if( lhs.day() < rhs.day() ) return true ; else if( lhs.day() > rhs.day() ) return false ; if( lhs.hour() < rhs.hour() ) return true ; else if( lhs.hour() > rhs.hour() ) return false ; if( lhs.minute() < rhs.minute() ) return true ; else if( lhs.minute() > rhs.minute() ) return false ; if( lhs.second() < rhs.second() ) return true ; else if( lhs.second() > rhs.second() ) return false ; if( lhs.millis() < rhs.millis() ) return true ; else if( lhs.millis() > rhs.millis() ) return false ; return false ; } //< inline bool operator==( const SqlServerDatetime &lhs, const SqlServerDatetime &rhs ) { if( lhs.year() != rhs.year() ) return false ; if( lhs.month() != rhs.month() ) return false ; if( lhs.day() != rhs.day() ) return false ; if( lhs.hour() != rhs.hour() ) return false ; if( lhs.minute() != rhs.minute() ) return false ; if( lhs.second() != rhs.second() ) return false ; if( lhs.millis() != rhs.millis() ) return false ; return true ; } //== /** * SQL Server Column */ class SqlServerCol { public: /// supported column types enum Types { type_none = 0 , type_int , type_float , type_string , type_char , type_binary , type_datetime , type_bit } ; /// return the internal length that SQL Server uses /// for a datatype; useful when passed to rs_col_describe. /// @param nDataType SQL Server data type static int data_length( Types ) ; /// empty SqlServerCol() ; /// assigned /// @param col_number One-based column number /// @param col_type Data type /// @param col_data Data to store /// @param col_size Data size SqlServerCol( int, Types, const void *, int ) ; /// copy SqlServerCol( const SqlServerCol & ) ; /// assignment SqlServerCol &operator=( const SqlServerCol & ) ; /// cleanup ~SqlServerCol() ; /// set data /// @param col_data Data to store /// @param col_size Data size void set_data( const void *, int ) ; /// special version for dates only void set_data( const SqlServerDatetime & ) ; /// column number (one-based) int col_number() const { return m_col_number ; } /// column type Types col_type() const { return m_col_type ; } /// raw data pointer const void *col_data() const { return m_col_data ; } /// size of the data int col_size() const { return m_col_size ; } /// coerce to string const char *as_string() const { return reinterpret_cast(col_data()) ; } /// coerce to int //lint -e613 int as_int() const { return col_data()? *(reinterpret_cast(col_data())): 0 ; } //as_int /// coerce to double //lint -e613 double as_double() const { return col_data()? *(reinterpret_cast(col_data())): 0.0 ; } //as_double /// coerce to binary (same as raw pointer) const void *as_binary() const { return col_data() ; } /// coerce to datetime SqlServerDatetime as_datetime() const ; /// is this an int bool is_int() const { return type_int == m_col_type ; } /// is this a float bool is_double() const { return type_float == m_col_type ; } /// is this a string bool is_string() const { return type_string == m_col_type ; } /// is this a char bool is_char() const { return type_char == m_col_type ; } /// is this a binary bool is_binary() const { return type_binary == m_col_type ; } /// is this a datetime bool is_datetime() const { return type_datetime == m_col_type ; } /// is this a bit bool is_bit() const { return type_bit == m_col_type ; } protected: private: int m_col_number ; Types m_col_type ; void *m_col_data ; int m_col_size ; } //SqlServerCol ; /// stl support inline bool operator<( const SqlServerCol &lhs, const SqlServerCol &rhs ) { return lhs.col_number() < rhs.col_number() ; } //< inline bool operator==( const SqlServerCol &lhs, const SqlServerCol &rhs ) { return lhs.col_number() == rhs.col_number() ; } //== /// a set of columns makes a row typedef std::vector SqlServerRow ; /** * A "Row-Handler" object; when the caller issues a SELECT using * the SqlServer object below, the caller must pass an object * implementing this interface. The interface is invoked once * per processed row. */ class ISqlServerRowHandler { public: /// allow this to be in an inheritance tree virtual ~ISqlServerRowHandler() {} /// invoked once per processed row /// @param row Row of current data /// @return FALSE to halt processing virtual bool notify( const SqlServerRow & ) = 0 ; protected: /// must be provided by the caller ISqlServerRowHandler() {} private: /// disallowed for this object ISqlServerRowHandler( const ISqlServerRowHandler & ) ; ISqlServerRowHandler &operator=( const ISqlServerRowHandler & ) ; } //ISqlServerRowHandler ; /** * SQL Server parameter passed to an XP from a stored procedure call. */ class SqlServerParm { public: /// create from a SQL Server object /// @param debug Message debugger to use /// @param pSrv Server object to use /// @param nParm Base-one arg number /// @param bInitialize Pass FALSE to create an empty parm object. SqlServerParm( ABR::DebugUtil &, abrSRV_PROC *, int, bool ) ; /// create from an existing object SqlServerParm( const SqlServerParm & ) ; /// assignment support SqlServerParm &operator=( const SqlServerParm & ) ; /// object cleanup ~SqlServerParm() ; /// load data -- same parms as for ctor above void load( ABR::DebugUtil &debug, abrSRV_PROC *, int ) ; /// debug object ABR::DebugUtil &debug() { return m_debug ; } /// server object abrSRV_PROC *srv() { return m_srv ; } /// parameter number int parm_number() const { return m_parm_number ; } /// data type of the param BYTE data_type() const { return m_data_type ; } /// max length of the param ULONG max_len() const { return m_max_len ; } /// actual length of the param ULONG actual_len() const { return m_actual_len ; } /// parm data const BYTE *parm_data() const { return m_parm_data ; } /// is this a NULL parm? BOOL is_null() const { return m_is_null ; } /// is this an OUTPUT parm? BOOL is_output() const { return m_is_output ; } /// name of the parm (empty string if not given) std::string name() const { return m_name ; } /// convert to a string representation std::string to_string() const ; /// to an integer int to_int() const ; /// to floating point double to_double() const ; // to a string SqlServerDatetime to_datetime() const ; /// set data to send back to SQL Server. this is const /// because it does *not* set any data in the parameter; /// the original parameter data from SQL Server is immutable. void set_output_data( const std::string & ) const ; /// set data to send back to SQL Server. this is const /// because it does *not* set any data in the parameter; /// the original parameter data from SQL Server is immutable. void set_output_data( int ) const ; /// set data to send back to SQL Server. this is const /// because it does *not* set any data in the parameter; /// the original parameter data from SQL Server is immutable. void set_output_data( float ) const ; /// set data to send back to SQL Server. this is const /// because it does *not* set any data in the parameter; /// the original parameter data from SQL Server is immutable. void set_output_data( const SqlServerDatetime & ) const ; protected: /// setters are all protected (object is immutable to callers) void parm_number( int x ) { m_parm_number = x ; } void data_type( BYTE x ) { m_data_type = x ; } void max_len( ULONG x ) { m_max_len = x ; } void actual_len( ULONG x ) { m_actual_len = x ; } void parm_data( const BYTE * ) ; void is_null( BOOL x ) { m_is_null = x ; } void is_output( BOOL x ) { m_is_output = x ; } void name( LPCSTR x ) { m_name = x ; } /// low-level assignment void assign( const SqlServerParm & ) ; /// reset the object void reset() ; private: // disallowed SqlServerParm() ; /// data members ABR::DebugUtil &m_debug ; abrSRV_PROC *m_srv ; int m_parm_number ; BYTE m_data_type ; ULONG m_max_len ; ULONG m_actual_len ; BYTE *m_parm_data ; BOOL m_is_null ; BOOL m_is_output ; std::string m_name ; } //SqlServerParm ; /// STL container support inline bool operator<( const SqlServerParm &lhs, const SqlServerParm &rhs ) { return lhs.parm_number() < rhs.parm_number() ; } //< inline bool operator==( const SqlServerParm &lhs, const SqlServerParm &rhs ) { return lhs.parm_number() == rhs.parm_number() ; } //== /// list of SQL Server parms (ordered by arg number) typedef std::vector SqlServerParms ; /// forward ref class SqlServerConn ; /** * SQL Server interface--hides low-level details. */ class SqlServer: public ABR::IDebugSink { protected: class Conn ; public: /// only way to create one of these SqlServer( ABR::DebugUtil &, abrSRV_PROC * ) ; /// clean up ~SqlServer() ; /// debug object passed in ABR::DebugUtil &debug() { return m_debug ; } /// server object passed in abrSRV_PROC *srv() { return m_pSrv ; } /// parms for this interface call const SqlServerParms &parms() const { return m_parms ; } /// initialize the object (read parms, other internal setup) void init() ; /// send "done" message to SQL Server (one per XP call) void senddone( abrSRVRETCODE ) ; /// number of parms passed in int parm_count() const { return static_cast(m_parms.size()) ; } ///////////////////////////////////////////////////////////////////// // INTERFACE FOR SENDING RESULT SET BACK TO THE SERVER /// describe a single output column (prior to calling rs_send) /// @col_name Name of the column /// @col_type Type of the column /// @col_len Max length of the column (type_string/type_char cols only) void rs_col_describe( const std::string &, SqlServerCol::Types, int ) ; /// set a column of data /// @col_number One-based column to set /// @col_data Integer to pass; must stay in scope until rs_send called void rs_col_setdata( int, const int & ) ; /// set a column of data /// @col_number One-based column to set /// @col_data Double to pass; must stay in scope until rs_send called void rs_col_setdata( int, const double & ) ; /// set a column of data /// @col_number One-based column to set /// @col_data Raw SqlServerDatetime data void rs_col_setdata( int, const SqlServerDatetime_raw & ) ; /// set a column of data /// @col_number One-based column to set /// @col_data String to pass; must stay in sope until rs_send called /// @col_len Length of the string void rs_col_setdata( int, const char *, int ) ; /// set a column of data /// @col_number One-based column to set /// @col_data Binary data to set /// @col_len Length of the binary data void rs_col_setdata( int, const void *, int, const char *_type = "BINARY" ) ; /// send a row of data void rs_send() ; ///////////////////////////////////////////////////////////////////// // SELECT SUPPORT /// special typedef for auto-closing connection typedef std::auto_ptr ConnPtr ; /// Retrieve a persistent connection object /// @param server_name Name of the server to connect to ConnPtr make_conn( const std::string & ) ; /// Issue a SELECT to the server /// @param server_name Name of the DB server to connect back to /// @param query The query to execute /// @param row_handler Row-handling object /// @param conn *Optional* persistent connection object /// @return Number of rows processed (-1 for early termination by /// row-handler object) int issue_query( const std::string & , const std::string & , ISqlServerRowHandler & , ConnPtr *conn = NULL ) ; /// Issue a direct command to the server /// @param server_name Name of the DB server to connect back to /// @param cmd The cmd to execute /// @param conn *Optional* persistent connection object /// @return Number of rows affected int issue_cmd( const std::string & , const std::string & , ConnPtr *conn = NULL ) ; ///////////////////////////////////////////////////////////////////// // INTERFACE: IDebugSink /// send a message to SQL Server void notify( ErrorLevel, Category, LPCSTR, int, const std::string & ) ; ///////////////////////////////////////////////////////////////////// // STATICS /// returns the current version of this library static ULONG version() ; protected: friend class SqlServerConn ; /// internal connection object for SQL Server class Conn { public: Conn( SqlServer &_srv, bool _is_external ) : m_srv( _srv ) , m_is_external( _is_external ) , m_dbprocess( NULL ) , m_loginrec( NULL ) , m_impersonated( 0 ) {} virtual ~Conn() { // force connection close try { srv().close_connection( m_dbprocess, m_loginrec, m_impersonated ) ; } catch( ... ) { } //try } //dtor // access/setters SqlServer &srv() { return m_srv ; } bool is_external() const { return m_is_external ; } void *&dbprocess() { return m_dbprocess; } void *&loginrec() { return m_loginrec ; } int &impersonated() { return m_impersonated ; } void impersonated( int _x ) { m_impersonated = _x ; } protected: SqlServer &m_srv ; bool m_is_external ; void *m_dbprocess ; void *m_loginrec ; int m_impersonated ; private: // disallowed Conn() ; Conn( const Conn & ) ; Conn &operator=( const Conn & ) ; } //Conn ; friend class Conn ; void open_connection( const std::string & , void *& , void *& , int & , const char * ) ; void run_query( void *&, const std::string & ) ; void close_connection( void *& , void *& , int & ) ; void build_row_headers( void * , SqlServerRow & , int & , bool & ) ; int handle_row( void * , SqlServerRow & , int , ISqlServerRowHandler * , int & ) ; int process_rows( void * , SqlServerRow & , int , ISqlServerRowHandler * , bool ) ; int process_query( void * , ISqlServerRowHandler * ) ; /// internal query issuer int issue_query_i( const std::string & , const std::string & , ISqlServerRowHandler & , ConnPtr *_conn = 0 ) ; /// internal command issuer int issue_cmd_i( const std::string & , const std::string & , ConnPtr *_conn = 0 ) ; private: /// disallowed SqlServer() ; SqlServer( const SqlServer & ) ; SqlServer &operator=( const SqlServer & ) ; /// sql server interface pointer ABR::DebugUtil &m_debug ; abrSRV_PROC *m_pSrv ; SqlServerParms m_parms ; int m_nDescribeCol ; } //SqlServer ; /** * SQL Server connection--can be passed to the query execution in * the functions below. Note that the connection is managed * */ class SqlServerConn: public SqlServer::Conn { public: /// only way to create the object /// @param _srv SqlServer object owning this object SqlServerConn( SqlServer &_srv ) : SqlServer::Conn( _srv, true ) {} ~SqlServerConn() {} private: // disallowed SqlServerConn() ; SqlServerConn( const SqlServerConn & ) ; SqlServerConn &operator=( const SqlServerConn & ) ; } //SqlServerConn ; } //namespace ABR #endif