unit fpodbc; {$mode objfpc} {$h+} interface uses odbcsql,SysUtils,Classes; Type TDSNTypes = (dtUser,dtSystem,dtBoth); TODBCParamType = (ptUnknown,ptInput,ptInputOutput,ptResult,ptOutput,ptRetVal); TODBCParamTypes = Set of TODBCParamType; TODBCObject = Class(TComponent) Private FHandle : SQLHandle; FHandleType : SQLSmallint; Function GetHandle : SQLHandle; function GetHandleAllocated: Boolean; function GetExtendedErrorInfo: String; Protected Function CreateHandle : SQLHandle; Virtual; Function ParentHandle : SQLHandle; Virtual; Procedure FreeHandle; Function CheckODBC(Res : Integer;Msg : String) : Integer; Public Destructor Destroy; override; Property Handle : SQLHandle Read GetHandle; Property HandleAllocated : Boolean Read GetHandleAllocated; end; TODBCEnvironment = Class(TODBCObject) Private FODBCBehaviour : Integer; procedure SetODBCbehaviour(const Value: Integer); function GetNullTerminate: Boolean; procedure SetNullTerminate(const Value: Boolean); protected function CreateHandle: SQLHandle; override; Procedure SetIntAttribute(Const Attr,Value : Integer); Procedure SetStringAttribute(Const Attr: Integer; Value : String); Function GetIntAttribute(Const Attr : Integer) : Integer; Function GetStringAttribute(Const Attr : Integer) : String; Public Constructor Create(Aowner : TComponent);override; Function GetDriverNames(List : Tstrings) : Integer; Function GetDataSourceNames(List : Tstrings; Types : TDSNTypes;Descriptions : Boolean) : Integer; function GetDriverOptions(Driver: String; Options: TStrings): Integer; Property ODBCBehaviour : Integer Read FODBCBehaviour Write SetODBCbehaviour; Property NullTerminateStrings : Boolean Read GetNullTerminate Write SetNullTerminate; end; TConnectionBrowseEvent = Procedure (Sender : TObject;InParams,OutParams : Tstrings) of Object; TODBCConnection = Class(TODBCObject) Private FActive : Boolean; FDriverParams : TStrings; FDSN, FDriverName, FUserName, FPassword : String; FEnvironMent : TODBCEnvironment; FOnBrowseConnection : TConnectionBrowseEvent; FWindowHandle : integer; FDriverCOmpletion: SQLUSmallInt; function GetDriverName: String; function GetDriverParams: TStrings; procedure SetActive(const Value: Boolean); procedure SetDriverName(const Value: String); procedure SetDriverParams(const Value: TStrings); procedure SetDSN(const Value: String); function GetEnvironment: TODBCEnvironMent; procedure SetEnvironment(const Value: TODBCEnvironMent); Protected procedure ConnectToDriver; procedure ConnectToDSN; Procedure ConnectBrowsing; Function ParentHandle : SQLHandle; override; Procedure CheckActive; Procedure CheckInActive; Public Constructor Create(Aowner : TComponent);override; Destructor Destroy; override; Procedure Connect; Procedure Disconnect; Procedure GetTableNames(S : TStrings; SystemTables : Boolean); Procedure GetFieldNames(TableName : String; S : TStrings); Procedure GetPrimaryKeyFields(TableName : String; S : TStrings); procedure GetProcedureNames(S : TStrings); procedure GetProcedureParams(ProcName : String;ParamTypes : TODBCParamTypes; S : TStrings); Property DSN : String Read FDSN Write SetDSN; Property DriverName : String Read GetDriverName Write SetDriverName; Property DriverCompletion : SQLUSmallInt Read FDriverCOmpletion Write FDriverCompletion; Property DriverParams : TStrings Read GetDriverParams Write SetDriverParams; Property Active : Boolean Read FActive Write SetActive; Property Environment : TODBCEnvironMent Read GetEnvironment Write SetEnvironment; Property UserName : String Read FUserName Write FUserName; Property Password : string Read FPassword Write FPassword; Property OnBrowseConnection : TConnectionBrowseEvent Read FonBrowseConnection Write FOnBrowseConnection; Property WindowHandle : integer Read FWindowHandle Write FWindowHandle; end; TODBCStatement = Class; TODBCFieldList = Class(TCollection) Private FStatement : TODBCStatement; Public Constructor Create(Statement : TODBCStatement); end; { TODBCStatement allocates 1 big data buffer. For each bound field two things are allocated in the buffer: - Size of fetched data as filled in by fetch. - data. (may be zero for blobs etc) The FBuffOffset contains the offset in the buffer of the size field. Data immediatly follows the size. } TODBCField = Class(TCollectionItem) Private FDecimalDigits, FPosition : SQLSmallInt; FName : String; FSize : SQLUInteger; // Declared size, as returned by DescribeCol FNullable : Boolean; FDataType : SQLSmallInt; // Declared type, as returned by DescribeCol FBuffOffSet : SQLInteger; // Offset in data buffer. FBuffer : Pointer; // Pointer to data. FBufSize : SQLInteger; // Allocated buffer size. FBufType : SQLSmallInt; // Allocated buffer type function GetAsString: String; function GetData : PChar; Function GetIsNull : Boolean; Function GetAsInteger : Integer; Function GetAsBoolean : Boolean; Function GetAsDouble : Double; Function GetAsDateTime : TDateTime; Public Property Position : SQLSmallint Read FPosition; Property Name : String read FName; Property DataType : SQLSmallInt read FDatatype; Property Size : SQLUinteger read FSize; property DecimalDigits : SQLSmallInt read FDecimalDigits; Property Nullable : Boolean Read FNullable; Property Data : Pchar Read GetData; Property BufType : SQLSmallInt Read FBufType; Property BufSize : SQLInteger Read FBufSize; Property IsNull : Boolean Read GetIsNull; Property AsString : String Read GetAsString; Property AsInteger : Integer Read GetAsInteger; Property AsBoolean : Boolean Read GetAsBoolean; Property AsDouble : Double Read GetAsDouble; Property AsDateTime : TDateTime Read GetAsDateTime; end; TODBCStatement = Class(TODBCObject) Private FBOF,FEOF : Boolean; FConnection: TODBCConnection; FFields : TODBCFieldList; FBuffer : Pointer; Protected Function ParentHandle : SQLHandle; override; procedure SetConnection(const Value: TODBCConnection); procedure AllocBuffers; Public Constructor Create(Aowner : TComponent);override; Destructor Destroy; override; Procedure BindFields(RestrictList : TStrings);virtual; Procedure ClearFields;virtual; Function Fetch : Boolean; Property Connection : TODBCConnection Read FConnection Write SetConnection; Property BOF : Boolean read FBOF; Property EOF : Boolean read FEOF; Property Fields : TODBCFieldList Read FFields; end; TODBCTableList = Class(TODBCStatement) Public Procedure GetTableNames(S : TStrings; SystemTables : Boolean); end; TODBCFieldNamesList = Class(TODBCStatement) Public Procedure GetFieldNames(TableName : String;S : TStrings); end; TODBCPrimaryKeyFieldsList = Class(TODBCStatement) Public Procedure GetPrimaryKeyFields(TableName : String;S : TStrings); end; TODBCProcedureList = Class(TODBCStatement) Public Procedure GetProcedureList(S : TStrings); end; TODBCProcedureParams = Class(TODBCStatement) Procedure GetProcedureParams(ProcName: String; ParamTypes: TODBCParamTypes; S: TStrings); end; TStatementState = (ssInactive,ssPrepared,ssBound,ssOpen); TODBCSQLStatement = Class(TODBCStatement) Private FSQL : TStrings; FState : TStatementState; function GetActive: Boolean; procedure SetActive(const Value: Boolean); Protected procedure FreeStatement(Option: SQLUSMALLINT); procedure ExecuteDirect; procedure ExecutePrepared; Procedure SetSQL(const Value: TStrings); Public Constructor Create(Aowner : TComponent);override; Destructor Destroy; override; procedure Prepare; procedure Unprepare; Procedure BindFields(RestrictList : TStrings);override; procedure ExecSQL; Procedure Open; Procedure Close; procedure GetFieldList(List: TStrings); Property Active : Boolean Read GetActive Write SetActive; Property SQL : TStrings Read FSQL Write SetSQL; end; EODBCError = Class(Exception); Const ODBCParamTypeNames : Array [TODBCParamType] of string = ('Unknown','Input','Input/Output','Result','Output','RetVal'); Function DefaultEnvironment : TODBCEnvironment; implementation { TODBCObject } resourcestring SErrUnexpected = 'Unexpected ODBC error:'; SErrEnvironmentHandle = 'Cannot allocate environment handle:'; SErrInvalidBehaviour = 'Invalid value for ODBC behaviour: %d'; SErrNotConnected = 'Operation invalid when not connected.'; SErrConnected = 'Operation invalid when connected.'; SNeedDSNOrDriver = 'Cannot connect with empty DSN and driver names.'; SErrGettingDataSources = 'Error getting datasources:'; SErrGettingDriverNames = 'Error getting driver names:'; SErrGettingDriverOptions = 'Error getting driver options:'; SErrSettingEnvAttribute = 'Error setting environment attribute:'; SErrGettingEnvAttribute = 'Error Getting environment attribute:'; SErrBrowseConnecting = 'Error connecting to datasource via browse:'; SErrDSNConnect = 'Error connecting to DSN:'; SErrDriverConnect = 'Error connecting to driver:'; SErrDisconnecting = 'Error disconnecting:'; SErrNoConnectionForStatement = 'Missing connection for statement.'; SErrNoSQLStatement = 'Missing SQL statement.'; SErrPreparing = 'Error preparing statement:'; SErrGettingTableNames = 'Error getting table names:'; SErrFetchingData = 'Error fetching data:'; SErrFieldNames = 'Error getting field names:'; SErrPrimaryKeys = 'Error getting primary key names:'; SErrProcedureNames = 'Error getting procedure names:'; SErrExecuting = 'Error while executing statement:'; SErrExecutingPrepared = 'Error while executing prepared statement:'; SErrNotPrepared = 'Statement is not prepared'; SErrNotInactive = 'Statement is already prepared or executed.'; SErrStatementActive = 'A statement is still active'; SErrColumnCount = 'Error retrieving cilumn count:'; SErrColDescription = 'Error retrieving column description'; SErrInvalidConversion = 'invalid type conversion'; SErrBindCol = 'Error binding column'; Const ODBCSuccess = [SQL_SUCCESS,SQL_SUCCESS_WITH_INFO]; Procedure ODBCError (Msg : String); begin Raise EODBCError.Create(Msg); end; Procedure ODBCErrorFmt (Fmt : String;Args : Array of const); begin Raise EODBCError.CreateFmt(Fmt,Args); end; Function CheckODBC(Res : Integer;Msg : String) : Integer; begin Result:=Res; if not Res in [SQL_SUCCESS,SQL_SUCCESS_WITH_INFO] then begin If MSG='' then MSG:=SErrUnexpected; ODBCErrorFmt(msg,[res]); end; end; function TODBCObject.CheckODBC(Res: Integer; Msg: String): Integer; Var S : String; begin Result:=Res; if not Res in [SQL_SUCCESS,SQL_SUCCESS_WITH_INFO] then begin If MSG='' then MSG:=SErrUnexpected; S:=GetExtendedErrorInfo; If S<>'' then Msg:=Msg+LineEnding+S; ODBCError(msg); end; end; function TODBCObject.GetExtendedErrorInfo : String; Var Res : SQLreturn; I,MsgLen : SQLSmallInt; SQLState : Array[0..6] of Char; NativeError : SQLInteger; MSg : Array[0..SQL_MAX_MESSAGE_LENGTH] of Char; SState,SMsg : String; begin I:=0; Result:=''; Repeat Inc(i); Res:=SQLGetDiagRec(FhandleType, FHandle, i, SqlState, NativeError, Msg, sizeof(Msg), MsgLen); If Res<>SQL_NO_DATA then begin SState:=SQLState; SMsg:=Msg; If Length(Result)>0 then Result:=Result+LineEnding; Result:=Result+Format('[%s] : %s (%d)',[SState,SMsg,NativeError]); end; Until (Res=SQL_NO_DATA); end; function TODBCObject.CreateHandle: SQLHandle; begin {$ifdef debug} Writeln(Classname,': Creating handle of type ',FHAndleType,' and parent ',ParentHandle); {$endif} CheckODBC(SQLAllocHandle(FHandleType,ParentHandle,FHandle),SErrEnvironmentHandle); Result:=FHandle; end; destructor TODBCObject.Destroy; begin If FHandle<>0 then FreeHandle; inherited; end; procedure TODBCObject.FreeHandle; begin If FHandle<>0 then begin SQLFreeHandle(FHandleType,FHandle); FHandle:=0; end; end; function TODBCObject.GetHandle: SQLHandle; begin If FHandle=0 then CreateHandle; Result:=FHandle; end; function TODBCObject.GetHandleAllocated: Boolean; begin Result:=(FHandle<>0) end; function TODBCObject.ParentHandle: SQLHandle; begin Result:=SQL_NULL_HANDLE; end; { TODBCEnvironment } constructor TODBCEnvironment.Create(Aowner: TComponent); begin FHandleType:=SQL_HANDLE_ENV; inherited; end; function TODBCEnvironment.CreateHandle: SQLHandle; begin Result:=Inherited CreateHandle; ODBCbehaviour:=SQL_OV_ODBC3; end; function TODBCEnvironment.GetDataSourceNames(List: Tstrings; Types: TDSNTypes;Descriptions : Boolean): Integer; var DSNName, DSNDesc: array[0..SQL_MAX_OPTION_STRING_LENGTH] of Char; lenn,lend : SQLSmallInt; Dir : SQLSmallInt; Sn,SD : String; begin Case Types of dtSystem : Dir:=SQL_FETCH_FIRST_SYSTEM; dtUser : Dir:=SQL_FETCH_FIRST_USER; dtBoth : Dir:=SQL_FETCH_FIRST; end; List.Clear; CheckODBC(SQLDatasources(Handle, Dir, DSNName,SQL_MAX_OPTION_STRING_LENGTH, @lenn, DSNDesc,SQL_MAX_OPTION_STRING_LENGTH, @lend),SErrGettingDataSources); Repeat If Not Descriptions then List.Add(DSNName) else begin SN:=DSNName; SD:=DSNDesc; List.Add(SN+'='+SD); end; Until Not (SQLDataSources(Handle, SQL_FETCH_NEXT, DSNName, SQL_MAX_OPTION_STRING_LENGTH, @lenn, DSNDesc,SQL_MAX_OPTION_STRING_LENGTH, @lend) in ODBCSuccess); Result:=List.Count; end; function TODBCEnvironment.GetDriverNames(List : Tstrings): Integer; Var DriverName: array[0..SQL_MAX_OPTION_STRING_LENGTH] of Char; len : SQLSmallInt; begin List.Clear; CheckODBC(SQLDrivers(Handle, SQL_FETCH_FIRST, DriverName, SQL_MAX_OPTION_STRING_LENGTH, @len, Nil,0,Nil),SErrGettingDriverNames); Repeat List.Add(DriverName); Until Not (SQLDrivers(Handle, SQL_FETCH_NEXT, DriverName, SQL_MAX_OPTION_STRING_LENGTH, @len, Nil,0,Nil) in ODBCSuccess); Result:=List.Count; end; function TODBCEnvironment.GetDriverOptions(Driver : String;Options: Tstrings): Integer; Var DriverName, DriverOptions: array[0..SQL_MAX_OPTION_STRING_LENGTH] of Char; lenn,leno : SQLSmallInt; Found : Boolean; P : PChar; S : string; begin CheckODBC(SQLDrivers(Handle, SQL_FETCH_FIRST, DriverName, SQL_MAX_OPTION_STRING_LENGTH, @lenn, DriverOptions, SQL_MAX_OPTION_STRING_LENGTH,@Leno),SErrGettingDriverOptions); Result:=0; Options.Clear; Repeat Found:=CompareText(Driver,DriverName)=0; If Found then begin P:=@DriverOptions[0]; While P[0]<>#0 do begin S:=StrPas(P); options.Add(S); Inc(P,Length(S)+1); end; end; Until Not (SQLDrivers(Handle, SQL_FETCH_NEXT, DriverName, SQL_MAX_OPTION_STRING_LENGTH, @lenn, DriverOptions, SQL_MAX_OPTION_STRING_LENGTH,@Leno) in ODBCSuccess) or Found; Result:=Options.Count; end; function TODBCEnvironment.GetIntAttribute(const Attr: Integer): Integer; begin CheckODBC(SQLSetEnvAttr(Handle,Attr,SQLPointer(@result),0),SErrSettingEnvAttribute); end; function TODBCEnvironment.GetNullTerminate: Boolean; begin Result:=(GetIntAttribute(SQL_ATTR_OUTPUT_NTS)=SQL_TRUE); end; function TODBCEnvironment.GetStringAttribute(const Attr: Integer): String; Var OldLen,Len: Integer; begin OldLen:=0; Repeat Inc(OldLen,255); SetLength(Result,OldLen); CheckODBC(SQLGetEnvAttr(Handle,Attr,SQLPointer(@result),OldLen,@Len),SErrGettingEnvAttribute); until (Len<=OldLen); SetLength(Result,Len); end; procedure TODBCEnvironment.SetIntAttribute(const Attr, Value: Integer); begin CheckODBC(SQLSetEnvAttr(Handle,Attr,SQLPointer(Value),0),SErrSettingEnvAttribute); end; procedure TODBCEnvironment.SetNullTerminate(const Value: Boolean); begin If Value then SetIntAttribute(SQL_ATTR_OUTPUT_NTS,SQL_TRUE) else SetIntAttribute(SQL_ATTR_OUTPUT_NTS,SQL_FALSE); end; procedure TODBCEnvironment.SetODBCbehaviour(const Value: Integer); begin If (Value<>FODBCBehaviour) then begin If Not (Value in [SQL_OV_ODBC3,SQL_OV_ODBC2]) Then ODBCErrorFmt(SErrInvalidBehaviour,[Value]); SetIntAttribute(SQL_ATTR_ODBC_VERSION,Value); FODBCBehaviour := Value; end; end; procedure TODBCEnvironment.SetStringAttribute(const Attr: Integer; Value: String); begin CheckODBC(SQLSetEnvAttr(Handle,Attr,SQLPointer(Value),Length(Value)),SErrSettingEnvAttribute); end; { TODBCConnection } procedure TODBCConnection.CheckActive; begin If Not FActive then ODBCError(SErrNotConnected); end; procedure TODBCConnection.CheckInActive; begin If FActive then ODBCError(SErrConnected); end; procedure TODBCConnection.Connect; begin If Not FActive then begin If Assigned (FonBrowseConnection) then ConnectBrowsing else If (FDSN<>'') then ConnectToDSN else if FDriverName<>'' then ConnectToDriver else ODBCError(SNeedDSNOrDriver); FActive:=True; end; end; Function ListToBuf(List : Tstrings; Buf : PChar; Sep : Char; MaxLen : Integer) : Boolean; Var P : PChar; S : String; I,Len : Integer; begin P:=Buf; I:=0; Result:=True; While Result and (I#0) or (totlen#0 then Inc(P,1); inc(Totlen,Len+1); end; Result:=List.Count; end; Procedure TODBCConnection.ConnectBrowsing; Var Inlist,OutList : TStringList; InStr, OutStr: Array[0..SQL_MAX_OPTION_STRING_LENGTH] of Char; i,Res : Integer; olen : SQLSmallint; begin InList:=TStringList.Create; OutList:=TstringList.Create; try If FDSN<>'' then InList.Add('DSN='+FDSN) else If FDriverName<>'' then begin Inlist.Add('DRIVER='+FDriverName); For I:=0 to DriverParams.Count-1 do Inlist.Add(DriverParams[i]); end; Repeat ListToBuf(Inlist,Instr,';',SQL_MAX_OPTION_STRING_LENGTH); Res:=SQLBrowseConnect(Handle,Instr,SQL_NTS,Outstr,SQL_MAX_OPTION_STRING_LENGTH,Olen); If RES=SQL_NEED_DATA then begin OutList.Clear; BufToList(OutStr,Olen,OutList,';'); FOnBrowseConnection(Self,InList,OutList); end Until (Res<>SQL_NEED_DATA); CheckODBC(Res,SErrBrowseConnecting); Finally Outlist.free; InList.Free; end; end; Procedure TODBCConnection.ConnectToDSN; begin CheckODBC(SQLConnect(Handle,PSQLChar(FDSN),SQL_NTS, PSQLChar(FUserName),SQL_NTS, PSQLChar(FPassword),SQL_NTS),SErrDSNConnect); end; Procedure TODBCConnection.ConnectToDriver; Var Instr, OutStr : Array[0..SQL_MAX_OPTION_STRING_LENGTH] of Char; OLen : SQLSmallint; InList : TStringList; begin InList:=TStringList.Create; Try Inlist.Assign(DriverParams); Inlist.Insert(0,'DRIVER={'+DRIVERNAME+'}'); ListToBuf(Inlist,InStr,';',SQL_MAX_OPTION_STRING_LENGTH); Finally Inlist.Free; end; CheckODBC(SQLDriverConnect(Handle,FWindowHandle, Instr,SQL_NTS, OutStr,SQL_MAX_OPTION_STRING_LENGTH, Olen,FDriverCompletion),SErrDriverConnect); end; constructor TODBCConnection.Create(Aowner: TComponent); begin inherited; FHandleType:=SQL_HANDLE_DBC; FDriverParams:=TStringList.Create; FDriverCompletion:=SQL_DRIVER_NOPROMPT; end; destructor TODBCConnection.Destroy; begin Disconnect; inherited; end; procedure TODBCConnection.Disconnect; begin If FActive then begin CheckODBC(SQLDisconnect(Handle),SErrDisconnecting); Factive:=False; end; end; function TODBCConnection.GetDriverName: String; begin Result:=FDriverName; end; function TODBCConnection.GetDriverParams: TStrings; begin Result:=FDriverParams; end; function TODBCConnection.GetEnvironment: TODBCEnvironMent; begin If FEnvironment=Nil then result:=DefaultEnvironment else Result:=FEnvironment; end; procedure TODBCConnection.SetActive(const Value: Boolean); begin If Value then Connect else Disconnect; end; procedure TODBCConnection.SetDriverName(const Value: String); begin CheckInactive; FDSN:=''; If CompareText(FDriverName,Value)<>0 then begin FDriverName:=Value; FDriverParams.Clear; end; end; procedure TODBCConnection.SetDriverParams(const Value: TStrings); begin CheckInactive; FDriverParams.Assign(Value); end; procedure TODBCConnection.SetDSN(const Value: String); begin CheckInactive; FDSN := Value; end; procedure TODBCConnection.SetEnvironment(const Value: TODBCEnvironMent); begin CheckInactive; If (Value<>Environment) then // !! may be defaultenvironment... begin If HandleAllocated then FreeHandle; FEnvironment:=Value end; end; function TODBCConnection.ParentHandle: SQLHandle; begin Result:=Environment.Handle end; Const DefEnv : Pointer = Nil; Function DefaultEnvironment : TODBCEnvironment; begin If DefEnv=Nil then DefEnv:=TODBCEnvironment.Create(Nil); Result:=TODBCEnvironment(DefEnv); end; procedure TODBCConnection.GetTableNames(S: TStrings; SystemTables: Boolean); begin With TODBCTableList.Create(Self) do try GetTableNames(S,SystemTables); finally Free; end; end; procedure TODBCConnection.GetFieldNames(TableName: String; S: TStrings); begin With TODBCFieldNamesList.Create(Self) do try GetFieldNames(TableName,S); finally Free; end; end; procedure TODBCConnection.GetPrimaryKeyFields(TableName: String; S: TStrings); begin With TODBCPrimaryKeyFieldsList.Create(Self) do try GetPrimaryKeyFields(TableName,S); finally Free; end; end; procedure TODBCConnection.GetProcedureNames(S: TStrings); begin With TODBCProcedureList.Create(Self) do try GetProcedureList(S); Finally Free; end; end; procedure TODBCConnection.GetProcedureParams(ProcName: String; ParamTypes: TODBCParamTypes; S: TStrings); begin With TODBCProcedureParams.Create(Self) do Try GetProcedureParams(ProcName,Paramtypes,S); finally Free; end; end; { TODBCStatement } Type TODBCFieldBufRec = Record T{ype} : SQlSmallint; B{ufsize} : SQLInteger; {Buftyp}e : SQLSmallint; end; Const BufDescrCount = 26; BufDescr : Array[1..BufDescrCount] of TODBCFieldBufRec = { Type Bufsize Buftype } ( (T:SQL_CHAR ;b:-1 ;e: SQL_CHAR), (T:SQL_NUMERIC ;b:sizeof(SQLDouble) ;e: SQL_DOUBLE), (T:SQL_DECIMAL ;b:sizeof(SQLDouble) ;e: SQL_DOUBLE), (T:SQL_INTEGER ;b:sizeof(SQLInteger) ;e: SQL_INTEGER), (T:SQL_SMALLINT ;b:sizeof(SQLSmallInt) ;e: SQL_SMALLINT), (T:SQL_FLOAT ;b:sizeof(SQLDOUBLE) ;e: SQL_DOUBLE), (T:SQL_REAL ;b:sizeof(SQLDOUBLE) ;e: SQL_DOUBLE), (T:SQL_DOUBLE ;b:Sizeof(SQLDOUBLE) ;e: SQL_DOUBLE), (T:SQL_DATE ;b:Sizeof(SQL_DATE_STRUCT) ;e: SQL_DATE), (T:SQL_TIME ;b:sizeof(SQL_TIME_STRUCT) ;e: SQL_TIME), (T:SQL_TIMESTAMP ;b:sizeof(SQL_TIMESTAMP_STRUCT) ;e: SQL_TIMESTAMP), (T:SQL_VARCHAR ;b:-1 ;e: SQL_CHAR), (T:SQL_UNKNOWN_TYPE ;b:0 ;e: SQL_UNKNOWN_TYPE), (T:SQL_LONGVARCHAR ;b:-1 ;e: SQL_CHAR), (T:SQL_BINARY ;b:-2 ;e: SQL_BINARY), (T:SQL_VARBINARY ;b:-2 ;e: SQL_BINARY), (T:SQL_LONGVARBINARY ;b:-2 ;e: SQL_BINARY), (T:SQL_BIGINT ;b:sizeOf(SQLDOUBLE) ;e: SQL_DOUBLE), (T:SQL_TINYINT ;b:Sizeof(SQLSMALLINT) ;e: SQL_SMALLINT), (T:SQL_BIT ;b:sizeof(SQL_CHAR) ;e: SQL_BIT), (T:SQL_WCHAR ;b:-1 ;e: SQL_CHAR), (T:SQL_WVARCHAR ;b:-1 ;e: SQL_CHAR), (T:SQL_WLONGVARCHAR ;b:-1 ;e: SQL_CHAR), (T:SQL_TYPE_DATE ;b:sizeof(SQL_DATE_STRUCT) ;e: SQL_TYPE_DATE), (T:SQL_TYPE_TIME ;b:sizeof(SQL_TIME_STRUCT) ;e: SQL_TYPE_TIME), (T:SQL_TYPE_TIMESTAMP;b:sizeof(SQL_TIMESTAMP_STRUCT) ;e: SQL_TYPE_TIMESTAMP) ); { // template (T: ;b: ;e: ), } Function GetColSizeBufType (Coltype: SQLSmallint; Var BufSize : SQLInteger; Var BufType : SQLSmallInt) : Boolean; Var I : Integer; begin I:=0; BufSize:=0; BufType:=0; While (I<=BufDescrCount) and (BufDescr[i].t<>Coltype) do Inc(i); Result:=(i<=BufDescrCount); If Result then begin BufSize:=BufDescr[i].b; BufType:=BufDescr[i].e; end; end; procedure TODBCStatement.BindFields(RestrictList : TStrings); Var Count: SQLSmallInt; CName : Array[0..SQL_NAME_LEN] of Char; CSize : SQLUINTEGER; CDataType,CDecimals,CNullable,CNameLen: SQLSmallInt; I : integer; begin CheckODBC(SQLNumResultCols(Handle,Count),SErrColumnCount); For I:=1 to Count do begin CheckODBC(SQLDescribeCol(Handle,i,CName,SQL_NAME_LEN,CNameLen, CdataType,CSize, CDecimals,CNullable) ,SErrColDescription); If Not Assigned(RestrictList) or (RestrictList.IndexOf(Cname)<>-1) then With FFields.Add as TODBCField do begin FPosition:=I; FName:=Cname; FDataType:=CDataType; FSize:=CSize; FDecimalDigits:=CDecimals; FNullable:=(CNullable=SQL_TRUE); GetColsizeBufType(FDataType,FBufSize,FBufType); If FBufSize=-1 then FBufSize:=FSize; end; end; AllocBuffers; For I:=0 to Count-1 do With FFields.Items[i] as TODBCField do CheckODBC(SQLBindCol(Handle,FPosition,FBufType,GetData,FBufSize,FBuffer+FBuffOffset) ,SErrBindCol); end; procedure TODBCStatement.ClearFields; begin FFields.Clear; end; constructor TODBCStatement.Create(Aowner: TComponent); begin FHandleType:=SQL_HANDLE_STMT; inherited; If AOwner is TODBCConnection then Connection:=TODBCConnection(Aowner); FFields:=TODBCFieldList.Create(Self); end; function TODBCStatement.ParentHandle: SQLHandle; begin If (Connection=Nil) then ODBCError(SErrNoConnectionForStatement); Result:=Connection.Handle; end; procedure TODBCStatement.SetConnection(const Value: TODBCConnection); begin If Value<>FConnection then begin If HandleAllocated then FreeHandle; FConnection := Value; end; end; Function TODBCStatement.fetch : Boolean; Var res : SQLReturn; begin Res:=SQLFetch(Handle); Result:=(Res=SQL_SUCCESS); If Not Result and (Res<>SQL_NO_DATA) then CheckODBC(Res,SErrFetchingData); FBof:=False; If (Res=SQL_NO_DATA) then FEOF:=True; end; destructor TODBCStatement.Destroy; begin FFields.Free; inherited; end; { TODBCSQLStatement } procedure TODBCSQLStatement.GetFieldList(List : TStrings); Var Count: SQLSmallInt; CName : Array[0..SQL_NAME_LEN] of Char; CSize : SQLUINTEGER; CDataType,CDecimals,CNullable,CNameLen: SQLSmallInt; I : integer; begin if Not (FState in [ssPrepared,ssBound,ssOpen]) then ODBCError(SErrNotPrepared); List.Clear; CheckODBC(SQLNumResultCols(Handle,Count),SErrColumnCount); For I:=1 to Count do begin CheckODBC(SQLDescribeCol(Handle,i,CName,SQL_NAME_LEN,CNameLen, CdataType,CSize, CDecimals,CNullable) ,SErrColDescription); List.Add(CName); end; end; procedure TODBCSQLStatement.Unprepare; begin Case FState of ssBound,ssOpen : begin ClearFields; FreeStatement(SQL_CLOSE); end; ssPrepared : begin FreeStatement(SQL_CLOSE); end; end; FState:=ssInactive; end; procedure TODBCSQLStatement.FreeStatement(Option : SQLUSMALLINT); begin SQLFreeStmt(Handle,SQL_CLOSE); end; procedure TODBCSQLStatement.Close; begin if FState<>ssInactive then begin Unprepare; FreeStatement(SQL_CLOSE); FState:=ssInactive; end; end; constructor TODBCSQLStatement.Create(Aowner: TComponent); begin inherited; FSQL:=TStringList.Create; end; destructor TODBCSQLStatement.Destroy; begin if FState=ssOpen then Close else If FState<>ssInactive then Unprepare; FSQL.Free; inherited; end; procedure TODBCSQLStatement.ExecSQL; begin Case FState of ssPrepared,ssBound : ExecutePrepared; ssInactive : ExecuteDirect; else Raise Exception.Create(SErrStatementActive) end; end; procedure TODBCSQLStatement.ExecuteDirect; Var S : String; begin if FState<>ssInactive then ODBCError(SErrStatementActive); S:=SQL.Text; CheckODBC(SQLExecDirect(Handle,PChar(S),SQL_NTS),SErrExecuting); end; procedure TODBCSQLStatement.ExecutePrepared; begin if Not (FState in [ssPrepared,ssBound]) then ODBCError(SErrNotPrepared); CheckODBC(SQLExecute(Handle),SErrExecutingPrepared); end; function TODBCSQLStatement.GetActive: Boolean; begin Result:=(FState=ssOpen); end; procedure TODBCSQLStatement.Open; begin if (FState<>ssOpen) then begin Writeln('Preparing'); If FState=ssInactive then Prepare; Writeln('Bind fields'); if FState=ssPrepared then BindFields(Nil); Writeln('Executing'); ExecSQL; Writeln('Fetching'); If FState=ssBound then Fetch; FState:=ssOpen; FBOF:=True; end; end; procedure TODBCSQLStatement.Prepare; Var S : String; begin If FState<>ssInactive then ODBCError(SErrNotInactive); If (FSQL.Count=0) then ODBCError(SErrNoSQLStatement); S:=FSQL.Text; CheckODBC(SQLPrepare(Handle,PChar(S),SQL_NTS),SErrPreparing); FState:=ssPrepared; end; procedure TODBCSQLStatement.SetActive(const Value: Boolean); begin If Value then Open else Close; end; procedure TODBCSQLStatement.SetSQL(const Value: TStrings); begin FSQL.Assign(Value); end; procedure TODBCSQLStatement.BindFields(RestrictList: TStrings); begin inherited; FState:=ssBound; end; procedure TODBCStatement.AllocBuffers; Var I,TotalSize,AddSize : Integer; begin TotalSize:=0; For i:=0 to FFields.Count-1 do With (FFields.Items[i] as TODBCField) do begin AddSize:=FBufSize; If FBufSize=-2 then // Blob. AddSize:=0 else if FBufSize=-1 then AddSize:=FSize+1; // some Char variant. // Store offset temporarily in FData FBuffOffset:=TotalSize; Inc(TotalSize,AddSize+SizeOf(SQLinteger)); end; FBuffer:=GetMem(TotalSize); TotalSize:=0; For i:=0 to FFields.Count-1 do With (FFields.Items[i] as TODBCField) do FBuffer:=Self.FBuffer; end; { TODBCTableList } procedure TODBCTableList.GetTableNames(S: TStrings; SystemTables : Boolean); var TName, TType: array[0..SQL_NAME_LEN+1] of char; NL,TL: SQLINTEGER; Res: SQLRETURN; begin S.Clear; Res:=CheckODBC(SQLTables(handle, nil,0,nil,0,nil,0,nil,0),SErrGettingTableNames); if Res=SQL_SUCCESS then begin // Must bind by colno, because names changed between ODBC 2.0 and 3.0 !! SQLBindCol(handle,3,SQL_CHAR,@TName,SQL_NAME_LEN,@NL); SQLBindCol(handle,4,SQL_CHAR,@TType,SQL_NAME_LEN,@TL); While Fetch do if SystemTables or (CompareText(TType,'SYSTEM TABLE')<>0) then S.Add(TName); end; end; { TODBCFieldNamesList } procedure TODBCFieldNamesList.GetFieldNames(TableName: String; S: TStrings); var FName : array[0..SQL_NAME_LEN+1] of char; NF : SQLINTEGER; Res: SQLRETURN; begin S.Clear; Res:=CheckODBC(SQLColumns(handle, nil, 0, nil, 0, pchar(TableName), SQL_NTS, nil, 0),SErrFieldNames); if Res=SQL_SUCCESS then begin SQLBindCol(handle, 4, SQL_CHAR, @FNAme, SQL_NAME_LEN, @NF); While Fetch do S.Add(FName); end; end; { TODBCPrimaryKeyFieldsList } procedure TODBCPrimaryKeyFieldsList.GetPrimaryKeyFields(TableName: String; S: TStrings); var FName : array[0..SQL_NAME_LEN+1] of char; NF : SQLINTEGER; Res: SQLRETURN; begin S.Clear; Res:=CheckODBC(SQLPrimaryKeys(handle, nil, 0, nil, 0, pchar(TableName), SQL_NTS),SErrPrimaryKeys); if Res=SQL_SUCCESS then begin SQLBindCol(handle, 4, SQL_CHAR, @FNAme, SQL_NAME_LEN, @NF); While Fetch do S.Add(FName); end; end; { TODBCProcedureList } procedure TODBCProcedureList.GetProcedureList(S: TStrings); var PName : array[0..SQL_NAME_LEN+1] of char; NP : SQLINTEGER; Res: SQLRETURN; begin S.Clear; Res:=CheckODBC(SQLProcedures(handle, nil, 0, nil, 0, Nil, 0),SErrProcedureNames); if Res=SQL_SUCCESS then begin SQLBindCol(handle, 3, SQL_CHAR, @PNAme, SQL_NAME_LEN, @NP); While Fetch do S.Add(PName); end; end; { TODBCProcedureParams } procedure TODBCProcedureParams.GetProcedureParams(ProcName: String; ParamTypes: TODBCParamTypes; S: TStrings); var PName : array[0..SQL_NAME_LEN+1] of char; NP,NT : SQLINTEGER; Ptype : SQLSmallInt; Res: SQLRETURN; begin S.Clear; Res:=CheckODBC(SQLProcedureColumns(handle, nil, 0, nil, 0, PChar(ProcName),SQL_NTS,Nil, 0),SErrProcedureNames); if Res=SQL_SUCCESS then begin SQLBindCol(handle, 4, SQL_CHAR, @PName, SQL_NAME_LEN, @NP); SQLBindCol(handle, 5, SQL_SMALLINT, @PType, SizeOf(SQLSmallInt), @NT); While Fetch do begin If TODBCParamType(PType) in ParamTypes then S.Add(PName); end; end; end; { TODBCFieldList } constructor TODBCFieldList.Create(Statement: TODBCStatement); begin FStatement:=Statement; Inherited Create(TODBCField); end; { TODBCField } function TODBCField.GetAsString: String; begin If IsNull then Result:='' else Case FBufType of SQL_Smallint : Result:=IntToStr(PSQLSmallInt(Data)^); SQL_Integer : Result:=IntToStr(PSQLINTEGER(Data)^); SQL_BIT : Result:=IntToStr(PByte(Data)^); SQL_CHAR : Result:=StrPas(Data); SQL_DOUBLE : Result:=FloatToStr(GetAsDouble); SQL_DATE : result:=DateToStr(AsDateTime); SQL_TIME : Result:=TimeToStr(AsDateTime); SQL_TIMESTAMP : result:=datetimeToStr(AsDateTime); SQL_TYPE_DATE : result:=dateToStr(AsDateTime); SQL_TYPE_TIMESTAMP : result:=datetimeToStr(AsDateTime); SQL_TYPE_TIME : Result:=TimeToStr(AsDateTime); else ODBCError(SErrInvalidConversion) end; end; function TODBCField.GetData : Pchar; begin Result:=FBuffer+FBuffOffset+SizeOf(SQLinteger); end; function TODBCField.GetIsNull : boolean; begin Result:=PSQLinteger(FBuffer+FBuffOffset)^=SQL_NULL_DATA; end; Function TODBCField.GetAsInteger : Integer; begin If IsNull then Result:=0 else Case FBufType of SQL_Smallint : Result:=PSQLSmallInt(Data)^; SQL_Integer : Result:=PSQLINTEGER(Data)^; SQL_BIT : Result:=PByte(Data)^; SQL_CHAR : Result:=StrToInt(GetAsString); SQL_DOUBLE : Result:=Round(GetAsDouble); SQL_DATE, SQL_TIME, SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIMESTAMP, SQL_TYPE_TIME : Result:=Round(AsDateTime); else ODBCError(SErrInvalidConversion) end; end; Function TODBCField.GetAsBoolean : Boolean; begin If IsNull then Result:=False else Case FBufType of SQL_Smallint : Result:=PSQLSmallInt(Data)^=0; SQL_Integer : Result:=PSQLINTEGER(Data)^=0; SQL_BIT : Result:=PBYTE(Data)^=0; SQL_CHAR : Result:=(StrToInt(GetAsString)=0); SQL_DOUBLE : Result:=Round(GetAsDouble)=0; SQL_DATE, SQL_TIME, SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIMESTAMP, SQL_TYPE_TIME : Result:=Round(AsDateTime)=0; else ODBCError(SErrInvalidConversion) end; end; Function TODBCField.GetAsDouble : Double; begin If IsNull then Result:=0 else Case FBufType of SQL_Smallint : Result:=PSQLSmallInt(Data)^; SQL_Integer : Result:=PSQLINTEGER(Data)^; SQL_BIT : Result:=PBYTE(Data)^; SQL_CHAR : Result:=StrToFloat(GetAsString); SQL_DOUBLE : Result:=PSQLDOUBLE(GetData)^; SQL_DATE, SQL_TIME, SQL_TIMESTAMP, SQL_TYPE_DATE, SQL_TYPE_TIMESTAMP, SQL_TYPE_TIME : Result:=AsDateTime; else ODBCError(SErrInvalidConversion) end; end; { function DateStructToDateTime( b:PSQL_DATE_STRUCT):TDateTime; function DateTimeToDateStruct( b:TDateTime):SQL_DATE_STRUCT; procedure DateTime2TimeStampStruct( var Value:SQL_TIMESTAMP_STRUCT; b:TDateTime); } Function TODBCField.GetAsDateTime : TDateTime; begin If IsNull then Result:=0 else Case FBufType of SQL_Smallint : Result:=PSQLSmallInt(Data)^; SQL_Integer : Result:=PSQLINTEGER(Data)^; SQL_BIT : Result:=PBYTE(Data)^; SQL_CHAR : Result:=StrToInt(GetAsString); SQL_DOUBLE : Result:=PSQLDOUBLE(GetData)^; SQL_DATE : Result:=DateStructToDateTime(PSQL_DATE_STRUCT(Data)); SQL_TIME : Result:=TimeStructToDateTime(PSQL_TIME_STRUCT(Data)); SQL_TIMESTAMP : Result:=TimeStampStructToDateTime(PSQL_TIMESTAMP_STRUCT(Data)); SQL_TYPE_DATE : Result:=DateStructToDateTime(PSQL_DATE_STRUCT(Data)); SQL_TYPE_TIMESTAMP : Result:=TimeStampStructToDateTime(PSQL_TIMESTAMP_STRUCT(Data)); SQL_TYPE_TIME : Result:=TimeStructToDateTime(PSQL_TIME_STRUCT(Data)); else ODBCError(SErrInvalidConversion) end; end; Finalization If Assigned(DefEnv) then TODBCEnvironment(DefEnv).Free; end.