/* * Xailer source code: * * OdbcDataSet.prg * Clase TOdbcDataSet() * * Copyright 2003, 2007 Ignacio Ortiz de Zúńiga * Copyright 2003, 2007 Xailer.com * All rights reserved * */ #include "Xailer.ch" #include "Odbc.ch" #include "Language.ch" #include "Dataset.ch" #include "error.ch" //-------------------------------------------------------------------------- CLASS XOdbcDataSet FROM TDbfDataSet PUBLISHED: PROPERTY cSelect WRITE INLINE ( ::FcSelect := ::cName := Value ) EDITOR PE_ExtendedString PROPERTY lDateAsString INIT .T. PROPERTY lAsynFetch INIT .F. PROPERTY lOpen PROPERTY lNoCompare INIT .F. EVENT OnComplete( oSender ) // --> Nil PUBLIC: PROPERTY aIdxFiles INIT {} PROTECTED: PROPERTY cFile INIT "" PROPERTY lNew INIT .T. PROPERTY lReUseDel INIT .F. PROPERTY lShared INIT .F. RESERVED: DATA oTimer AS TTimer DATA aPrimaryFields INIT {} PROPERTY cName INIT "" WRITE INLINE ::FcName := Value DATA nTimerFetch INIT 1 DATA hStmt INIT 0 DATA lCursorChanged INIT .F. PUBLIC: METHOD Open() // --> lSuccess METHOD Close() // --> lSuccess | Nil METHOD GoBottom() // --> Nil METHOD Goto( nRecno ) // --> Nil METHOD Skip( nRecs ) // --> Nil METHOD nSkip( nRecs ) // --> nSkip METHOD Eof() // --> lResult METHOD Complete() INLINE ::Fill( 0 ) // --> lSuccess METHOD IsComplete() // --> lResult METHOD Refresh() // --> Nil METHOD RefreshCurrent() // --> lSuccess METHOD RecordHasChanged() // --> lSuccess METHOD AddNew() // --> lSuccess METHOD Edit() // --> lSucccess METHOD CanEdit() INLINE Len( ::aPrimaryFields ) > 0 // --> lResult METHOD Update() // --> lSuccess METHOD Delete() // --> lSuccess //METHOD FieldPut( n, x ) INLINE ::aFields[ n ]:VarPut( x ) // --> Nil METHOD FieldIsNull( nField ) INLINE ( Substr((::cAlias)->IsNull, nField, 1) == "1" ) // --> lResult METHOD SetPrimaryKeys( aFields ) // --> Nil METHOD GetPrimaryKeys() INLINE ::aPrimaryFields // --> aPrimaryFields METHOD Clone() INLINE ::NewError( "Clone not supported on TOdbcDataSet" ) // --> Nil RESERVED: METHOD AutoFill() METHOD SetDataSource( oDataSource, lCheckType ) PROTECTED: METHOD Execute( cCommand, cOperation, hStmt ) INLINE ::oDataSource:Execute( cCommand, cOperation, hStmt ) METHOD AllocStatement( hStmt) INLINE ::oDataSource:AllocStatement( hStmt ) METHOD DropStatement( hStmt ) INLINE ::oDataSource:DropStatement( hStmt ) METHOD Fetch( hStmt ) INLINE ::oDataSource:Fetch( hStmt ) METHOD Fill( nRecs ) METHOD TableName() METHOD GenAppend() METHOD GenUpdate() METHOD GenWhere() METHOD GenCurRec() METHOD NewError( cText, nError, hStmt ) ENDCLASS //-------------------------------------------------------------------------- METHOD Open( lEvalEvent ) CLASS XOdbcDataSet LOCAL aFields, aStruct LOCAL cField, cTemp LOCAL nFor, nLen LOCAL lCancel, lSuccess DEFAULT lEvalEvent TO .T. If Empty( ::cSelect ) RETURN .F. ENDIF IF ! ::lCreated ::FlOpen := .T. RETURN .T. ENDIF IF !::oDataSource:lConnected .AND. ! ::oDataSource:Connect() RETURN .F. ENDIF iF ::FlOpen ::NewError( ::cSelect + " " + LT( XA_MSG_YA_ESTA_ABIERTO ) ) RETURN .T. ENDIF IF lEvalEvent lCancel := ::OnOpen() IF Valtype( lCancel ) == "L" .AND. ! lCancel RETURN .F. ENDIF ENDIF If ! Empty( ::hStmt ) ::DropStatement( ::hStmt ) ENDIF ::hStmt := ::AllocStatement() If Empty( ::hStmt ) RETURN .F. ENDIF IF ! "SELECT" $ Upper( ::cSelect ) ::cSelect := "SELECT * FROM " + ChkSp( ::cSelect ) ENDIF /* IOZ: 31/08/05 Hay que ponerlo antes del Select, sino a SQL SERVER da un error diciendo que esta ocupado con otro Statement */ ::SetPrimaryKeys() IF ! ::Execute( ::BuildSQLSt(), "TOdbcDataSet:Open() " + ::cSelect, ::hStmt ) RETURN .F. ENDIF IF Len( ::aFields ) == 0 aFields := SQLFieldsInfo( ::hStmt, ::lDateAsString ) IF Len( aFields ) == 0 ::DropStatement( ::hStmt ) RETURN .F. ENDIF aStruct := {} FOR nFor := 1 TO Len( aFields ) cField := ValidField( aFields[ nFor, SQL_INFO_NAME ], aStruct ) WITH OBJECT ::AddField( aFields[ nFor, SQL_INFO_NAME ] ) :cDisplayName := :cName :cDbfName := cField :cType := aFields[ nFor, SQL_INFO_TYPE ] :nSQlType := aFields[ nFor, SQL_INFO_SQLTYPE ] :nLen := aFields[ nFor, SQL_INFO_LEN ] :nSQLLen := aFields[ nFor, SQL_INFO_SQLLEN ] :nDec := aFields[ nFor, SQL_INFO_DEC ] :nSQLDec := aFields[ nFor, SQL_INFO_SQLDEC ] :lAllowsNull := aFields[ nFor, SQL_INFO_NULLABLE ] :cFullname := :cName AAdd( aStruct, { cField, :cType, :nLen, :nDec } ) END WITH ::oRecords:CreateRecord( Len( aStruct ) ) ::aRecordEdit := Array( Len( aStruct ) ) NEXT AAdd( aStruct, { "ISNULL", "C", Len( aFields ), 0 } ) ::cFile := FileUnique( GetEnv( "TEMP" ), "tmp", "XA_DataSet" ) ::oFileName := TFileName():New( ::cFile ) DbCreate( ::cFile, aStruct, RDDSetDefault() ) ENDIF lSuccess := ::Super:Open( .F. ) IF lSuccess IF ::lAsynFetch ::Fill( 100 ) IF ! ::IsComplete() ::nTimerFetch := Max( 20, Int( 1000 / Len( ::aFields ) ) ) WITH OBJECT ::oTimer := TTimer():Create( Application:oMainForm, 10, {|| ::AutoFill() } ) :Enable() END WITH ENDIF ELSE ::Complete() ENDIF ENDIF IF lEvalEvent ::OnPostOpen() ENDIF RETURN lSuccess //-------------------------------------------------------------------------- METHOD Close() CLASS XOdbcDataSet LOCAL aFiles LOCAL lCancel IF ! ::FlOpen RETURN .T. ENDIF lCancel := ::OnClose() IF Valtype( lCancel ) == "L" .AND. ! lCancel RETURN .F. ENDIF IF ::oTimer != Nil ::oTimer:Destroy() ::oTimer := Nil ENDIF IF ::lCursorChanged .AND. ::oDbBrowse != Nil ::oDBBrowse:oCursor := TCursor():Create( IDC_ARROW ) ::lCursorChanged := .F. ENDIF IF ::hStmt != 0 ::DropStatement( ::hStmt ) ::hStmt := 0 ENDIF ::Super:Close( .F. ) IF ::oFileName != Nil aFiles := Directory( ::oFileName:WithExtension( "*" ) ) AEval( aFiles, {|v| FErase( ::oFileName:Path() + "\" + v[ 1 ] ) } ) ENDIF ::aFields := {} // ::aUserFields := {} ::cName := "" RETURN Nil //-------------------------------------------------------------------------- METHOD SetPrimaryKeys( aFields ) CLASS XOdbcDataSet LOCAL aData LOCAL cData, cTable LOCAL hStmt IF aFields != Nil IF Valtype( aFields ) == "C" aFields := { aFields } ENDIF ::aPrimaryFields := aFields ENDIF IF Len( ::aPrimaryFields ) > 0 RETURN ::aPrimaryFields ENDIF aData := {} cTable := ::TableName() IF Empty( cTable ) RETURN {} ENDIF hStmt := ::AllocStatement() IF Empty( hStmt ) RETURN {} ENDIF IF SQL_OK( SqlPrimaryKeys( hStmt, cTable ) ) DO WHILE ::Fetch( hStmt ) cData := Space( 255 ) IF SQL_OK( SqlGetData( hStmt, 4, 255, @cData ) ) AAdd( aData, Upper( Trim( cData ) ) ) ELSE EXIT ENDIF ENDDO ::aPrimaryFields := aData ELSE ::NewError( "Primary Keys retrieve error", -1, hStmt ) ENDIF ::DropStatement( hStmt ) RETURN aData //-------------------------------------------------------------------------- METHOD Fill( nRecs ) CLASS XOdbcDataSet LOCAL Value LOCAL aFields LOCAL cIsNull, cAlias LOCAL nFor, nLen, nFlen, nRecno, nRet, nStmt LOCAL lAll, lDateAsStr IF ::hStmt == 0 RETURN .F. ENDIF DEFAULT nRecs TO 1 IF nRecs == 0 .AND. ::oTimer != Nil ::oTimer:Destroy() ::oTimer := Nil ENDIF aFields := ::aFields cAlias := ::cAlias nRecno := ( cAlias )->( Recno() ) nLen := Len( aFields ) nStmt := ::hStmt lAll := ( nRecs == 0 ) lDateAsStr := ::lDateAsString cIsNull := Space( nLen ) ::hStmt := 0 // para evitar que sea reentrante por si acaso DO WHILE ( lAll .OR. nRecs > 0 ) .AND. nStmt != 0 ProcessMessages() IF ( nRet := SqlFetch( nStmt ) ) == SQL_SUCCESS ( cAlias )->( dbAppend() ) FOR nFor := 1 TO nLen WITH OBJECT aFields[ nFor ] nFlen := :nSqlLen Value := SqlGetValue( nStmt, nFor, :nSqlType, @nFlen , :nSqlDec, lDateAsStr ) TRY ( cAlias )->( Fieldput( nFor, Value ) ) CATCH LogDebug( "SqlType: ", LTrim( Str( :nSqlType ) ), ; "nLen: ", LTrim( Str( nFLen ) ), ; "Dec: ", LTrim( Str( :nSqlDec ) ), ; "Value: ", Value ) END cIsNull := Left( cIsNull, nFor - 1 ) + IIf( nFLen < 0, "1", "0") + Substr( cIsNull, nFor + 1 ) END WITH NEXT TRY ( cAlias )->( Fieldput( nFor, cIsNull ) ) CATCH LogDebug( "IsNull: ", cIsNull ) END ELSE ::DropStatement( nStmt ) nStmt := 0 IF ::oDbBrowse != Nil ( ::cAlias )->( DbGoto( nRecno ) ) ::oDBBrowse:Refresh() IF ::lCursorChanged ::oDBBrowse:oCursor := TCursor():Create( IDC_ARROW ) ::lCursorChanged := .F. ENDIF ENDIF ::OnComplete() EXIT ENDIF nRecs -- ENDDO IF lAll .OR. nRecno == 0 ( ::cAlias )->( DbGoTop() ) ELSE ( ::cAlias )->( DbGoto( nRecno ) ) ENDIF //::UpdateControls() ::hStmt := nStmt RETURN ( nRecs == 0 ) //-------------------------------------------------------------------------- METHOD AutoFill() CLASS XOdbcDataSet ::oTimer:Disable() IF ::oDBBrowse != Nil .AND. !::lCursorChanged ::oDBBrowse:oCursor := TCursor():Create( IDC_APPSTARTING ) ::lCursorChanged := .T. ENDIF ::Fill( ::nTimerFetch ) IF ::oTimer != Nil IF ::IsComplete() ::oTimer:Destroy() ::oTimer := Nil ELSE IF ::oDBBrowse != Nil ::oDBBrowse:KeyCount() ENDIF ::oTimer:Enable() ENDIF ENDIF RETURN Nil //-------------------------------------------------------------------------- METHOD GoBottom() CLASS XOdbcDataSet IF ::hStmt != 0 ::Fill( 0 ) ENDIF RETURN ::Super:GoBottom() //-------------------------------------------------------------------------- METHOD Goto( nRecno, lUpd ) CLASS XOdbcDataSet LOCAL nTotal := ( ::cAlias )->( RecCount() ) IF nRecno > nTotal .AND. ::hStmt != 0 ::Fill( nRecno - nTotal ) ENDIF RETURN ::Super:Goto( nRecno, lUpd ) //-------------------------------------------------------------------------- METHOD Skip( nRecs ) CLASS XOdbcDataSet LOCAL nTotal, nRecno DEFAULT nRecs TO 1 IF ::hStmt != 0 .AND. nRecs > 0 IF ( ::cAlias )->( IndexOrd() ) == 0 nTotal := ( ::cAlias )->( RecCount() ) nRecno := ( ::cAlias )->( Recno() ) IF ( nRecno + nRecs ) > nTotal ::Fill( nRecno - nTotal + nRecs ) ENDIF ELSE ::Fill( 0 ) ENDIF ENDIF RETURN ::Super:Skip( nRecs ) //-------------------------------------------------------------------------- METHOD nSkip( nRecs ) CLASS XOdbcDataSet LOCAL nTotal, nRecno DEFAULT nRecs TO 1 IF ::hStmt != 0 .AND. nRecs > 0 IF ( ::cAlias )->( IndexOrd() ) == 0 nTotal := ( ::cAlias )->( RecCount() ) nRecno := ( ::cAlias )->( Recno() ) IF ( nRecno + nRecs ) > nTotal ::Fill( nRecno - nTotal + nRecs ) ENDIF ELSE ::Fill( 0 ) ENDIF ENDIF RETURN ::Super:nSkip( nRecs ) //-------------------------------------------------------------------------- METHOD Refresh( ) CLASS XOdbcDataSet LOCAL nRecno := ( ::cAlias)->( Recno() ) LOCAL nTotal := ( ::cAlias)->( RecCount() ) LOCAL lComplete := ( ::hStmt == 0 ) IF ::hStmt != 0 ::DropStatement( ::hStmt ) ENDIF ::hStmt := ::AllocStatement() IF ::Execute( ::BuildSQLSt(), "TOdbcDataSet:Refresh()", ::hStmt ) ( ::cAlias )->( __DbZap() ) IF lComplete ::Complete() ELSE ::Fill( nTotal ) ENDIF ::Goto( nRecno ) ENDIF RETURN Nil //-------------------------------------------------------------------------- METHOD RefreshCurrent() CLASS XOdbcDataSet LOCAL Value LOCAL hStmt LOCAL cIsNull LOCAL nLen, nFlen, nFor LOCAL lDateAsStr IF Len( ::aPrimaryFields ) == 0 ::NewError( "No primary key, RefreshCurrent not supported" ) RETURN .F. ENDIF hStmt := ::AllocStatement() nLen := Len( ::aFields ) lDateAsStr := ::lDateAsString IF ! ::Execute( ::GenCurRec(), "TOdbcDataSet:RefreshCurrent()", hStmt ) ::DropStatement( hStmt ) RETURN .F. ENDIF IF ::Fetch( hStmt ) cIsNull := "" FOR nFor := 1 TO nLen WITH OBJECT ::aFields[ nFor ] nFlen := :nSqlLen Value := SqlGetValue( hStmt, nFor, :nSqlType, @nFlen , :nSqlDec, lDateAsStr ) ( ::cAlias )->( Fieldput( nFor, Value ) ) cIsNull += IIf( nFLen < 0, "1", "0") END WITH NEXT ( ::cAlias )->( Fieldput( nFor, cIsNull ) ) ELSE (::cAlias)->( DbDelete() ) (::cAlias)->( __dbPack() ) ENDIF ::DropStatement( hStmt ) RETURN .T. //-------------------------------------------------------------------------- METHOD RecordHasChanged( aChanged ) CLASS XOdbcDataSet LOCAL Value, xOrg LOCAL hStmt LOCAL nLen, nFlen, nFor LOCAL lDateAsStr IF ::lNoCompare RETURN .F. ENDIF aChanged := {} IF Len( ::aPrimaryFields ) == 0 ::NewError( "No primary key, 'RecordHasChanged' not supported" ) RETURN .T. ENDIF hStmt := ::AllocStatement() IF ! ::Execute( ::GenCurRec(), "TOdbcDataSet:RecordHasChanged()", hStmt ) ::DropStatement( hStmt ) RETURN .T. ENDIF IF ::Fetch( hStmt ) // No ha sido borrado nLen := Len( ::aFields ) lDateAsStr := ::lDateAsString FOR nFor := 1 TO nLen WITH OBJECT ::aFields[ nFor ] xOrg := ::oRecord:InitValue( nFor ) IF Valtype( xOrg ) == "C" xOrg := Trim( xOrg ) ENDIF nFlen := :nSqlLen Value := SqlGetValue( hStmt, nFor, :nSqlType, @nFlen , :nSqlDec, lDateAsStr ) // IOZ: Problema de precisión con los doubles IF Valtype( Value ) == "N" .and. Valtype( xOrg ) == "N" .and. Abs( Value - xOrg ) < 0.01 Value := xOrg ENDIF IF Value != xOrg .OR. ::FieldIsNull( nFor ) != ( nFlen < 0 ) AAdd( aChanged, nFor ) ENDIF END WITH NEXT ELSE aChanged := Nil ENDIF ::DropStatement( hStmt ) RETURN ( aChanged == Nil .OR. Len( aChanged ) > 0 ) //-------------------------------------------------------------------------- METHOD AddNew() CLASS XOdbcDataSet IF Len( ::aPrimaryFields ) == 0 ::NewError( "No primary key, 'AddNew' not supported" ) RETURN .F. ENDIF RETURN ::Super:AddNew() //-------------------------------------------------------------------------- METHOD Edit() CLASS XOdbcDataSet IF Len( ::aPrimaryFields ) == 0 ::NewError( "No primary key, 'Edit' not supported" ) RETURN .F. ENDIF RETURN ::Super:Edit() //-------------------------------------------------------------------------- METHOD Update( lEvents ) CLASS XOdbcDataSet LOCAL aChanged LOCAL cSql, cFields, cUpdate, cTmp LOCAL nFor, nRecno LOCAL lCancel, lRet, lAppend DEFAULT lEvents TO .T. IF ! ::lOnEdit() RETURN .F. ENDIF nRecno := ::Recno() lAppend := ::lOnAppend() IF ::nEditRecord != ::Recno() .AND. !::lOnAppend() ::NewError( LT( XA_MSG_SE_HA_MODIFICADO_LA_LINEA_EN_CURSO ) ) ::Cancel() RETURN .F. ENDIF ::OnUpdate() AEval( ::aLinkedControls, {|v| v:Lock() } ) //AEval( ::aLinkedControls, {|v| v:UpdateData() } ) IF ::lOnAppend() lCancel := ::OnPreAppend() IF Valtype( lCancel ) == "L" .AND. ! lCancel ::nEditRecord := 0 ::DelTempControls() RETURN .F. ENDIF cTmp := ::GenAppend() IF ! Empty( cTmp ) cSql := "INSERT INTO " + ::TableName() + cTmp ENDIF ELSE cUpdate := ::GenUpdate() IF Empty( cUpdate ) ::nEditRecord := 0 ::DelTempControls() RETURN .F. ENDIF lCancel := ::OnPreSave() IF Valtype( lCancel ) == "L" .AND. ! lCancel ::nEditRecord := 0 ::DelTempControls() RETURN .F. ENDIF cTmp := ::GenWhere() IF ! Empty( cTmp ) cSql := "UPDATE " + ::TableName() + " SET " + cUpdate + " WHERE " + cTmp IF ::RecordHasChanged( @aChanged ) IF aChanged == Nil ::NewError( "Record deleted after 'Edit' operation, 'Update' aborted" ) ELSE cFields := "" AEval( aChanged, {|v,e| cFields += Ltrim( Str( v ) ) + IIf( e < Len( aChanged ),", " ,"" ) } ) ::NewError( "The fields:" + cFields + " have changed after last 'Edit' operation, 'Update' aborted" ) ENDIF ::nEditRecord := 0 ::DelTempControls() RETURN .F. ENDIF ENDIF ENDIF IF Empty( cSql ) ::nEditRecord := 0 ::DelTempControls() RETURN .F. ENDIF lRet := ::Execute( cSql, "TOdbcDataSet:Update()" ) IF lRet IF ::lOnAppend() ( ::cAlias )->( dbAppend() ) ENDIF FOR nFor := 1 TO Len( ::aFields ) WITH OBJECT ::aFields[ nFor ] ( ::cAlias )->( FieldPut( nFor, CastTo( :Value(), ::aFields[ nFor ]:cType ) ) ) END WITH NEXT IF ::lOnAppend() If ::EventAssigned( "OnPostAppend" ) lRet := ::OnPostAppend() ENDIF ELSE IF ::EventAssigned( "OnPostSave" ) lRet := ::OnPostSave() ENDIF ENDIF ::RefreshCurrent() ENDIF ::nEditRecord := 0 IF lRet .AND. lEvents IF lAppend ::UpdateControls( XA_DSNAV_ADDNEW, ::Recno(), nRecno ) ELSE ::UpdateControls( XA_DSNAV_EDIT, nRecno ) ENDIF ENDIF ::DelTempControls() RETURN lRet //-------------------------------------------------------------------------- METHOD Delete() CLASS XOdbcDataSet LOCAL cSql LOCAL nRecno, nJump LOCAL lCancel, lBof IF ::Recno() == 0 ::NewError( "Empty table, 'Delete' not allowed" ) RETURN .F. ENDIF IF Len( ::aPrimaryFields ) == 0 ::NewError( "No primary key, 'Delete' not supported" ) RETURN .F. ENDIF lCancel := ::OnPreDelete() If Valtype( lCancel ) == "L" .AND. ! lCancel RETURN .F. ENDIF cSql := "DELETE FROM " + ::TableName() + " WHERE " + ::GenWhere() IF ::Execute( cSql, "TOdbcDataSet:Delete()" ) nRecno := ::Recno() ( ::cAlias )->( DbSkip( -1 ) ) lBof := ( ::cAlias )->( Bof() ) nJump := ::Recno() ( ::cAlias )->( DbGoto( nRecno ) ) ::ChangePos( XA_DSNAV_DELETE ) ( ::cAlias )->( DbDelete() ) ( ::cAlias )->( __dbPack() ) IF ! lBof ::Goto( nJump ) ELSE ::GoTop() ENDIF ::UpdateControls( XA_DSNAV_DELETE ) IF ::EventAssigned( "OnPostDelete" ) RETURN ::OnPostDelete() ELSE RETURN .T. ENDIF ENDIF RETURN .F. //-------------------------------------------------------------------------- METHOD TableName() CLASS XOdbcDataSet LOCAL cName LOCAL nAt cName := Upper( ::cSelect ) nAt := At( "FROM ", Upper( ::cSelect ) ) IF nAt == 0 ::NewError( "Table name not found on 'SELECT' command" ) RETURN "" ENDIF cName := Substr( ::cSelect, nAt + 5 ) cName := hb_TokenGet( cName, 1, " " ) IF "," $ cName // más de una tabla cName := hb_TokenGet( cName, 1, "," ) ENDIF IF Empty( cName ) ::NewError( "Table name not found on 'SELECT' command" ) ENDIF RETURN ChkSp( cName ) //-------------------------------------------------------------------------- METHOD GenAppend() CLASS XOdbcDataSet LOCAL Value LOCAL cString, cValue, cType LOCAL nFor, nLen cString := " (" nLen := len( ::aFields ) FOR nFor := 1 TO nLen IF ! ::aFields[ nFor ]:lModified LOOP ENDIF cString += ::aFields[ nFor ]:cName + " , " NEXT cString := Substr( cString, 1, len( cString ) - 3 ) + ") VALUES (" FOR nFor := 1 TO nLen WITH OBJECT ::aFields[ nFor ] Value := :Value() cType := Valtype( Value ) IF ! :lModified LOOP ENDIF DO CASE CASE :cType = "C" .OR. :cType = "M" IF cType == "D" cValue := GetDateSql( Value ) ELSE cValue := "'" + Trim( Value ) + "'" ENDIF CASE :cType = "N" IF cType == "N" cValue := LTrim( Str( Value, :nLen, :nDec ) ) ELSEIF cType == "L" cValue := IIf( Value , "1", "0" ) ELSE cValue = "'" + ToString( Value, "" ) + "'" ENDIF CASE :cType = "L" cValue := IIf( Value, "1", "0" ) CASE :cType = "D" cValue := GetDateSql( Value ) OTHERWISE cValue = "'" + ToString( Value, "" ) + "'" ENDCASE cString += cValue + " , " END WITH NEXT IF ! Empty( cString ) cString := Substr( cString, 1, Len( cString ) - 3 ) + ")" ENDIF RETURN cString //-------------------------------------------------------------------------- METHOD GenUpdate() CLASS XOdbcDataSet LOCAL Value LOCAL cString, cValue, cType LOCAL nFor, nLen cString := "" nLen := Len( ::aFields ) FOR nFor := 1 TO nLen IF ! ::aFields[ nFor ]:lModified LOOP ENDIF WITH OBJECT ::aFields[ nFor ] Value := :Value() cType := Valtype( Value ) DO CASE CASE :cType = "C" .OR. :cType = "M" IF :nSqlType = SQL_LONGVARBINARY cValue := "x'" + StringToHex( Trim( Value ) ) + "'" ELSEIF cType == "D" cValue := GetDateSql( Value ) ELSE cValue := "'" + Trim( Value ) + "'" ENDIF CASE :cType = "N" IF cType == "N" cValue := LTrim( Str( Value, :nLen, :nDec ) ) ELSEIF cType == "L" cValue := IIf( Value , "1", "0" ) ELSE cValue = "'" + ToString( Value, "" ) + "'" ENDIF CASE :cType = "L" cValue := IIf( Value, "1", "0" ) CASE :cType = "D" cValue := GetDateSql( Value ) OTHERWISE cValue = "'" + ToString( Value, "" ) + "'" ENDCASE cString += :cName + " = " + cValue + " , " END WITH NEXT IF ! Empty( cString ) cString := Left( cString, Len( cString ) - 3 ) ENDIF RETURN cString //-------------------------------------------------------------------------- METHOD GenWhere() CLASS XOdbcDataSet LOCAL cString, cValue LOCAL nFor, nField, nLen cString := "" nLen := Len( ::aPrimaryFields ) FOR nFor := 1 TO nLen nField := AScan( ::aFields, {|v| Upper( v:cName ) == Upper( ::aPrimaryFields[ nFor ] ) } ) IF nField == 0 ::NewError("The primary key field [" + ::aPrimaryFields[ nFor ] + "] is not present on the 'SELECT' sentence") RETURN "" ENDIF WITH OBJECT ::aFields[ nField ] DO CASE CASE :cType = "C" .OR. :cType = "M" cValue := "'" + Trim( :Value ) + "'" CASE :cType = "N" cValue := LTrim( Str( :Value, :nLen, :nDec ) ) CASE :cType = "L" cValue := IIf( :Value, "1", "0" ) CASE :cType = "D" cValue := GetDateSql( :Value ) OTHERWISE cValue = "'" + ToString( :Value, "" ) + "'" ENDCASE cString += :cName + " = " + cValue + " AND " END WITH NEXT IF ! Empty( cString ) cString := Left( cString, Len( cString ) - 5 ) ENDIF RETURN cString //-------------------------------------------------------------------------- METHOD GenCurRec() CLASS XOdbcDataSet LOCAL cString, cSelect, cMaySel LOCAL nWhere, nTemp, nLen, nFor cSelect := ::BuildSQLSt() cMaySel := Upper( cSelect ) nWhere := Len( cSelect ) + 1 nLen := Len( ::aFields ) nTemp := At( "GROUP BY ", cMaySel ) IF nTemp > 0 nWhere := Min( nWhere, nTemp - 2 ) ENDIF nTemp := At( "HAVING ", cMaySel ) IF nTemp > 0 nWhere := Min( nWhere, nTemp - 2 ) ENDIF nTemp := At( "ORDER BY ", cMaySel ) IF nTemp > 0 nWhere := Min( nWhere, nTemp - 2 ) ENDIF nTemp := At( "WHERE ", cMaySel ) IF nTemp > 0 cString := Left( cSelect, nTemp + 5 ) + "(" + ::GenWhere() + ; ") AND " + Substr( cSelect, nTemp + 6 ) ELSE cString := Left( cSelect, nWhere ) + " WHERE " + ::GenWhere() + ; " " + Substr( cSelect, nWhere + 1 ) ENDIF RETURN cString //-------------------------------------------------------------------------- METHOD NewError( cText, nError, hStmt ) CLASS XOdbcDataSet IF ::oDataSource != Nil ::oDataSource:NewError( @cText, @nError, Self, hStmt ) ENDIF ::cLastError := cText ::nLastError := IIf( nError != Nil, nError, 0 ) RETURN Nil //-------------------------------------------------------------------------- METHOD SetDataSource( oDataSource, lCheckType ) CLASS XOdbcDataSet LOCAL oError DEFAULT lCheckType TO .T. IF oDataSource != Nil .AND. lCheckType .AND. ! oDataSource:IsKindOf( "TOdbcDataSource" ) oError := ErrorNew() oError:Subsystem := "XAILER:SetDataSource" oError:Severity := ES_ERROR oError:Description := "Incorrect DataSource type, should be KindOf('TOdbcDataSource') " oError:Operation := "" Eval( ErrorBlock(), oError ) ENDIF RETURN ::Super:SetDataSource( oDataSource, .F. ) //-------------------------------------------------------------------------- METHOD Eof() CLASS XOdbcDataSet RETURN ::Super:Eof() .AND. ::hStmt == 0 //-------------------------------------------------------------------------- METHOD IsComplete() CLASS XOdbcDataSet RETURN ::hStmt == 0 //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- STATIC FUNCTION ChkSp( cText ) IF " "$ AllTrim( cText ) cText := "[" + cText + "]" ENDIF RETURN cText //-------------------------------------------------------------------------- STATIC FUNCTION GetDateSQL( dDate ) LOCAL aMonth LOCAL cDate IF Empty( dDate ) RETURN "NULL" ENDIF cDate := "'" + StrZero( Year( dDate ), 4 ) + "-" + ; StrZero( Month( dDate ), 2 ) + "-" + ; StrZero( Day( dDate ), 2 ) + " 00:00:00'" RETURN cDate //-------------------------------------------------------------------------- STATIC FUNCTION CastTo( Value, cType ) LOCAL cValType := Valtype( Value ) SWITCH cType CASE "C" IF cValType != "C" Value := ToString( Value, "" ) ENDIF EXIT CASE "N" IF cValType == "L" Value := IIf( Value, 1, 0 ) ENDIF EXIT END RETURN Value //-------------------------------------------------------------------------- STATIC FUNCTION ValidField( cField, aStruct ) LOCAL nCounter := 0 cField := Upper( Left( StrTran( cField, " ", "" ), 10 ) ) DO WHILE AScan( aStruct, {|v| v[ 1 ] == cField } ) > 0 cField := Left( cField, 9 ) + Str( nCounter++, 1 ) ENDDO RETURN cField //--------------------------------------------------------------------------