* harbour/include/hbextern.ch
! added missing declarations:
DBRELATION, DBRSELECT, MENUMODAL, _PUSHBUTT_, _LISTBOX_
* harbour/contrib/mysql/tmysql.prg
! added conversion from tiny int MySQL type into numeric DBF. (an error before).
Supplied by Mitja Podgornik
1801 lines
52 KiB
Plaintext
1801 lines
52 KiB
Plaintext
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* Harbour Project source code:
|
|
* MySQL DBMS classes.
|
|
* These classes try to emulate clipper dbXXXX functions on a SQL query
|
|
*
|
|
* Copyright 2000 Maurilio Longo <maurilio.longo@libero.it>
|
|
* www - http://www.harbour-project.org
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this software; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
|
|
*
|
|
* As a special exception, the Harbour Project gives permission for
|
|
* additional uses of the text contained in its release of Harbour.
|
|
*
|
|
* The exception is that, if you link the Harbour libraries with other
|
|
* files to produce an executable, this does not by itself cause the
|
|
* resulting executable to be covered by the GNU General Public License.
|
|
* Your use of that executable is in no way restricted on account of
|
|
* linking the Harbour library code into it.
|
|
*
|
|
* This exception does not however invalidate any other reasons why
|
|
* the executable file might be covered by the GNU General Public License.
|
|
*
|
|
* This exception applies only to the code released by the Harbour
|
|
* Project under the name Harbour. If you copy code from other
|
|
* Harbour Project or Free Software Foundation releases into a copy of
|
|
* Harbour, as the General Public License permits, the exception does
|
|
* not apply to the code that you add in this way. To avoid misleading
|
|
* anyone as to the status of such modified files, you must delete
|
|
* this exception notice from them.
|
|
*
|
|
* If you write modifications of your own for Harbour, it is your choice
|
|
* whether to permit this exception to apply to your modifications.
|
|
* If you do not wish that, delete this exception notice.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
2002-01-28 21:30 UTC+0100 Patrick Mast <email@patrickmast.com>
|
|
* contrib/mysql/tmysql
|
|
+ Added DateTime field
|
|
* Added more info on Alert message for Unknown type
|
|
* Modified ClipValue2SQL() to process empty strings
|
|
*/
|
|
|
|
#include "hbclass.ch"
|
|
#include "common.ch"
|
|
#include "dbstruct.ch"
|
|
#include "mysql.ch"
|
|
|
|
|
|
|
|
// Every single row of an answer
|
|
CLASS TMySQLRow
|
|
|
|
DATA aRow // a single row of answer
|
|
DATA aDirty // array of booleans set to .T. if corresponding field of aRow has been changed
|
|
DATA aOldValue // If aDirty[n] is .T. aOldValue[n] keeps a copy of changed value if aRow[n] is part of a primary key
|
|
//DAVID:
|
|
DATA aOriValue // Original values ( same as TMySQLtable:aOldValue )
|
|
|
|
DATA aFieldStruct // type of each field
|
|
DATA cTable // Name of table containing this row, empty if TMySQLQuery returned this row
|
|
|
|
METHOD New(aRow, aFStruct, cTableName) // Create a new Row object
|
|
|
|
METHOD FieldGet(cnField) // Same as clipper ones, but FieldGet() and FieldPut() accept a string as
|
|
METHOD FieldPut(cnField, Value) // field identifier, not only a number
|
|
METHOD FieldName(nNum)
|
|
METHOD FieldPos(cFieldName)
|
|
|
|
METHOD FieldLen(nNum) // Length of field N
|
|
METHOD FieldDec(nNum) // How many decimals in field N
|
|
METHOD FieldType(nNum) // Clipper type of field N
|
|
|
|
METHOD MakePrimaryKeyWhere() // returns a WHERE x=y statement which uses primary key (if available)
|
|
|
|
ENDCLASS
|
|
|
|
|
|
METHOD New(aRow, aFStruct, cTableName) CLASS TMySQLRow
|
|
|
|
default cTableName to ""
|
|
default aFStruct to {}
|
|
|
|
::aRow := aRow
|
|
//DAVID:
|
|
::aOriValue := ACLONE( aRow ) // Original values ( same as TMySQLtable:aOldValue )
|
|
|
|
::aFieldStruct := aFStruct
|
|
::cTable := cTableName
|
|
|
|
::aDirty := Array(Len(::aRow))
|
|
::aOldValue := Array(Len(::aRow))
|
|
|
|
AFill(::aDirty, .F.)
|
|
|
|
return Self
|
|
|
|
|
|
METHOD FieldGet(cnField) CLASS TMySQLRow
|
|
|
|
local nNum
|
|
|
|
if ValType(cnField) == "C"
|
|
nNum := ::FieldPos(cnField)
|
|
else
|
|
nNum := cnField
|
|
endif
|
|
|
|
if nNum > 0 .AND. nNum <= Len(::aRow)
|
|
|
|
// Char fields are padded with spaces since a real .dbf field would be
|
|
if ::FieldType(nNum) == "C"
|
|
return PadR(::aRow[nNum], ::aFieldStruct[nNum][MYSQL_FS_LENGTH])
|
|
else
|
|
return ::aRow[nNum]
|
|
endif
|
|
|
|
endif
|
|
|
|
return nil
|
|
|
|
|
|
METHOD FieldPut(cnField, Value) CLASS TMySQLRow
|
|
|
|
local nNum
|
|
|
|
if ValType(cnField) == "C"
|
|
nNum := ::FieldPos(cnField)
|
|
else
|
|
nNum := cnField
|
|
endif
|
|
|
|
if nNum > 0 .AND. nNum <= Len(::aRow)
|
|
|
|
//DAVID: if Valtype(Value) == Valtype(::aRow[nNum]) .OR. Empty(::aRow[nNum])
|
|
if Valtype(Value) == Valtype(::aRow[nNum]) .OR. ::aRow[nNum]==NIL
|
|
|
|
// if it is a char field remove trailing spaces
|
|
if ValType(Value) == "C"
|
|
Value := RTrim(Value)
|
|
endif
|
|
|
|
// Save starting value for this field
|
|
if !::aDirty[nNum]
|
|
::aOldValue[nNum] := ::aRow[nNum]
|
|
::aDirty[nNum] := .T.
|
|
endif
|
|
|
|
::aRow[nNum] := Value
|
|
|
|
return Value
|
|
endif
|
|
endif
|
|
|
|
return nil
|
|
|
|
|
|
// Given a field name returns it's position
|
|
METHOD FieldPos(cFieldName) CLASS TMySQLRow
|
|
|
|
local cUpperName, nPos := 0
|
|
|
|
cUpperName := Upper(cFieldName)
|
|
|
|
//DAVID: nPos := AScan(::aFieldStruct, {|aItem| iif(Upper(aItem[MYSQL_FS_NAME]) == cUpperName, .T., .F.)})
|
|
nPos := AScan(::aFieldStruct, {|aItem| (Upper(aItem[MYSQL_FS_NAME]) == cUpperName)})
|
|
|
|
/*while ++nPos <= Len(::aFieldStruct)
|
|
if Upper(::aFieldStruct[nPos][MYSQL_FS_NAME]) == cUpperName
|
|
exit
|
|
endif
|
|
enddo
|
|
|
|
// I haven't found field name
|
|
if nPos > Len(::aFieldStruct)
|
|
nPos := 0
|
|
endif*/
|
|
|
|
return nPos
|
|
|
|
|
|
// Returns name of field N
|
|
METHOD FieldName(nNum) CLASS TMySQLRow
|
|
|
|
if nNum >=1 .AND. nNum <= Len(::aFieldStruct)
|
|
return ::aFieldStruct[nNum][MYSQL_FS_NAME]
|
|
endif
|
|
|
|
return ""
|
|
|
|
|
|
METHOD FieldLen(nNum) CLASS TMySQLRow
|
|
|
|
if nNum >=1 .AND. nNum <= Len(::aFieldStruct)
|
|
return ::aFieldStruct[nNum][MYSQL_FS_LENGTH]
|
|
endif
|
|
|
|
//DAVID: return ""
|
|
return 0
|
|
|
|
|
|
METHOD FieldDec(nNum) CLASS TMySQLRow
|
|
|
|
if nNum >=1 .AND. nNum <= Len(::aFieldStruct)
|
|
return ::aFieldStruct[nNum][MYSQL_FS_DECIMALS]
|
|
endif
|
|
|
|
//DAVID: return ""
|
|
return 0
|
|
|
|
|
|
METHOD FieldType(nNum) CLASS TMySQLRow
|
|
|
|
local cType := "U"
|
|
|
|
if nNum >=1 .AND. nNum <= Len(::aFieldStruct)
|
|
do case
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_TINY_TYPE
|
|
cType := "L"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_SHORT_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_LONG_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_LONGLONG_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_FLOAT_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_DOUBLE_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_DECIMAL_TYPE
|
|
cType := "N"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_DATE_TYPE
|
|
cType := "D"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_BLOB_TYPE
|
|
cType := "M"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_VAR_STRING_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_STRING_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_DATETIME_TYPE
|
|
cType := "C"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_INT24_TYPE
|
|
cType := "N"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_MEDIUM_BLOB_TYPE
|
|
cType := "M"
|
|
|
|
otherwise
|
|
cType := "U"
|
|
|
|
endcase
|
|
endif
|
|
|
|
return cType
|
|
|
|
|
|
// returns a WHERE x=y statement which uses primary key (if available)
|
|
METHOD MakePrimaryKeyWhere() CLASS TMySQLRow
|
|
|
|
local ni, cWhere := " WHERE "
|
|
|
|
for nI := 1 to Len(::aFieldStruct)
|
|
|
|
// search for fields part of a primary key
|
|
if (sqlAND(::aFieldStruct[nI][MYSQL_FS_FLAGS], PRI_KEY_FLAG) == PRI_KEY_FLAG) .OR.;
|
|
(sqlAND(::aFieldStruct[nI][MYSQL_FS_FLAGS], MULTIPLE_KEY_FLAG) == MULTIPLE_KEY_FLAG)
|
|
|
|
cWhere += ::aFieldStruct[nI][MYSQL_FS_NAME] + "="
|
|
|
|
// if a part of a primary key has been changed, use original value
|
|
if ::aDirty[nI]
|
|
cWhere += ClipValue2SQL(::aOldValue[nI])
|
|
else
|
|
cWhere += ClipValue2SQL(::aRow[nI])
|
|
endif
|
|
|
|
cWhere += " AND "
|
|
endif
|
|
|
|
next
|
|
|
|
// remove last " AND "
|
|
cWhere := Left(cWhere, Len(cWhere) - 5)
|
|
|
|
return cWhere
|
|
|
|
/* ----------------------------------------------------------------------------------------*/
|
|
|
|
// Every single query submitted to MySQL server
|
|
CLASS TMySQLQuery
|
|
|
|
DATA nSocket // connection handle to MySQL server
|
|
DATA nResultHandle // result handle received from MySQL
|
|
|
|
DATA cQuery // copy of query that generated this object
|
|
|
|
DATA nNumRows // number of rows available on answer NOTE MySQL is 0 based
|
|
DATA nCurRow // I'm currently over row number
|
|
|
|
//DAVID:
|
|
DATA lBof
|
|
DATA lEof
|
|
|
|
//DAVID:
|
|
DATA lFieldAsData //Use fields as object DATA. For compatibility
|
|
//Names of fields can match name of TMySQLQuery/Table DATAs,
|
|
//and it is dangerous. ::lFieldAsData:=.F. can fix it
|
|
DATA aRow //Values of fields of current row
|
|
|
|
DATA nNumFields // how many fields per row
|
|
DATA aFieldStruct // type of each field, a copy is here a copy inside each row
|
|
|
|
DATA lError // .T. if last operation failed
|
|
|
|
METHOD New(nSocket, cQuery) // New query object
|
|
METHOD Destroy()
|
|
METHOD End() INLINE ::Destroy()
|
|
METHOD Refresh() // ReExecutes the query (cQuery) so that changes to table are visible
|
|
|
|
METHOD GetRow(nRow) // return Row n of answer
|
|
|
|
METHOD Skip(nRows) // Same as clipper ones
|
|
|
|
METHOD Bof() INLINE ::lBof //DAVID: ::nCurRow == 1
|
|
METHOD Eof() INLINE ::lEof //DAVID: ::nCurRow == ::nNumRows
|
|
METHOD RecNo() INLINE ::nCurRow
|
|
METHOD LastRec() INLINE ::nNumRows
|
|
METHOD GoTop() INLINE ::GetRow(1)
|
|
METHOD GoBottom() INLINE ::GetRow(::nNumRows)
|
|
METHOD GoTO(nRow) INLINE ::GetRow(nRow)
|
|
|
|
METHOD FCount()
|
|
|
|
METHOD NetErr() INLINE ::lError // Returns .T. if something went wrong
|
|
METHOD Error() // Returns textual description of last error and clears ::lError
|
|
|
|
METHOD FieldName(nNum)
|
|
METHOD FieldPos(cFieldName)
|
|
METHOD FieldGet(cnField)
|
|
|
|
METHOD FieldLen(nNum) // Length of field N
|
|
METHOD FieldDec(nNum) // How many decimals in field N
|
|
METHOD FieldType(nNum) // Clipper type of field N
|
|
|
|
ENDCLASS
|
|
|
|
|
|
METHOD New(nSocket, cQuery) CLASS TMySQLQuery
|
|
|
|
local nI, aField, rc
|
|
|
|
::nSocket := nSocket
|
|
::cQuery := cQuery
|
|
|
|
::lError := .F.
|
|
::aFieldStruct := {}
|
|
::nCurRow := 1
|
|
::nResultHandle := nil
|
|
::nNumFields := 0
|
|
::nNumRows := 0
|
|
//DAVID:
|
|
::lBof := .T.
|
|
::lEof := .T.
|
|
|
|
::lFieldAsData := .T. //Use fields as object DATA. For compatibility
|
|
::aRow := {} //Values of fields of current row
|
|
|
|
if (rc := sqlQuery(nSocket, cQuery)) == 0
|
|
|
|
// save result set
|
|
if (::nResultHandle := sqlStoreR(nSocket)) > 0
|
|
|
|
::nNumRows := sqlNRows(::nResultHandle)
|
|
::nNumFields := sqlNumFi(::nResultHandle)
|
|
//DAVID:
|
|
::aRow := Array( ::nNumFields )
|
|
|
|
for nI := 1 to ::nNumFields
|
|
|
|
aField := sqlFetchF(::nResultHandle)
|
|
AAdd(::aFieldStruct, aField)
|
|
//DAVID:
|
|
if ::lFieldAsData
|
|
__ObjAddData(Self,::aFieldStruct[nI][MYSQL_FS_NAME])
|
|
endif
|
|
|
|
next
|
|
|
|
::getRow(::nCurRow)
|
|
|
|
else
|
|
// Should query have returned rows? (Was it a SELECT like query?)
|
|
|
|
if (::nNumFields := sqlNumFi(nSocket)) == 0
|
|
|
|
// Was not a SELECT so reset ResultHandle changed by previous sqlStoreR()
|
|
::nResultHandle := nil
|
|
|
|
else
|
|
::lError := .T.
|
|
|
|
endif
|
|
endif
|
|
|
|
else
|
|
::lError := .T.
|
|
endif
|
|
|
|
return Self
|
|
|
|
|
|
METHOD Refresh() CLASS TMySQLQuery
|
|
|
|
local rc
|
|
|
|
// free present result handle
|
|
sqlFreeR(::nResultHandle)
|
|
|
|
::lError := .F.
|
|
|
|
if (rc := sqlQuery(::nSocket, ::cQuery)) == 0
|
|
|
|
// save result set
|
|
::nResultHandle := sqlStoreR(::nSocket)
|
|
::nNumRows := sqlNRows(::nResultHandle)
|
|
|
|
// NOTE: I presume that number of fields doesn't change (that is nobody alters this table) between
|
|
// successive refreshes of the same
|
|
|
|
// But row number could very well change
|
|
if ::nCurRow > ::nNumRows
|
|
::nCurRow := ::nNumRows
|
|
endif
|
|
|
|
::getRow(::nCurRow)
|
|
|
|
else
|
|
/* ::aFieldStruct := {}
|
|
::nResultHandle := nil
|
|
::nNumFields := 0
|
|
::nNumRows := 0
|
|
*/
|
|
::lError := .T.
|
|
|
|
endif
|
|
|
|
return !::lError
|
|
|
|
|
|
METHOD Skip(nRows) CLASS TMySQLQuery
|
|
//DAVID:
|
|
local lbof
|
|
|
|
// NOTE: MySQL row count starts from 0
|
|
default nRows to 1
|
|
|
|
//DAVID:
|
|
::lBof := ( EMPTY( ::LastRec() ) )
|
|
|
|
if nRows == 0
|
|
// No move
|
|
|
|
elseif nRows < 0
|
|
// Negative movement
|
|
//DAVID: ::nCurRow := Max(::nCurRow + nRows, 1)
|
|
if ( ( ::recno() + nRows ) + 0 ) < 1
|
|
nRows := - ::recno() + 1
|
|
//Clipper: only SKIP movement can set BOF() to .T.
|
|
::lBof := .T. //Try to skip before first record
|
|
endif
|
|
|
|
else
|
|
// positive movement
|
|
//DAVID: ::nCurRow := Min(::nCurRow + nRows, ::nNumRows)
|
|
if ( ( ::recno() + nRows ) + 0 ) > ::lastrec()
|
|
nRows := ::lastrec() - ::recno() + 1
|
|
endif
|
|
|
|
endif
|
|
|
|
//DAVID:
|
|
::nCurRow := ::nCurRow + nRows
|
|
|
|
//DAVID: maintain ::bof() true until next movement
|
|
//Clipper: only SKIP movement can set BOF() to .T.
|
|
lbof := ::bof()
|
|
|
|
// sqlDataS(::nResultHandle, ::nCurRow - 1)
|
|
::getRow(::nCurrow)
|
|
|
|
if lbof
|
|
::lBof := .T.
|
|
endif
|
|
|
|
//DAVID: DBSKIP() return NIL return ::nCurRow
|
|
return nil
|
|
|
|
|
|
/* Given a three letter month name gives back month number as two char string (ie. Apr -> 04) */
|
|
static function NMonth(cMonthValue)
|
|
|
|
static cMonths := {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dec" }
|
|
local nMonth
|
|
|
|
nMonth := AScan(cMonths, cMonthValue)
|
|
|
|
return PadL(nMonth, 2, "0")
|
|
|
|
|
|
// Get row n of a query and return it as a TMySQLRow object
|
|
METHOD GetRow(nRow) CLASS TMySQLQuery
|
|
|
|
//DAVID: replaced by ::aRow local aRow := NIL
|
|
local oRow := NIL
|
|
local cFormatoDaData := set(4) //ACRESCENTEI
|
|
local i
|
|
|
|
//DAVID: use current row default nRow to 0
|
|
default nRow to ::nCurRow
|
|
|
|
if ::nResultHandle <> NIL
|
|
|
|
//DAVID:
|
|
::lBof := ( EMPTY( ::LastRec() ) )
|
|
|
|
if nRow < 1 .or. nRow > ::lastrec() //Out of range
|
|
// Equal to Clipper behaviour
|
|
nRow := ::lastrec() + 1 //LASTREC()+1
|
|
::nCurRow := ::lastrec() + 1
|
|
// ::lEof := .t.
|
|
endif
|
|
|
|
if nRow >= 1 .AND. nRow <= ::nNumRows
|
|
|
|
// NOTE: row count starts from 0
|
|
sqlDataS(::nResultHandle, nRow - 1)
|
|
::nCurRow := nRow
|
|
//DAVID: else
|
|
//DAVID: use current row ::nCurRow++
|
|
endif
|
|
|
|
//DAVID:
|
|
::lEof := ( ::Recno() > ::LastRec() )
|
|
::aRow := NIL
|
|
|
|
if ::eof()
|
|
// Phantom record with empty fields
|
|
::aRow := Array( Len( ::aFieldStruct ) )
|
|
Afill( ::aRow, "" )
|
|
|
|
else
|
|
::aRow := sqlFetchR(::nResultHandle)
|
|
endif
|
|
|
|
if ::aRow <> NIL
|
|
|
|
// Convert answer from text field to correct clipper types
|
|
for i := 1 to ::nNumFields
|
|
do case
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_TINY_TYPE
|
|
//DAVID:
|
|
if ::aRow[i]==NIL
|
|
::aRow[i] := "0"
|
|
endif
|
|
::aRow[i] := iif(Val(::aRow[i]) == 0, .F., .T.)
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_SHORT_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_LONG_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_LONGLONG_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_INT24_TYPE .OR. ;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_DECIMAL_TYPE
|
|
//DAVID:
|
|
if ::aRow[i]==NIL
|
|
::aRow[i] := "0"
|
|
endif
|
|
::aRow[i] := Val(::aRow[i])
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_DOUBLE_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_FLOAT_TYPE
|
|
//DAVID:
|
|
if ::aRow[i]==NIL
|
|
::aRow[i] := "0"
|
|
endif
|
|
::aRow[i] := Val(::aRow[i])
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_DATE_TYPE
|
|
if Empty(::aRow[i])
|
|
::aRow[i] := CToD("")
|
|
*else
|
|
// Date format YYYY-MM-DD
|
|
|
|
* abaixo presume que o se utiliza (set date USA)
|
|
* ::aRow[i] := CToD(SubStr(::aRow[i], 6, 2) + "/" + Right(::aRow[i], 2) + "/" + Left(::aRow[i], 4))
|
|
|
|
elseif cFormatoDaData = 'mm-dd-yyyy' // USA
|
|
::aRow[i] := ctod(substr(::aRow[i],6,2)+"-"+right(::aRow[i],2,0)+ "-" + Left(::aRow[i], 4))
|
|
|
|
elseif cFormatoDaData = 'dd/mm/yyyy' // BRITISH ou FRENCH
|
|
::aRow[i] := ctod(right(::aRow[i],2,0)+ "/"+ substr(::aRow[i],6,2)+"/"+ Left(::aRow[i], 4))
|
|
|
|
elseif cFormatoDaData = 'yyyy.mm.dd' // ANSI
|
|
::aRow[i] := ctod(Left(::aRow[i], 4)+ "."+substr(::aRow[i],6,2)+"."+right(::aRow[i],2,0))
|
|
|
|
elseif cFormatoDaData = 'dd.mm.yyyy' //GERMAN
|
|
::aRow[i] :=ctod(right(::aRow[i],2,0)+ "."+ substr(::aRow[i],6,2)+"."+ Left(::aRow[i], 4 ))
|
|
|
|
elseif cFormatoDaData = 'dd-mm-yyyy' //ITALIAN
|
|
::aRow[i] :=ctod(right(::aRow[i],2,0)+ "-"+ substr(::aRow[i],6,2)+"-"+ Left(::aRow[i], 4))
|
|
|
|
elseif cFormatoDaData = 'yyyy/mm/dd' //JAPAN
|
|
::aRow[i] := ctod(Left(::aRow[i], 4)+ "/"+substr(::aRow[i],6,2)+"/"+right(::aRow[i],2,0))
|
|
|
|
elseif cFormatoDaData = 'mm/dd/yyyy' // AMERICAN
|
|
::aRow[i] := ctod(substr(::aRow[i],6,2)+"/"+right(::aRow[i],2,0)+ "/" + Left(::aRow[i], 4))
|
|
else
|
|
::aRow[i] := "''"
|
|
endif
|
|
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_BLOB_TYPE
|
|
// Memo field
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_STRING_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_VAR_STRING_TYPE
|
|
// char field
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_DATETIME_TYPE
|
|
// DateTime field
|
|
|
|
otherwise
|
|
|
|
//DAVID: Alert("Unknown type from SQL Server Field: " + LTrim(Str(i))+" is type "+LTrim(Str(::aFieldStruct[i][MYSQL_FS_TYPE])))
|
|
// QOUT("Unknown type from SQL Server Field: " + LTrim(Str(i))+" is type "+LTrim(Str(::aFieldStruct[i][MYSQL_FS_TYPE])))
|
|
|
|
endcase
|
|
|
|
//DAVID:
|
|
if ::lFieldAsData
|
|
__objsetValuelist(Self,{{::aFieldStruct[i][MYSQL_FS_NAME],::aRow[i]}})
|
|
endif
|
|
|
|
next
|
|
|
|
oRow := TMySQLRow():New(::aRow, ::aFieldStruct)
|
|
|
|
endif
|
|
|
|
endif
|
|
//DAVID: if ::arow==nil; msginfo("::arow nil"); end
|
|
|
|
return iif(::aRow == NIL, NIL, oRow)
|
|
|
|
|
|
// Free result handle and associated resources
|
|
METHOD Destroy() CLASS TMySQLQuery
|
|
|
|
sqlFreeR(::nResultHandle)
|
|
|
|
return Self
|
|
|
|
|
|
METHOD FCount() CLASS TMySQLQuery
|
|
|
|
return ::nNumFields
|
|
|
|
|
|
METHOD Error() CLASS TMySQLQuery
|
|
|
|
::lError := .F.
|
|
|
|
return sqlGetErr(::nSocket)
|
|
|
|
// Given a field name returns it's position
|
|
METHOD FieldPos(cFieldName) CLASS TMySQLQuery
|
|
|
|
local cUpperName, nPos := 0
|
|
|
|
cUpperName := Upper(cFieldName)
|
|
|
|
//DAVID: nPos := AScan(::aFieldStruct, {|aItem| iif(Upper(aItem[MYSQL_FS_NAME]) == cUpperName, .T., .F.)})
|
|
nPos := AScan(::aFieldStruct, {|aItem| (Upper(aItem[MYSQL_FS_NAME]) == cUpperName)})
|
|
|
|
/*while ++nPos <= Len(::aFieldStruct)
|
|
if Upper(::aFieldStruct[nPos][MYSQL_FS_NAME]) == cUpperName
|
|
exit
|
|
endif
|
|
enddo
|
|
|
|
// I haven't found field name
|
|
if nPos > Len(::aFieldStruct)
|
|
nPos := 0
|
|
endif*/
|
|
|
|
return nPos
|
|
|
|
|
|
// Returns name of field N
|
|
METHOD FieldName(nNum) CLASS TMySQLQuery
|
|
|
|
if nNum >=1 .AND. nNum <= Len(::aFieldStruct)
|
|
return ::aFieldStruct[nNum][MYSQL_FS_NAME]
|
|
endif
|
|
|
|
return ""
|
|
|
|
METHOD FieldGet(cnField) CLASS TMySQLQuery
|
|
|
|
local nNum,Value
|
|
|
|
if ValType(cnField) == "C"
|
|
nNum := ::FieldPos(cnField)
|
|
else
|
|
nNum := cnField
|
|
endif
|
|
|
|
if nNum > 0 .AND. nNum <= ::nNumfields
|
|
//DAVID: Value := __objsendmsg(Self,::aFieldStruct[nNum][MYSQL_FS_NAME])
|
|
Value := ::aRow[ nNum ]
|
|
|
|
// Char fields are padded with spaces since a real .dbf field would be
|
|
if ::FieldType(nNum) == "C"
|
|
return PadR(Value,::aFieldStruct[nNum][MYSQL_FS_LENGTH])
|
|
else
|
|
return Value
|
|
endif
|
|
|
|
endif
|
|
|
|
return nil
|
|
|
|
|
|
METHOD FieldLen(nNum) CLASS TMySQLQuery
|
|
|
|
if nNum >=1 .AND. nNum <= Len(::aFieldStruct)
|
|
return ::aFieldStruct[nNum][MYSQL_FS_LENGTH]
|
|
endif
|
|
|
|
return 0
|
|
|
|
|
|
METHOD FieldDec(nNum) CLASS TMySQLQuery
|
|
|
|
if nNum >=1 .AND. nNum <= Len(::aFieldStruct)
|
|
return ::aFieldStruct[nNum][MYSQL_FS_DECIMALS]
|
|
endif
|
|
|
|
return 0
|
|
|
|
|
|
METHOD FieldType(nNum) CLASS TMySQLQuery
|
|
|
|
local cType := "U"
|
|
|
|
if nNum >=1 .AND. nNum <= Len(::aFieldStruct)
|
|
do case
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_TINY_TYPE
|
|
cType := "L"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_SHORT_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_LONG_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_LONGLONG_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_FLOAT_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_DOUBLE_TYPE.OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_DECIMAL_TYPE
|
|
cType := "N"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_DATE_TYPE
|
|
cType := "D"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_BLOB_TYPE
|
|
cType := "M"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_VAR_STRING_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_STRING_TYPE .OR.;
|
|
::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_DATETIME_TYPE
|
|
cType := "C"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_INT24_TYPE
|
|
cType := "N"
|
|
|
|
case ::aFieldStruct[nNum][MYSQL_FS_TYPE] == MYSQL_MEDIUM_BLOB_TYPE
|
|
cType := "M"
|
|
|
|
otherwise
|
|
cType := "U"
|
|
|
|
endcase
|
|
endif
|
|
|
|
return cType
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------------------------------*/
|
|
|
|
// A Table is a query without joins; this way I can Insert() e Delete() rows.
|
|
// NOTE: it's always a SELECT result, so it will contain a full table only if
|
|
// SELECT * FROM ... was issued
|
|
CLASS TMySQLTable FROM TMySQLQuery
|
|
|
|
DATA cTable // name of table
|
|
DATA aOldValue // keeps a copy of old value
|
|
|
|
METHOD New(nSocket, cQuery, cTableName)
|
|
METHOD GetRow(nRow)
|
|
METHOD Skip(nRow)
|
|
METHOD GoTop() INLINE ::GetRow(1)
|
|
METHOD GoBottom() INLINE ::GetRow(::nNumRows)
|
|
METHOD GoTo(nRow) INLINE ::GetRow(nRow)
|
|
|
|
//DAVID: lOldRecord, lrefresh added
|
|
METHOD Update(oRow, lOldRecord, lRefresh) // Gets an oRow and updates changed fields
|
|
|
|
METHOD Save() INLINE ::Update()
|
|
|
|
//DAVID: lOldRecord, lRefresh added
|
|
METHOD Delete(oRow, lOldRecord, lRefresh) // Deletes passed row from table
|
|
//DAVID: lRefresh added
|
|
METHOD Append(oRow, lRefresh) // Inserts passed row into table
|
|
//DAVID: lSetValues added
|
|
METHOD GetBlankRow( lSetValues ) // Returns an empty row with all available fields empty
|
|
//DAVID:
|
|
METHOD SetBlankRow() INLINE ::GetBlankRow( .T. ) //Compatibility
|
|
|
|
METHOD Blank() INLINE ::GetBlankRow()
|
|
METHOD FieldPut(cnField, Value) // field identifier, not only a number
|
|
METHOD Refresh()
|
|
METHOD MakePrimaryKeyWhere() // returns a WHERE x=y statement which uses primary key (if available)
|
|
|
|
ENDCLASS
|
|
|
|
|
|
METHOD New(nSocket, cQuery, cTableName) CLASS TMySQLTable
|
|
|
|
Local i := 0
|
|
super:New(nSocket, AllTrim(cQuery))
|
|
|
|
::cTable := Lower(cTableName)
|
|
::aOldValue:={}
|
|
|
|
for i := 1 to ::nNumFields
|
|
aadd(::aOldValue, ::fieldget(i))
|
|
next
|
|
|
|
|
|
return Self
|
|
|
|
|
|
METHOD GetRow(nRow) CLASS TMySQLTable
|
|
|
|
local oRow := super:GetRow(nRow),i := 0
|
|
|
|
if oRow <> NIL
|
|
oRow:cTable := ::cTable
|
|
endif
|
|
|
|
::aOldvalue:={}
|
|
for i := 1 to ::nNumFields
|
|
// ::aOldValue[i] := ::FieldGet(i)
|
|
aadd(::aOldvalue,::fieldget(i))
|
|
next
|
|
|
|
return oRow
|
|
|
|
|
|
METHOD Skip(nRow) CLASS TMySQLTable
|
|
Local i
|
|
super:skip(nRow)
|
|
|
|
for i := 1 to ::nNumFields
|
|
::aOldValue[i] := ::FieldGet(i)
|
|
next
|
|
|
|
//DAVID: DBSKIP() return NIL return Self
|
|
return nil
|
|
|
|
|
|
/* Creates an update query for changed fields and submits it to server */
|
|
//DAVID: lOldRecord, lRefresh added
|
|
METHOD Update(oRow, lOldRecord, lRefresh ) CLASS TMySQLTable
|
|
|
|
local cUpdateQuery := "UPDATE " + ::cTable + " SET "
|
|
local i, cField
|
|
//DAVID:
|
|
local ni, cWhere := " WHERE "
|
|
default lOldRecord to .F.
|
|
//DAVID: too many ::refresh() can slow some processes, so we can desactivate it by parameter
|
|
default lRefresh to .T.
|
|
|
|
::lError := .F.
|
|
|
|
Do case
|
|
|
|
// default Current row
|
|
case oRow==nil
|
|
|
|
for i := 1 to ::nNumFields
|
|
|
|
if ::aOldValue[i]<>::FieldGet(i)
|
|
cUpdateQuery += ::aFieldStruct[i][MYSQL_FS_NAME] + "=" + ClipValue2SQL(::FieldGet(i)) + ","
|
|
endif
|
|
next
|
|
|
|
// no Change
|
|
if right(cUpdateQuery,4)=="SET "; return !::lError; end
|
|
|
|
// remove last comma
|
|
cUpdateQuery := Left(cUpdateQuery, Len(cUpdateQuery) -1)
|
|
|
|
//DAVID:
|
|
if lOldRecord
|
|
// based in matching of ALL fields of old record
|
|
// WARNING: if there are more than one record of ALL fields matching, all of those records will be changed
|
|
|
|
for nI := 1 to Len(::aFieldStruct)
|
|
cWhere += ::aFieldStruct[nI][MYSQL_FS_NAME] + "="
|
|
// use original value
|
|
cWhere += ClipValue2SQL(::aOldValue[nI])
|
|
cWhere += " AND "
|
|
next
|
|
// remove last " AND "
|
|
cWhere := Left(cWhere, Len(cWhere) - 5)
|
|
cUpdateQuery += cWhere
|
|
|
|
else
|
|
//MakePrimaryKeyWhere is based in fields part of a primary key
|
|
cUpdateQuery += ::MakePrimaryKeyWhere()
|
|
endif
|
|
|
|
if sqlQuery(::nSocket, cUpdateQuery) == 0
|
|
//DAVID: Clipper maintain same record pointer
|
|
|
|
//DAVID: after refresh(), position of current record is often unpredictable
|
|
if lRefresh
|
|
::refresh()
|
|
else
|
|
//DAVID: just reset values (?)
|
|
for i := 1 to ::nNumFields
|
|
::aOldValue[i] := ::FieldGet(i)
|
|
next
|
|
endif
|
|
|
|
else
|
|
::lError := .T.
|
|
|
|
endif
|
|
|
|
Case oRow<>nil
|
|
|
|
if oRow:cTable == ::cTable
|
|
|
|
for i := 1 to Len(oRow:aRow)
|
|
if oRow:aDirty[i]
|
|
cUpdateQuery += oRow:aFieldStruct[i][MYSQL_FS_NAME] + "=" + ClipValue2SQL(oRow:aRow[i]) + ","
|
|
endif
|
|
next
|
|
|
|
// remove last comma
|
|
cUpdateQuery := Left(cUpdateQuery, Len(cUpdateQuery) -1)
|
|
|
|
//DAVID:
|
|
if lOldRecord
|
|
// based in matching of ALL fields of old record
|
|
// WARNING: if there are more than one record of ALL fields matching, all of those records will be changed
|
|
|
|
for nI := 1 to Len(oRow:aFieldStruct)
|
|
cWhere += oRow:aFieldStruct[nI][MYSQL_FS_NAME] + "="
|
|
// use original value
|
|
cWhere += ClipValue2SQL(oRow:aOriValue[nI])
|
|
cWhere += " AND "
|
|
next
|
|
// remove last " AND "
|
|
cWhere := Left(cWhere, Len(cWhere) - 5)
|
|
cUpdateQuery += cWhere
|
|
|
|
else
|
|
//MakePrimaryKeyWhere is based in fields part of a primary key
|
|
cUpdateQuery += oRow:MakePrimaryKeyWhere()
|
|
endif
|
|
|
|
if sqlQuery(::nSocket, cUpdateQuery) == 0
|
|
|
|
// All values are commited
|
|
Afill(oRow:aDirty, .F.)
|
|
Afill(oRow:aOldValue, nil)
|
|
|
|
//DAVID:
|
|
oRow:aOriValue := ACLONE( oRow:aRow )
|
|
|
|
//DAVID: Clipper maintain same record pointer
|
|
|
|
//DAVID: after refresh(), position of current record is often unpredictable
|
|
if lRefresh
|
|
::refresh()
|
|
endif
|
|
|
|
else
|
|
::lError := .T.
|
|
|
|
endif
|
|
|
|
endif
|
|
endCase
|
|
|
|
return !::lError
|
|
|
|
|
|
//DAVID: lOldRecord, lRefresh added
|
|
METHOD Delete(oRow, lOldRecord, lRefresh) CLASS TMySQLTable
|
|
|
|
local cDeleteQuery := "DELETE FROM " + ::cTable , i
|
|
|
|
//DAVID:
|
|
local ni, cWhere := " WHERE "
|
|
default lOldRecord to .F.
|
|
//DAVID: too many ::refresh() can slow some processes, so we can desactivate it by parameter
|
|
default lRefresh to .T.
|
|
|
|
// is this a row of this table ?
|
|
Do Case
|
|
Case orow==nil
|
|
|
|
//DAVID:
|
|
if lOldRecord
|
|
// based in matching of ALL fields of old record
|
|
// WARNING: if there are more than one record of ALL fields matching, all of those records will be changed
|
|
|
|
for nI := 1 to Len(::aFieldStruct)
|
|
cWhere += ::aFieldStruct[nI][MYSQL_FS_NAME] + "="
|
|
// use original value
|
|
cWhere += ClipValue2SQL(::aOldValue[nI])
|
|
cWhere += " AND "
|
|
next
|
|
// remove last " AND "
|
|
cWhere := Left(cWhere, Len(cWhere) - 5)
|
|
cDeleteQuery += cWhere
|
|
|
|
else
|
|
//MakePrimaryKeyWhere is based in fields part of a primary key
|
|
cDeleteQuery += ::MakePrimaryKeyWhere()
|
|
endif
|
|
|
|
if sqlQuery(::nSocket, cDeleteQuery) == 0
|
|
::lError := .F.
|
|
//DAVID: Clipper maintain same record pointer
|
|
//DAVID: ::nCurRow--
|
|
|
|
//DAVID: after refresh(), position of current record is often unpredictable
|
|
if lRefresh
|
|
::refresh()
|
|
else
|
|
//DAVID: just reset values (?)
|
|
for i := 1 to ::nNumFields
|
|
::aOldValue[i] := ::FieldGet(i)
|
|
next
|
|
endif
|
|
|
|
else
|
|
::lError := .T.
|
|
|
|
endif
|
|
|
|
Case oRow<>nil
|
|
if oRow:cTable == ::cTable
|
|
|
|
//DAVID:
|
|
if lOldRecord
|
|
// based in matching of ALL fields of old record
|
|
// WARNING: if there are more than one record of ALL fields matching, all of those records will be changed
|
|
|
|
for nI := 1 to Len(oRow:aFieldStruct)
|
|
cWhere += oRow:aFieldStruct[nI][MYSQL_FS_NAME] + "="
|
|
// use original value
|
|
cWhere += ClipValue2SQL(oRow:aOriValue[nI])
|
|
cWhere += " AND "
|
|
next
|
|
// remove last " AND "
|
|
cWhere := Left(cWhere, Len(cWhere) - 5)
|
|
cDeleteQuery += cWhere
|
|
|
|
else
|
|
//MakePrimaryKeyWhere is based in fields part of a primary key
|
|
cDeleteQuery += oRow:MakePrimaryKeyWhere()
|
|
endif
|
|
|
|
if sqlQuery(::nSocket, cDeleteQuery) == 0
|
|
::lError := .F.
|
|
|
|
//DAVID: after refresh(), position of current record is often unpredictable
|
|
if lRefresh
|
|
::refresh()
|
|
endif
|
|
|
|
else
|
|
::lError := .T.
|
|
|
|
endif
|
|
|
|
endif
|
|
EndCase
|
|
|
|
return !::lError
|
|
|
|
|
|
// Adds a row with values passed into oRow
|
|
//DAVID: lRefresh added
|
|
METHOD Append(oRow, lRefresh) CLASS TMySQLTable
|
|
|
|
local cInsertQuery := "INSERT INTO " + ::cTable + " ("
|
|
local i, cField
|
|
//DAVID: too many ::refresh() can slow some processes, so we can desactivate it by parameter
|
|
default lRefresh to .T.
|
|
|
|
Do Case
|
|
// default Current row
|
|
Case oRow==nil
|
|
|
|
// field names
|
|
for i := 1 to ::nNumFields
|
|
if ::aFieldStruct[i][MYSQL_FS_FLAGS]<>AUTO_INCREMENT_FLAG
|
|
cInsertQuery += ::aFieldStruct[i][MYSQL_FS_NAME] + ","
|
|
endif
|
|
next
|
|
// remove last comma from list
|
|
cInsertQuery := Left(cInsertQuery, Len(cInsertQuery) -1) + ") VALUES ("
|
|
|
|
// field values
|
|
for i := 1 to ::nNumFields
|
|
if ::aFieldStruct[i][MYSQL_FS_FLAGS]<>AUTO_INCREMENT_FLAG
|
|
cInsertQuery += ClipValue2SQL(::FieldGet(i)) + ","
|
|
endif
|
|
next
|
|
|
|
// remove last comma from list of values and add closing parenthesis
|
|
cInsertQuery := Left(cInsertQuery, Len(cInsertQuery) -1) + ")"
|
|
|
|
if sqlQuery(::nSocket, cInsertQuery) == 0
|
|
::lError := .F.
|
|
//DAVID: Clipper add record at end
|
|
::nCurRow := ::lastrec() + 1
|
|
|
|
//DAVID: after refresh(), position of current record is often unpredictable
|
|
if lRefresh
|
|
::refresh()
|
|
else
|
|
//DAVID: just reset values in memory (?)
|
|
/* was same values from fieldget(i) !
|
|
for i := 1 to ::nNumFields
|
|
::aOldValue[i] := ::FieldGet(i)
|
|
next
|
|
*/
|
|
endif
|
|
|
|
return .T.
|
|
else
|
|
::lError := .T.
|
|
endif
|
|
|
|
Case oRow<>nil
|
|
|
|
if oRow:cTable == ::cTable
|
|
|
|
// field names
|
|
for i := 1 to Len(oRow:aRow)
|
|
if oRow:aFieldStruct[i][MYSQL_FS_FLAGS]<>AUTO_INCREMENT_FLAG
|
|
cInsertQuery += oRow:aFieldStruct[i][MYSQL_FS_NAME] + ","
|
|
endif
|
|
next
|
|
// remove last comma from list
|
|
cInsertQuery := Left(cInsertQuery, Len(cInsertQuery) -1) + ") VALUES ("
|
|
|
|
// field values
|
|
for i := 1 to Len(oRow:aRow)
|
|
if oRow:aFieldStruct[i][MYSQL_FS_FLAGS]<>AUTO_INCREMENT_FLAG
|
|
cInsertQuery += ClipValue2SQL(oRow:aRow[i]) + ","
|
|
endif
|
|
next
|
|
|
|
// remove last comma from list of values and add closing parenthesis
|
|
cInsertQuery := Left(cInsertQuery, Len(cInsertQuery) -1) + ")"
|
|
|
|
if sqlQuery(::nSocket, cInsertQuery) == 0
|
|
//DAVID:
|
|
::lError := .F.
|
|
|
|
//DAVID:
|
|
// All values are commited
|
|
Afill(oRow:aDirty, .F.)
|
|
Afill(oRow:aOldValue, nil)
|
|
|
|
//DAVID:
|
|
oRow:aOriValue := ACLONE( oRow:aRow )
|
|
|
|
//DAVID: Clipper add record at end
|
|
::nCurRow := ::lastrec() + 1
|
|
|
|
//DAVID: after refresh(), position of current record is often unpredictable
|
|
if lRefresh
|
|
::refresh()
|
|
endif
|
|
|
|
return .T.
|
|
else
|
|
::lError := .T.
|
|
endif
|
|
|
|
endif
|
|
|
|
Endcase
|
|
return .F.
|
|
|
|
|
|
//DAVID: lSetValues added METHOD GetBlankRow() CLASS TMySQLTable
|
|
METHOD GetBlankRow( lSetValues ) CLASS TMySQLTable
|
|
|
|
local i
|
|
local aRow := Array(::nNumFields)
|
|
|
|
//DAVID: It is not current row, so do not change it
|
|
default lSetValues to .F.
|
|
|
|
// crate an array of empty fields
|
|
for i := 1 to ::nNumFields
|
|
|
|
do case
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_STRING_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_VAR_STRING_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_BLOB_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_DATETIME_TYPE
|
|
aRow[i] := ""
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_SHORT_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_LONG_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_LONGLONG_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_INT24_TYPE .OR. ;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_DECIMAL_TYPE
|
|
|
|
aRow[i] := 0
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_TINY_TYPE
|
|
aRow[i] := .F.
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_DOUBLE_TYPE .OR.;
|
|
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_FLOAT_TYPE
|
|
aRow[i] := 0.0
|
|
|
|
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_DATE_TYPE
|
|
aRow[i] := CToD("")
|
|
|
|
otherwise
|
|
aRow[i] := nil
|
|
|
|
endcase
|
|
next
|
|
|
|
//DAVID:
|
|
if lSetValues //Assign values as current row values
|
|
for i := 1 to ::nNumFields
|
|
::FieldPut(i, aRow[i])
|
|
::aOldValue[i] := aRow[i]
|
|
next
|
|
endif
|
|
|
|
return TMySQLRow():New(aRow, ::aFieldStruct, ::cTable, .F.)
|
|
|
|
return nil
|
|
|
|
|
|
METHOD FieldPut(cnField, Value) CLASS TMySQLTable
|
|
|
|
local nNum
|
|
|
|
if ValType(cnField) == "C"
|
|
nNum := ::FieldPos(cnField)
|
|
else
|
|
nNum := cnField
|
|
endif
|
|
|
|
if nNum > 0 .AND. nNum <= ::nNumFields
|
|
|
|
//DAVID: if Valtype(Value) == Valtype(::FieldGet(nNum)) .OR. Empty(::Fieldget(nNum))
|
|
if Valtype(Value) == Valtype(::aRow[nNum]) .OR. ::aRow[nNum]==NIL
|
|
|
|
// if it is a char field remove trailing spaces
|
|
if ValType(Value) == "C"
|
|
Value := RTrim(Value)
|
|
endif
|
|
|
|
//DAVID:
|
|
::aRow[ nNum ] := Value
|
|
if ::lFieldAsData
|
|
__objsetValueList(Self,{{::aFieldStruct[nNum][MYSQL_FS_NAME],Value}})
|
|
endif
|
|
|
|
return Value
|
|
endif
|
|
endif
|
|
|
|
return nil
|
|
|
|
|
|
METHOD Refresh() CLASS TMySQLTABLE
|
|
|
|
local rc, i
|
|
|
|
// free present result handle
|
|
sqlFreeR(::nResultHandle)
|
|
|
|
::lError := .F.
|
|
|
|
if (rc := sqlQuery(::nSocket, ::cQuery)) == 0
|
|
|
|
// save result set
|
|
::nResultHandle := sqlStoreR(::nSocket)
|
|
::nNumRows := sqlNRows(::nResultHandle)
|
|
|
|
// NOTE: I presume that number of fields doesn't change (that is nobody alters this table) between
|
|
// successive refreshes of the same
|
|
|
|
// But row number could very well change
|
|
if ::nCurRow > ::nNumRows
|
|
::nCurRow := ::nNumRows
|
|
endif
|
|
|
|
::getRow(::nCurRow)
|
|
|
|
else
|
|
/* ::aFieldStruct := {}
|
|
::nResultHandle := nil
|
|
::nNumFields := 0
|
|
::nNumRows := 0
|
|
|
|
::aOldValue:={}
|
|
*/
|
|
::lError := .T.
|
|
endif
|
|
|
|
return !::lError
|
|
|
|
|
|
// returns a WHERE x=y statement which uses primary key (if available)
|
|
METHOD MakePrimaryKeyWhere() CLASS TMySQLTable
|
|
|
|
local ni, cWhere := " WHERE "
|
|
|
|
for nI := 1 to Len(::aFieldStruct)
|
|
|
|
// search for fields part of a primary key
|
|
if (sqlAND(::aFieldStruct[nI][MYSQL_FS_FLAGS], PRI_KEY_FLAG) == PRI_KEY_FLAG) .OR.;
|
|
(sqlAND(::aFieldStruct[nI][MYSQL_FS_FLAGS], MULTIPLE_KEY_FLAG) == MULTIPLE_KEY_FLAG)
|
|
|
|
cWhere += ::aFieldStruct[nI][MYSQL_FS_NAME] + "="
|
|
|
|
// if a part of a primary key has been changed, use original value
|
|
|
|
cWhere += ClipValue2SQL(::aOldValue[nI])
|
|
|
|
|
|
cWhere += " AND "
|
|
endif
|
|
|
|
next
|
|
|
|
// remove last " AND "
|
|
cWhere := Left(cWhere, Len(cWhere) - 5)
|
|
|
|
return cWhere
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------------------------------*/
|
|
|
|
// Every available MySQL server
|
|
CLASS TMySQLServer
|
|
|
|
DATA nSocket // connection handle to server (currently pointer to a MYSQL structure)
|
|
DATA cServer // server name
|
|
DATA cDBName // Selected DB
|
|
DATA cUser // user accessing db
|
|
DATA cPassword // his/her password
|
|
DATA lError // .T. if occurred an error
|
|
DATA cCreateQuery
|
|
|
|
METHOD New(cServer, cUser, cPassword) // Opens connection to a server, returns a server object
|
|
METHOD Destroy() // Closes connection to server
|
|
|
|
METHOD SelectDB(cDBName) // Which data base I will use for subsequent queries
|
|
|
|
METHOD CreateTable(cTable, aStruct,cPrimaryKey,cUniqueKey,cAuto) // Create new table using the same syntax of dbCreate()
|
|
METHOD DeleteTable(cTable) // delete table
|
|
METHOD TableStruct(cTable) // returns a structure array compatible with clipper's dbStruct() ones
|
|
METHOD CreateIndex(cName, cTable, aFNames, lUnique) // Create an index (unique) on field name(s) passed as an array of strings aFNames
|
|
METHOD DeleteIndex(cName, cTable) // Delete index cName from cTable
|
|
|
|
METHOD ListDBs() // returns an array with list of data bases available
|
|
METHOD ListTables() // returns an array with list of available tables in current database
|
|
|
|
METHOD Query(cQuery) // Gets a textual query and returns a TMySQLQuery or TMySQLTable object
|
|
|
|
METHOD NetErr() INLINE ::lError // Returns .T. if something went wrong
|
|
METHOD Error() // Returns textual description of last error
|
|
METHOD CreateDatabase( cDataBase ) // Create an New Mysql Database
|
|
|
|
ENDCLASS
|
|
|
|
|
|
METHOD New(cServer, cUser, cPassword) CLASS TMySQLServer
|
|
|
|
::cServer := cServer
|
|
::cUser := cUser
|
|
::cPassword := cPassword
|
|
::nSocket := sqlConnect(cServer, cUser, cPassword)
|
|
::lError := .F.
|
|
|
|
if ::nSocket == 0
|
|
::lError := .T.
|
|
endif
|
|
|
|
return Self
|
|
|
|
METHOD Destroy() CLASS TMySQLServer
|
|
|
|
sqlClose(::nSocket)
|
|
|
|
return Self
|
|
|
|
|
|
*METHOD SelectDB(cDBName) CLASS TMySQLServer
|
|
*
|
|
* if sqlSelectD(::nSocket, cDBName) == 0
|
|
* ::cDBName := cDBName
|
|
* return .T.
|
|
* else
|
|
* ::cDBName := ""
|
|
* endif
|
|
*
|
|
*return .F.
|
|
|
|
*****************alterado
|
|
METHOD SelectDB(cDBName) CLASS TMySQLServer
|
|
|
|
::lError := .F.
|
|
|
|
if sqlSelectD(::nSocket, cDBName) != 0 && tabela nao existe
|
|
::cDBName :=""
|
|
::lError := .T.
|
|
else && tabela existe
|
|
::cDBName :=cDBName
|
|
::lError := .F.
|
|
return .T.
|
|
endif
|
|
|
|
return .F.
|
|
|
|
|
|
|
|
METHOD CreateDatabase ( cDataBase ) CLASS TMySQLServer
|
|
local cCreateQuery := "CREATE DATABASE "+ lower(cDatabase)
|
|
|
|
if sqlQuery(::nSocket, cCreateQuery) == 0
|
|
return .T.
|
|
endif
|
|
|
|
return .F.
|
|
|
|
|
|
// NOTE: OS/2 port of MySQL is picky about table names, that is if you create a table with
|
|
// an upper case name you cannot alter it (for example) using a lower case name, this violates
|
|
// OS/2 case insensibility about names
|
|
METHOD CreateTable(cTable, aStruct,cPrimaryKey,cUniqueKey,cAuto) CLASS TMySQLServer
|
|
|
|
/* NOTE: all table names are created with lower case */
|
|
|
|
local i
|
|
|
|
// returns NOT NULL if extended structure has DBS_NOTNULL field to true
|
|
local cNN := {|aArr| iif(Len(aArr) > DBS_DEC, iif(aArr[DBS_NOTNULL], " NOT NULL ", ""), "")}
|
|
::cCreateQuery := "CREATE TABLE " + Lower(cTable) + " ("
|
|
for i := 1 to Len(aStruct)
|
|
do case
|
|
case aStruct[i][DBS_TYPE] == "C"
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " char(" + AllTrim(Str(aStruct[i][DBS_LEN])) + ")" + Eval(cNN, aStruct[i])+ if(aStruct[i][DBS_NAME]==cPrimaryKey," NOT NULL ",'' )+ ","
|
|
|
|
case aStruct[i][DBS_TYPE] == "M"
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " text" + Eval(cNN, aStruct[i]) + ","
|
|
|
|
case aStruct[i][DBS_TYPE] == "N"
|
|
/*
|
|
if aStruct[i][DBS_DEC] == 0
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " int(" + AllTrim(Str(aStruct[i][DBS_LEN])) + ")" + Eval(cNN, aStruct[i]) + if(aStruct[i][DBS_NAME]==cPrimaryKey," NOT NULL ",'' )+ if(aStruct[i][DBS_NAME]==cAuto," auto_increment ",'' ) + ","
|
|
else
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " real(" + AllTrim(Str(aStruct[i][DBS_LEN])) + "," + AllTrim(Str(aStruct[i][DBS_DEC])) + ")" + Eval(cNN, aStruct[i]) + ","
|
|
endif
|
|
*/
|
|
if (aStruct[i][DBS_DEC] == 0) .and. (aStruct[i][DBS_LEN] <= 18)
|
|
do case
|
|
case (aStruct[i][DBS_LEN] <= 4)
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " smallint(" + AllTrim(Str(aStruct[i][DBS_LEN])) + ")"
|
|
case (aStruct[i][DBS_LEN] <= 6)
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " mediumint(" + AllTrim(Str(aStruct[i][DBS_LEN])) + ")"
|
|
case (aStruct[i][DBS_LEN] <= 9)
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " int(" + AllTrim(Str(aStruct[i][DBS_LEN])) + ")"
|
|
otherwise
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " bigint(" + AllTrim(Str(aStruct[i][DBS_LEN])) + ")"
|
|
endcase
|
|
::cCreateQuery += Eval(cNN, aStruct[i]) + if(aStruct[i][DBS_NAME]==cPrimaryKey," NOT NULL ",'' )+ if(aStruct[i][DBS_NAME]==cAuto," auto_increment ",'' ) + ","
|
|
else
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " real(" + AllTrim(Str(aStruct[i][DBS_LEN])) + "," + AllTrim(Str(aStruct[i][DBS_DEC])) + ")" + Eval(cNN, aStruct[i]) + ","
|
|
endif
|
|
case aStruct[i][DBS_TYPE] == "D"
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " date " + Eval(cNN, aStruct[i]) + ","
|
|
|
|
case aStruct[i][DBS_TYPE] == "L"
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " tinyint " + Eval(cNN, aStruct[i]) + ","
|
|
|
|
case aStruct[i][DBS_TYPE] == "B"
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " mediumblob " + Eval(cNN, aStruct[i]) + ","
|
|
|
|
case aStruct[i][DBS_TYPE] == "I"
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " mediumint " + Eval(cNN, aStruct[i]) + ","
|
|
|
|
otherwise
|
|
::cCreateQuery += aStruct[i][DBS_NAME] + " char(" + AllTrim(Str(aStruct[i][DBS_LEN])) + ")" + Eval(cNN, aStruct[i]) + ","
|
|
|
|
endcase
|
|
|
|
next
|
|
if cPrimarykey != NIL
|
|
::cCreateQuery += ' PRIMARY KEY ('+cPrimaryKey+'),'
|
|
endif
|
|
if cUniquekey != NIL
|
|
::cCreateQuery += ' UNIQUE '+cUniquekey +' ('+cUniqueKey+'),'
|
|
endif
|
|
|
|
// remove last comma from list
|
|
::cCreateQuery := Left(::cCreateQuery, Len(::cCreateQuery) -1) + ");"
|
|
if sqlQuery(::nSocket, ::cCreateQuery) == 0
|
|
return .T.
|
|
else
|
|
::lError := .T.
|
|
endif
|
|
|
|
return .F.
|
|
|
|
|
|
METHOD CreateIndex(cName, cTable, aFNames, lUnique) CLASS TMySQLServer
|
|
|
|
local cCreateQuery := "CREATE "
|
|
local i
|
|
|
|
default lUnique to .F.
|
|
|
|
if lUnique
|
|
cCreateQuery += "UNIQUE INDEX "
|
|
else
|
|
cCreateQuery += "INDEX "
|
|
endif
|
|
|
|
cCreateQuery += cName + " ON " + Lower(cTable) + " ("
|
|
|
|
for i := 1 to Len(aFNames)
|
|
cCreateQuery += aFNames[i] + ","
|
|
next
|
|
|
|
// remove last comma from list
|
|
cCreateQuery := Left(cCreateQuery, Len(cCreateQuery) -1) + ")"
|
|
|
|
if sqlQuery(::nSocket, cCreateQuery) == 0
|
|
return .T.
|
|
|
|
endif
|
|
|
|
return .F.
|
|
|
|
|
|
METHOD DeleteIndex(cName, cTable) CLASS TMySQLServer
|
|
|
|
local cDropQuery := "DROP INDEX " + cName + " FROM " + Lower(cTable)
|
|
|
|
if sqlQuery(::nSocket, cDropQuery) == 0
|
|
return .T.
|
|
endif
|
|
|
|
return .F.
|
|
|
|
|
|
METHOD DeleteTable(cTable) CLASS TMySQLServer
|
|
|
|
local cDropQuery := "DROP TABLE " + Lower(cTable)
|
|
|
|
if sqlQuery(::nSocket, cDropQuery) == 0
|
|
return .T.
|
|
|
|
endif
|
|
|
|
return .F.
|
|
|
|
|
|
METHOD Query(cQuery) CLASS TMySQLServer
|
|
|
|
local oQuery, cTableName, i, cUpperQuery, nNumTables, cToken
|
|
|
|
default cQuery to ""
|
|
|
|
|
|
cUpperQuery := Upper(AllTrim(cQuery))
|
|
i := 1
|
|
nNumTables := 1
|
|
|
|
while (cToken := __StrToken(cUpperQuery, i++, " ")) <> "FROM" .AND. !Empty(cToken)
|
|
enddo
|
|
|
|
// first token after "FROM" is a table name
|
|
// NOTE: SubSelects ?
|
|
cTableName := __StrToken(cUpperQuery, i++, " ")
|
|
|
|
while (cToken := __StrToken(cUpperQuery, i++, " ")) <> "WHERE" .AND. !Empty(cToken)
|
|
// do we have more than one table referenced ?
|
|
if cToken == "," .OR. cToken == "JOIN"
|
|
nNumTables++
|
|
endif
|
|
enddo
|
|
|
|
if nNumTables == 1
|
|
oQuery := TMySQLTable():New(::nSocket, cQuery, cTableName)
|
|
else
|
|
oQuery := TMySQLQuery():New(::nSocket, cQuery)
|
|
endif
|
|
|
|
if oQuery:NetErr()
|
|
::lError := .T.
|
|
endif
|
|
|
|
return oQuery
|
|
|
|
|
|
METHOD Error() CLASS TMySQLServer
|
|
|
|
::lError := .F.
|
|
|
|
return iif(::nSocket > 0, sqlGetErr(::nSocket), "No connection to server")
|
|
|
|
|
|
METHOD ListDBs() CLASS TMySQLServer
|
|
|
|
local aList
|
|
|
|
aList := sqlListDB(::nSocket)
|
|
|
|
return aList
|
|
|
|
|
|
METHOD ListTables() CLASS TMySQLServer
|
|
|
|
local aList
|
|
|
|
aList := sqlListTbl(::nSocket)
|
|
|
|
return aList
|
|
|
|
|
|
/* TOFIX: Conversion creates a .dbf with fields of wrong dimension (often) */
|
|
METHOD TableStruct(cTable) CLASS TMySQLServer
|
|
|
|
local nRes, aField, aStruct, aSField, i
|
|
|
|
|
|
aStruct := {}
|
|
|
|
/* TODO: rewrite for MySQL
|
|
nRes := sqlListF(::nSocket, cTable)
|
|
|
|
if nRes > 0
|
|
for i := 1 to sqlNumFi(nRes)
|
|
|
|
aField := sqlFetchF(nRes)
|
|
aSField := Array(DBS_DEC)
|
|
|
|
// don't count indexes as real fields
|
|
if aField[MSQL_FS_TYPE] <= MSQL_LAST_REAL_TYPE
|
|
|
|
aSField[DBS_NAME] := Left(aField[MSQL_FS_NAME], 10)
|
|
aSField[DBS_DEC] := 0
|
|
|
|
do case
|
|
case aField[MSQL_FS_TYPE] == MSQL_INT_TYPE
|
|
aSField[DBS_TYPE] := "N"
|
|
aSField[DBS_LEN] := 11
|
|
|
|
case aField[MSQL_FS_TYPE] == MSQL_UINT_TYPE
|
|
aSField[DBS_TYPE] := "L"
|
|
aSField[DBS_LEN] := 1
|
|
|
|
case aField[MSQL_FS_TYPE] == MSQL_CHAR_TYPE
|
|
aSField[DBS_TYPE] := "C"
|
|
aSField[DBS_LEN] := aField[MSQL_FS_LENGTH]
|
|
|
|
case aField[MSQL_FS_TYPE] == MSQL_DATE_TYPE
|
|
aSField[DBS_TYPE] := "D"
|
|
aSField[DBS_LEN] := aField[MSQL_FS_LENGTH]
|
|
|
|
case aField[MSQL_FS_TYPE] == MSQL_REAL_TYPE
|
|
aSField[DBS_TYPE] := "N"
|
|
aSField[DBS_LEN] := 12
|
|
aSFIeld[DBS_DEC] := 8
|
|
|
|
case aField[MSQL_FS_TYPE] == MYSQL_MEDIUM_BLOB_TYPE
|
|
aSField[DBS_TYPE] := "B"
|
|
aSField[DBS_LEN] := aField[MSQL_FS_LENGTH]
|
|
|
|
case aField[MSQL_FS_TYPE] == FIELD_TYPE_INT24
|
|
aSField[DBS_TYPE] := "I"
|
|
aSField[DBS_LEN] := aField[MSQL_FS_LENGTH]
|
|
aSFIeld[DBS_DEC] := aField[MYSQL_FS_DECIMALS]
|
|
otherwise
|
|
|
|
endcase
|
|
|
|
AAdd(aStruct, aSField)
|
|
endif
|
|
next
|
|
|
|
sqlFreeR(nRes)
|
|
|
|
endif*/
|
|
|
|
return aStruct
|
|
|
|
|
|
// Returns an SQL string with clipper value converted ie. Date() -> "'YYYY-MM-DD'"
|
|
static function ClipValue2SQL(Value)
|
|
|
|
local cValue := ""
|
|
local cFormatoDaData := set(4)
|
|
do case
|
|
case Valtype(Value) == "N"
|
|
cValue := AllTrim(Str(Value))
|
|
|
|
case Valtype(Value) == "D"
|
|
if !Empty(Value)
|
|
// MySQL dates are like YYYY-MM-DD
|
|
if cFormatoDaData = 'mm-dd-yyyy' // USA
|
|
cValue := "'"+PadL(Month(Value), 2, "0") + '-'+ PadL(Day(Value), 2, "0") + "-" + Str(Year(Value), 4) + "'"
|
|
|
|
elseif cFormatoDaData = 'dd/mm/yyyy' // BRITISH ou FRENCH
|
|
cValue := "'"+PadL(Day(Value), 2, "0") + "/" + PadL(Month(Value), 2, "0") + "/" + Str(Year(Value), 4) + "'"
|
|
|
|
elseif cFormatoDaData = 'yyyy.mm.dd' // ANSI
|
|
cValue := "'"+Str(Year(Value), 4) + "." + PadL(Month(Value), 2, "0") + "." + PadL(Day(Value), 2, "0") + "'"
|
|
|
|
elseif cFormatoDaData = 'dd.mm.yyyy' //GERMAN
|
|
cValue := "'"+PadL(Day(Value), 2, "0") + "." + PadL(Month(Value), 2, "0") + "." + Str(Year(Value), 4) + "'"
|
|
|
|
elseif cFormatoDaData = 'dd-mm-yyyy' //ITALIAN
|
|
cValue := "'"+PadL(Day(Value), 2, "0") + "-" + PadL(Month(Value), 2, "0") + "-" + Str(Year(Value), 4) + "'"
|
|
|
|
elseif cFormatoDaData = 'yyyy/mm/dd' //JAPAN
|
|
cValue := "'"+Str(Year(Value), 4) + "/" + PadL(Month(Value), 2, "0") + "/" + PadL(Day(Value), 2, "0") + "'"
|
|
|
|
elseif cFormatoDaData = 'mm/dd/yyyy' // AMERICAN
|
|
cValue := "'"+Str(Year(Value), 4) + "/" + PadL(Month(Value), 2, "0") + "/" + PadL(Day(Value), 2, "0") + "'"
|
|
endif
|
|
else
|
|
cValue := "''"
|
|
endif
|
|
|
|
case Valtype(Value) $ "CM"
|
|
IF Empty( Value)
|
|
cValue="''"
|
|
ELSE
|
|
cValue := "'"
|
|
Value:=DATATOSQL(value)
|
|
cValue+= value+ "'"
|
|
ENDIF
|
|
|
|
case Valtype(Value) == "L"
|
|
cValue := AllTrim(Str(iif(Value == .F., 0, 1)))
|
|
|
|
otherwise
|
|
cValue := "''" // NOTE: Here we lose values we cannot convert
|
|
|
|
endcase
|
|
|
|
return cValue |