/*
 * Xailer source code:
 *
 * AdoRecords.prg
 * Clase TAdoRecords()
 *
 * Copyright 2003, 2007 Ignacio Ortiz de Zúñiga
 * Copyright 2003, 2007 Xailer.com
 * All rights reserved
 *
 */

#include "Xailer.ch"
#include "error.ch"
#include "dataset.ch"
#include "ado.ch"

//--------------------------------------------------------------------------

CLASS XAdoRecords FROM TRecords

PUBLIC:
   PROPERTY oDataSource READONLY READ INLINE ::oDataSet:oDataSource AS TDataSource
   PROPERTY oAdo        READONLY READ GetAdo

   METHOD Open()  // --> lSuccess
   METHOD Close() // --> lSuccess
   METHOD AddNew()
   METHOD Edit()     VIRTUAL  // no hace falta, es automático
   METHOD Update()  // --> lSuccess
   METHOD Cancel()  // --> lSuccess

   // Las altas las hacemos aqui porque ADO deja el recordset vacio con AddNew()
   METHOD Save()    // --> lSuccess
   METHOD CanEdit()     INLINE ::oAdo:Supports( adUpdate )  // --> lResult
   METHOD CanBrowse()   INLINE IIf( ::FoAdo == Nil, .T. , ::oAdo:Supports( adMovePrevious ) ) // --> lResult
   METHOD Delete()      // --> lSuccess

   METHOD FieldCount()  // --> nCount
   METHOD FieldInfo( nField, nType )   // --> aInfo
   METHOD FieldGet( n ) // --> uValue
   METHOD FieldPut( n, v ) // --> v

   METHOD Recno()       // --> nId
   METHOD RecCount()    // --> nRecCount
   METHOD Eof()         // --> lResult
   METHOD Bof()         // --> lResult
   METHOD GoTop()       // --> Nil
   METHOD GoBottom()    // --> Nil
   METHOD Goto( Value ) // --> Nil
   METHOD Skip( n )     // --> Nil
   METHOD nSkip( n )    // --> nSkip

   METHOD KeyCount()    // --> nCount
   METHOD KeyNo()       // --> nPosition
   METHOD KeyGoto( n )  // --> Nil

   METHOD Refresh()     // --> Nil
   METHOD RefreshCurrent( nFlags1, nFlags2 ) // --> Nil

   METHOD Locate( cExp, lContinue ) // --> lSuccess
   METHOD Filter( cExp )            // --> cOldFilter
   METHOD Sort( cExp )              // --> lResult

   METHOD Clone()    // --> oDataSet

   METHOD Deleted()  // --> lResult

   METHOD GetRows()  INLINE aTranspose( ::oAdo:GetRows() ) // --> aData
   METHOD GetRow( nRow, lMove )  // --> aRow

RESERVED:
   METHOD GetFilterExp( oDBBrowse )
   METHOD GetSeekExp( oDataField, cSeek )
   METHOD Commit()               VIRTUAL
   METHOD RecLock()              INLINE .T.
   METHOD RecUnLock()            INLINE .T.
   METHOD NewAdoError( cDes )    INLINE ::oDataSource:NewADOError( cDes, ::oDataSet )
	METHOD SetAdo( oAdo )         INLINE ::FoAdo := oAdo

PROTECTED:
   METHOD GetAdo()
   DATA lOnAppend   INIT .F.

ENDCLASS

//--------------------------------------------------------------------------

METHOD Open() CLASS XAdoRecords

   LOCAL oRS, oError
	LOCAL cSelect
   LOCAL nFlags

   IF ! ::oDataSource:CheckConnection()
      RETURN .F.
   ENDIF

   IF ::FoAdo != Nil // Por si proviene de un metodo Clone()
      RETURN .T.
   ENDIF

   IF ::lIsTable
      nFlags := adCmdTable
   ELSE
      nFlags := adOptionUnspecified
   ENDIF

   WITH OBJECT ::oDataset
      IF :nLockType == adLockUnspecified
         :nLockType := adLockOptimistic
      ENDIF
   END

   TRY
      oRS := TOleAuto():New( "ADODB.RecordSet" )
   CATCH oError
      ::oDataSource:NewError( oError:Description, oError:SubCode,, "ADODB:RecordSet" )
   END

   WITH OBJECT ::oDataSet
		cSelect := :BuildSQLSt()
      TRY
         IF :nMaxRecords > 0
            oRS:MaxRecords := :nMaxRecords
         ENDIF
         oRS:CursorLocation := :nCursorLocation
         oRS:Open( cSelect, :oDataSource:oConnection, :nCursorType, :nLockType, nFlags )
      CATCH oError
         ::NewADOError( "ADODB:RecordSet:Open( '" + cSelect + "' )" )
         RETURN .F.
      END
   END WITH

	::FoAdo := oRS

RETURN .T.

//--------------------------------------------------------------------------

METHOD Close() CLASS XAdoRecords

   IF ::FoAdo != Nil
      TRY
         ::FoAdo:Close()
         ::FoAdo := Nil
      CATCH
         ::NewADOError( "ADODB:RecordSet:Close( '" + ::oDataSet:cName + "' )" )
         RETURN .F.
      END
   ENDIF

RETURN .T.

//--------------------------------------------------------------------------

METHOD FieldCount() CLASS XAdoRecords
RETURN ::oAdo:Fields:Count

//--------------------------------------------------------------------------

METHOD FieldGet( nField, lForceRecno ) CLASS XAdoRecords

   LOCAL Value

   IF ::lOnAppend .AND. ( lForceRecno == NIL .OR. !lForceRecno )
      Value := ::aRecord[ nField ]
   ELSE
      WITH OBJECT ::oAdo
         TRY
            IF !( :Bof .OR. :Eof )
               Value := :Fields[ nField - 1 ]:Value
            ENDIF
         CATCH
            ::NewADOError( "ADODB:RecordSet:FieldGet( '" + Ltrim( Str( nField ) ) + "' )" )
         END
      END WITH
   ENDIF

RETURN Value

//--------------------------------------------------------------------------

METHOD FieldPut( nField, Value ) CLASS XAdoRecords

   LOCAL oField

   IF ::lOnAppend
      RETURN ::aRecord[ nField ] := Value
	ENDIF

   oField := ::oDataset:aFields[ nField ]

   WITH OBJECT ::oAdo
      TRY
         IF oField:lEditable .AND. !( :Bof .OR. :Eof )
            WITH OBJECT :Fields[ nField - 1 ]
               IF Value != Nil .OR. lAnd( :Attributes, adFldIsNullable )
                  IF Value == Nil
                     :Value := OLE_NULL
                  ELSEIF :Type == adLongVarBinary .AND. lAnd( :Attributes, adFldLong )
                     :AppendChunk( __OleVariantNew( 8209, @Value ) )  // nOr( VT_ARRAY, VT_UI1 )
                  ELSE
                     Value := :Value := Value
                  ENDIF
               ENDIF
            END WITH
         ENDIF
      CATCH
         ::Cancel()
         ::NewADOError( "ADODB:RecordSet:FieldPut( '" + ::oDataset:aFields[ nField ]:cName + "' ) := " + ;
                        ToString( Value ) + " (" + Valtype( Value ) + ")" )
      END
   END WITH

RETURN Value

//--------------------------------------------------------------------------

METHOD AddNew() CLASS XAdoRecords

   ::lOnAppend := .T.

RETURN NIL

//--------------------------------------------------------------------------

METHOD Update() CLASS XAdoRecords

   LOCAL lSuccess := .T.

   WITH OBJECT ::oAdo
		TRY
			:Update()
		CATCH
         lSuccess := (:Status == adRecOK .OR. :Status == adRecModified )
         IF !lSuccess
            TRY
               :BookMark := :BookMark  // Para forzar el error aportando información
            CATCH
               :CancelUpdate()
               ::NewADOError( "ADODB:RecordSet:Update()" )
            END
         ENDIF
		END
  	END WITH

	::lOnAppend := .F.

RETURN lSuccess

//--------------------------------------------------------------------------

METHOD Cancel() CLASS XAdoRecords

   LOCAL lSuccess := .T.

   WITH OBJECT ::oAdo
      TRY
         IF !( :Bof .OR. :Eof ) .and. :EditMode != adEditNone
            :CancelUpdate()
         ENDIF
      CATCH
         lSuccess := .F.
         ::NewADOError( "ADODB:RecordSet:Cancel()" )
      END
  END WITH

  ::lOnAppend := .F.

RETURN lSuccess

//--------------------------------------------------------------------------
// Solo se utiliza realmente para altas

METHOD Save() CLASS XAdoRecords

   LOCAL oField
	LOCAL v
	LOCAL nFor, nLen
	LOCAL lSuccess

   IF !::lOnAppend
      RETURN .T.
   ENDIF

   WITH OBJECT ::oAdo
      TRY
         :AddNew()
         nLen := :Fields:Count
         FOR nFor := 1 TO nLen
            oField := ::oDataset:aFields[ nFor ]
            v      := ::aRecord[ nFor ]
            IF v != NIL .AND. oField:lModified .AND. oField:lEditable
               WITH OBJECT :Fields[ nFor - 1 ]
                  IF :Type == adLongVarBinary .AND. lAnd( :Attributes, adFldLong )
                     :AppendChunk( __OleVariantNew( 8209, @v ) )  // nOr( VT_ARRAY, VT_UI1 )
                  ELSE
                     :Value := v
                  ENDIF
               END WITH
            ENDIF
         NEXT
         lSuccess := .T.
      CATCH
         lSuccess := .F.
         :CancelUpdate()
         ::NewADOError( "ADODB:RecordSet:Save(On call to TAdo:AddNew())" )
      END
  END WITH

RETURN lSuccess

//--------------------------------------------------------------------------

METHOD Delete() CLASS XAdoRecords

   LOCAL lSuccess := .F.

	IF ::lOnAppend
		RETURN .F.
	ENDIF

   WITH OBJECT ::oAdo
      TRY
         :Delete()
         lSuccess := .T.
         TRY
            :MoveNext()
            IF :Eof
 		        ::nRecno --
					:MoveLast()
            ENDIF
         CATCH
         END
      CATCH
			:CancelUpdate()
         ::NewADOError( "ADODB:RecordSet:Delete()" )
      END
  END WITH

RETURN lSuccess

//--------------------------------------------------------------------------

METHOD Deleted() CLASS XAdoRecords

   LOCAL lDeleted

   WITH OBJECT ::oAdo
      TRY
         lDeleted := ( :EditMode == adEditDelete )
      CATCH
         lDeleted := .F.
      END
  END WITH

RETURN lDeleted

//--------------------------------------------------------------------------

METHOD GetRow( nRow, lMove ) CLASS XAdoRecords

   LOCAL aData
   LOCAL Bookmark, Field

   DEFAULT lMove TO .F.  // por compatibilidad con anterior código

   aData := {}

   WITH OBJECT ::oAdo
      TRY
         IF !( :Bof .OR. :Eof )
            IF nRow != NIL
               Bookmark  := :Bookmark
               ::Goto( nRow )
            ENDIF
            WITH OBJECT ::oDataset
               aData := Array( Len( :aFields ) )
               FOR EACH Field IN :aFields
                  aData[ Field:__EnumIndex() ] := Field:Value
               NEXT
            END WITH
            IF nRow != NIL .AND. lMove
               :Bookmark  := Bookmark
            ENDIF
         ENDIF
      CATCH
         ::NewADOError( "ADODB:RecordSet:GetRow( " + AllTrim( ToString( nRow ) ) +" )" )
      END
   END WITH

RETURN aData

//--------------------------------------------------------------------------

METHOD FieldInfo( nField, nPos ) CLASS XAdoRecords

   LOCAL aInfo
   LOCAL cType
   LOCAL nType, nSize, nDec

   aInfo := Array( XA_FLDINFO_COUNT )

   WITH OBJECT ::oAdo:Fields[ --nField ]
      nType := :Type
      cType := BasicType( nType )
      nSize := :DefinedSize
      nDec  := :NumericScale
      IF nDec == 255
         nDec := 0
      ENDIF
      aInfo[ XA_FLDINFO_NAME ]       := :Name
      aInfo[ XA_FLDINFO_SQLTYPE ]    := nType
      aInfo[ XA_FLDINFO_BASICTYPE ]  := cType
      aInfo[ XA_FLDINFO_SQLLEN ]     := nSize
      aInfo[ XA_FLDINFO_BASICLEN ]   := BasicSize( nSize, cType )
      aInfo[ XA_FLDINFO_SQLDEC ]     := nDec
      aInfo[ XA_FLDINFO_BASICDEC ]   := nDec
      aInfo[ XA_FLDINFO_ALLOWSNULL ] := lAnd( :Attributes, adFldIsNullable )
      aInfo[ XA_FLDINFO_AUTOINC ]    := :Properties("IsAutoIncrement"):Value
      aInfo[ XA_FLDINFO_EDITABLE ]   := !aInfo[ XA_FLDINFO_AUTOINC ]
      aInfo[ XA_FLDINFO_DEFVALUE ]   := iif( aInfo[ XA_FLDINFO_AUTOINC ], NIL, XA_GetDefValue( cType ) )
      aInfo[ XA_FLDINFO_PK ]         := lAnd( :Attributes, adFldKeyColumn  )
      aInfo[ XA_FLDINFO_TABLE ]      := ""
   END WITH

   IF ! Empty( nPos )
      RETURN aInfo[ nPos ]
   ENDIF

RETURN aInfo

//--------------------------------------------------------------------------

METHOD Recno() CLASS XAdoRecords

   LOCAL Bookmark := 0

   WITH OBJECT ::oAdo
      TRY
         IF !( :Bof .OR. :Eof )
            BookMark := :BookMark
         ENDIF
      CATCH
         ::NewADOError( "ADODB:RecordSet:Recno()" )
      END
   END WITH

RETURN BookMark

//--------------------------------------------------------------------------

METHOD RecCount() CLASS XAdoRecords

   LOCAL nCount

   TRY
      nCount := ::oAdo:RecordCount
   CATCH
      nCount := 0
   END

RETURN nCount

//--------------------------------------------------------------------------

METHOD Eof() CLASS XAdoRecords

   LOCAL lEof

   TRY
      lEof := ::oAdo:Eof
   CATCH
      lEof := .T.
   END

RETURN lEof

//--------------------------------------------------------------------------

METHOD Bof() CLASS XAdoRecords

   LOCAL lBof

   TRY
      lBof := ::oAdo:Bof
   CATCH
      lBof := .T.
   END

RETURN lBof

//--------------------------------------------------------------------------

METHOD GoTop() CLASS XAdoRecords

   WITH OBJECT ::oAdo
      TRY
         IF :RecordCount > 0
            :MoveFirst()
         ENDIF
      CATCH
         ::NewADOError( "ADODB:RecordSet:GoTop()" )
      END
   END WITH

RETURN Nil

//--------------------------------------------------------------------------

METHOD GoBottom() CLASS XAdoRecords

   WITH OBJECT ::oAdo
      TRY
         IF :RecordCount > 0
            :MoveLast()
         ENDIF
      CATCH
         ::NewADOError( "ADODB:RecordSet:GoBottom()" )
      END
   END WITH

RETURN Nil

//--------------------------------------------------------------------------

METHOD Goto( Value ) CLASS XAdoRecords

   WITH OBJECT ::oAdo
      TRY
         IF :RecordCount > 0
            :BookMark :=  Round( Value, 2 ) // IOZ: Falla ADO cuando es un integer :-o
         ENDIF
      CATCH
         ::NewADOError( "ADODB:RecordSet:Goto( " + AllTrim( ToString( Value ) ) +" )" )
      END
   END WITH

RETURN Nil

//--------------------------------------------------------------------------

METHOD Skip( n ) CLASS XAdoRecords


   WITH OBJECT ::oAdo
      TRY
         :Move( n )
      CATCH
         ::NewADOError( "ADODB:RecordSet:Skip( " + Ltrim( Str( n ) ) +" )" )
      END
   END WITH

RETURN Nil

//--------------------------------------------------------------------------

METHOD KeyCount() CLASS XAdoRecords

   LOCAL nCount

   TRY
      nCount := ::oAdo:RecordCount
   CATCH
      nCount := 0
      ::NewADOError( "ADODB:RecordSet:KeyCount()" )
   END

RETURN nCount

//--------------------------------------------------------------------------

METHOD KeyNo() CLASS XAdoRecords

   LOCAL nPos

   TRY
      nPos := ::oAdo:AbsolutePosition
   CATCH
      nPos := 0
      ::NewADOError( "ADODB:RecordSet:KeyNo()" )
   END

RETURN nPos

//--------------------------------------------------------------------------

METHOD KeyGoto( n ) CLASS XAdoRecords

   TRY
      ::oAdo:AbsolutePosition := n
   CATCH
      ::NewADOError( "ADODB:RecordSet:KeyGoto( " + Ltrim( Str( n ) ) +" )" )
   END

RETURN Nil

//--------------------------------------------------------------------------

METHOD nSkip( n ) CLASS XAdoRecords

   LOCAL nSkipped := 0

   WITH OBJECT ::oAdo
      TRY
         IF :RecordCount > 0
            IF n > 0
               DO WHILE n > 0
                  :MoveNext()
                  IF ! :Eof
                     n--
                     nSkipped++
                  ELSE
                     :MoveLast()
                     EXIT
                  ENDIF
               ENDDO
            ELSE
               DO WHILE n < 0
                  :MovePrevious()
                  IF ! :Bof
                     n++
                     nSkipped--
                  ELSE
                     :MoveFirst()
                     EXIT
                  ENDIF
               ENDDO
            ENDIF
         ENDIF
      CATCH
         //::NewADOError( "ADODB:RecordSet:nSkip( " + Ltrim( Str( n ) ) +" )" )
      END
   END WITH

RETURN nSkipped

//--------------------------------------------------------------------------

METHOD Refresh()

   TRY
      ::oAdo:Requery()
   CATCH
      ::NewADOError( "ADODB:RecordSet:Refresh()" )
   END

RETURN NIL

//--------------------------------------------------------------------------

METHOD RefreshCurrent(  nFlags1, nFlags2 )

   DEFAULT nFlags1 TO adAffectCurrent
   DEFAULT nFlags2 TO adResyncAllValues

   TRY
      ::oAdo:ReSync( nFlags1, nFlags2 )
   CATCH
      ::NewADOError( "ADODB:RecordSet:RefreshCurrent()" )
   END

RETURN NIL

//--------------------------------------------------------------------------

METHOD Locate( cExp, lContinue )  CLASS XAdoRecords

   LOCAL Bookmark, lSuccess

   DEFAULT lContinue TO .F.

   IF ValType( cExp ) == "C"
      TRY
         WITH OBJECT ::oAdo
            IF !( :Bof .OR. :Eof )
               Bookmark  := :Bookmark
               IF ! lContinue
                  :MoveFirst()
               ENDIF
            ENDIF

            :Find( cExp, IIf( lContinue, 1, 0 ) )

            IF :Eof
               IF Bookmark != NIL
                  :Bookmark := Bookmark
               ENDIF
               lSuccess := .F.
            ELSE
               lSuccess := .T.
            ENDIF
         END WITH
      CATCH
         lSuccess := .F.
      END
   ELSE // Si es un code-block hacemos búsqueda lenta
      TRY
         WITH OBJECT ::oAdo
            IF !( :Bof .OR. :Eof )
               Bookmark  := :Bookmark
               IF ! lContinue
                  :MoveFirst()
               ENDIF
            ENDIF
            DO WHILE !:Eof
               IF Eval( cExp, Self )
                  EXIT
               ENDIF
               :MoveNext()
            ENDDO
            IF :Eof
               IF Bookmark != NIL
                  :Bookmark := Bookmark
               ENDIF
               lSuccess := .F.
            ELSE
               lSuccess := .T.
            ENDIF
         END WITH
      CATCH
         lSuccess := .F.
      END
   ENDIF

RETURN lSuccess

//--------------------------------------------------------------------------

METHOD Filter( cExp ) CLASS XAdoRecords

   LOCAL cOldFilter

   WITH OBJECT ::oAdo
      TRY
         cOldFilter := :Filter
         IF cExp != Nil
            :Filter := cExp
         ENDIF
      CATCH
         ::NewADOError( "ADODB:RecordSet:Filter( " + cExp + " )" )
      END
   END WITH

   IF Valtype( cOldFilter ) != "C"
      cOldFilter := ""
   ENDIF

RETURN cOldFilter

//--------------------------------------------------------------------------

METHOD Sort( cExp ) CLASS XAdoRecords

   LOCAL lRet := .T.

   TRY
      ::oAdo:Sort := cExp
   CATCH
      lRet := .F.
      ::NewADOError( "ADODB:RecordSet:Sort( " + cExp + " )" )
   END

RETURN lRet

//--------------------------------------------------------------------------

METHOD Clone() CLASS XAdoRecords

   LOCAL oDs, oField
   LOCAL cFilter

   IF ::lIsTable
      oDs := TSqlTable():Create()
   ELSE
      oDs := TSqlQuery():Create()
   ENDIF

   WITH OBJECT oDs
      :oDataSource     := ::oDataSource
      :cProcess        := ::oDataSet:cProcess
		:aSQLParams      := ::oDataSet:aSQLParams
      :cName           := ::oDataSet:cName
      :nCursorLocation := ::oDataSet:nCursorLocation
      :nCursorType     := ::oDataSet:nCursorType
      :nLockType       := ::oDataSet:nLockType
      :nMaxRecords     := ::oDataSet:nMaxRecords
      :lDisplayErrors  := ::oDataSet:lDisplayErrors
      :lUpdLinked      := ::oDataSet:lUpdLinked
      :lReadOnly       := ::oDataSet:lReadOnly
      WITH OBJECT :oRecords := TAdoRecords():Create( ::oDataSet, ::lIsTable )
         :oAdo := ::oAdo:Clone()
      END WITH
      :lOpen := ::oDataSet:lOpen
      FOR EACH oField IN ::oDataSet:aUserFields
         :AddCalcField( oField:cName, oField:bGetValue )
      NEXT
      IF ! Empty( cFilter := ::Filter() )
         :SetFilter( cFilter )
      ENDIF
      :Goto( ::Recno() )
   END WITH

RETURN oDs

//--------------------------------------------------------------------------

METHOD GetAdo() CLASS XAdoRecords

   IF ::FoAdo == Nil
      WITH OBJECT ErrorNew()
         :SubSystem   := "XAILER"
         :SubCode     := 0
         :Severity    := ES_ERROR
         :Description := "ADO Invalid object (NIL)"
         :Operation   := ::oDataset:ClassName() + ":('" + ::oDataSet:cName + "')"
         Eval( ErrorBlock(), :__WithObject() )
      END WITH
   ENDIF

RETURN ::FoAdo

//--------------------------------------------------------------------------

METHOD GetFilterExp( oDBBrowse ) CLASS XAdoRecords

   LOCAL oCol
   LOCAL cExp, cVal, cFil
   LOCAL nAt
   LOCAL lComBef, lComAft

   IF ! Empty( oDBBrowse:cOrgFilter )
      cFil := "( " + oDBBrowse:cOrgFilter + ")"
   ELSE
      cFil := ""
   ENDIF

   FOR EACH oCol IN oDBBrowse:aCols
      cVal := oCol:cFilter
      IF ! Empty( cVal )
         cExp := oCol:FilterEval
         WITH OBJECT oCol:oDataField
            DO CASE
               CASE :cType == "C" .OR. :cType == "M"
                  IF Empty( cExp )
                     lComBef := ( Left( cVal, 1 ) == "%" )
                     lComAft := ( Right( cVal, 1 ) == "%" )
                     IF lComBef .AND. !lComAft
                        cVal := Substr( cVal, 2 )
                        cExp :=  :cFullName + " LIKE '*" + StrSql( cVal ) + "'"
                     ELSEIF !lComBef .AND. lComAft
                        cVal := Substr( cVal, 1, Len( cVal ) - 1 )
                        cExp :=  :cFullName + " LIKE '" + StrSql( cVal ) + "*'"
                     ELSE
                        IF lComBef .AND. lComAft
                           cVal := Substr( cVal, 2, Len( cVal ) - 2 )
                        ENDIF
                        cExp :=  :cFullName + " LIKE '*" + StrSql( cVal ) + "*'"
                     ENDIF
                  ENDIF
               CASE :cType == "N"
                  IF Empty( cExp )
                     cExp := :cFullName + IIF( !Left( cVal,1 ) $ "<>", " = " , " " ) + cVal
                  ENDIF
               CASE :cType == "D"
                  IF Left( cVal, 1 ) $ "<>"
                     nAt := 1
                     IF Substr( cVal, 2, 1 ) == "="
                        nAt ++
                     ENDIF
                     cExp := :cFullName + " " + Left( cVal, nAt ) + " '" + DToSql( CTOD( Substr( cVal, nAt + 1 ) ) ) + "'"
                  ELSE
                     cExp := :cFullName + " = '" + DToSql( CTOD( cVal ) ) + "'"
                  ENDIF
               CASE :cType == "L"
                  IF Empty( cExp )
                     cExp := :cFullName + " = " + IIF( Upper( Left( cVal, 1 ) ) $ "YSTO1", "1" , "0" )
                  ENDIF
            END CASE
         END WITH
         cFil += IIF( ! Empty( cFil ), " AND ", "" ) + cExp
      ENDIF
   NEXT

RETURN cFil

//--------------------------------------------------------------------------

METHOD GetSeekExp( oDataField, cSeek ) CLASS XADORecords

   LOCAL cExp, cLen, cIdx

   cSeek := Upper( cSeek )
   cLen  := LTrim( Str( Len( cSeek ) ) )

   WITH OBJECT oDataField
      cIdx := LTrim( Str( :nIndex ) )
      DO CASE
      CASE :cType == "C" .or. :cType == "M"
         cExp := :cFullName + " LIKE '" + StrSQLite( cSeek ) + "%'"
      CASE :cType == "N"
         cExp := &("{|oRecs| Left( lTrim( Str( oRecs:FieldGet(" + cIdx + ") ) ), " +  cLen + " ) == '" + cSeek + "' } " )
      CASE :cType == "D"
         cExp := &("{|oRecs| Left( Dtoc( oRecs:FieldGet(" + cIdx + ") ), " + cLen + " ) == '" + cSeek + "' } " )
      CASE :cType == "L"
         cExp := :cFullName + IIF( Upper( Left( cSeek, 1 ) ) $ "1TYSO", " <> 0", " = 0" )
      END CASE
   END WITH

RETURN cExp

//--------------------------------------------------------------------------
//--------------------------------------------------------------------------

STATIC FUNCTION BasicType( nType )

   LOCAL cType

   SWITCH nType
      CASE adBigInt
      CASE adCurrency
      CASE adDecimal
      CASE adDouble
      CASE adFileTime
      CASE adGUID
      CASE adIDispatch
      CASE adInteger
      CASE adNumeric
      CASE adSingle
      CASE adSmallInt
      CASE adTinyInt
      CASE adUnsignedBigInt
      CASE adUnsignedInt
      CASE adUnsignedSmallInt
      CASE adUnsignedTinyInt
      CASE adVarNumeric
         cType := "N"
         EXIT
      CASE adBoolean
         cType := "L"
         EXIT
      CASE adDate
      CASE adDBDate
      CASE adDBTimeStamp
         cType := "D"
         EXIT
      OTHERWISE
         cType := "C"
   END

RETURN cType

//--------------------------------------------------------------------------

STATIC FUNCTION BasicSize( nSize, cType )

   SWITCH cType
      CASE "L"
         nSize := 1
         EXIT
      CASE "D"
         nSize := 8
         EXIT
   END

RETURN nSize

//--------------------------------------------------------------------------
