$APPTYPE CONSOLE

'ODBC - Open DataBase Connectivity
'Basic Steps
'Connecting to the SQL Server DataBase for retrieving information from tables

' ODBC Variables and Constants
Const MAX_DATA_BUFFER = 255
Const SQL_SUCCESS = 0
Const SQL_SUCCESS_WITH_INFO = 1
Const SQL_ERROR = -1
Const SQL_NO_DATA_FOUND = 100
Const SQL_CLOSE = 0
Const SQL_DROP = 1
Const SQL_CHAR = 1
Const SQL_NUMERIC = 2
Const SQL_DECIMAL = 3
Const SQL_INTEGER = 4
Const SQL_SMALLINT = 5
Const SQL_FLOAT = 6
Const SQL_REAL = 7
Const SQL_DOUBLE = 8
Const SQL_VARCHAR = 12
Const SQL_DATA_SOURCE_NAME = 6
Const SQL_USER_NAME = 8

'ODBC Declarations
Declare Function SQLAllocEnv Lib "odbc32.dll" Alias "SQLAllocEnv"(env As Long) As Short
Declare Function SQLFreeEnv Lib "odbc32.dll" Alias "SQLFreeEnv"(ByVal env As Long) As Short
Declare Function SQLAllocConnect Lib "odbc32.dll" Alias "SQLAllocConnect"(ByVal env As Long, ldbc As Long) As Short
Declare Function SQLConnect Lib "odbc32.dll" Alias "SQLConnect"(ByVal ldbc As Long, ByVal Server As String, ByVal serverlen As Integer, ByVal uid As String, ByVal uidlen As Integer, ByVal pwd As String, ByVal pwdlen As Integer) As Short
Declare Function SQLDriverConnect Lib "odbc32.dll" Alias "SQLDriverConnect"(ByVal ldbc As Long, ByVal hWnd As Long, ByVal szCSIn As Long, ByVal cbCSIn As Integer, ByVal szCSOut As Long, ByVal cbCSMax As Integer, cbCSOut As Long, ByVal f As Integer) As Short
Declare Function SQLFreeConnect Lib "odbc32.dll" Alias "SQLFreeConnect"(ByVal ldbc As Long) As Short
Declare Function SQLDisconnect Lib "odbc32.dll" Alias "SQLDisconnect"(ByVal ldbc As Long) As Short
Declare Function SQLAllocStmt Lib "odbc32.dll" Alias "SQLAllocStmt"(ByVal ldbc As Long, lStmt As Long) As Short
Declare Function SQLFreeStmt Lib "odbc32.dll" Alias "SQLFreeStmt"(ByVal lStmt As Long, ByVal EndOption As Integer) As Short
Declare Function SQLTables Lib "odbc32.dll" Alias "SQLTables"(ByVal lStmt As Long, ByVal q As Long, ByVal cbq As Integer, ByVal o As Long, ByVal cbo As Integer, ByVal t As Long, ByVal cbt As Integer, ByVal tt As Long, ByVal cbtt As Integer) As Short
Declare Function SQLExecDirect Lib "odbc32.dll" Alias "SQLExecDirect"(ByVal lStmt As Long, ByVal sqlString As LONG, ByVal sqlstrlen As Long) As Short
Declare Function SQLNumResultCols Lib "odbc32.dll" Alias "SQLNumResultCols"(ByVal lStmt As Long, NumCols As Long) As Short
Declare Function SQLDescribeCol Lib "odbc32.dll" Alias "SQLDescribeCol"(ByVal lStmt As Long, ByVal colnum As Integer, ByVal colname As Long, ByVal Buflen As Integer, colnamelen As Integer, dtype As Integer, dl As Long, ds As Integer, n As Integer) As Short
Declare Function SQLFetch Lib "odbc32.dll" Alias "SQLFetch"(ByVal lStmt As Long) As Short
Declare Function SQLGetData Lib "odbc32.dll" Alias "SQLGetData"(ByVal lStmt As Long, ByVal col As Integer, ByVal wConvType As Integer, ByVal lpbBuf As Long, ByVal dwbuflen As Long, lpcbout As Long) As Short
Declare Function SQLGetInfo Lib "odbc32.dll" Alias "SQLGetInfo"(ByVal ldbc As Long, ByVal hWnd As Long, ByVal szInfo As String, ByVal cbInfoMax As Integer, cbInfoOut As Integer) As Short
Declare Function SQLError Lib "odbc32.dll" Alias "SQLError"(ByVal env As Long, ByVal ldbc As Long, ByVal lStmt As Long, ByVal SQLState As Long, NativeError As Long, ByVal Buffer As Long, ByVal Buflen As Integer, Outlen As Integer) As Short
Declare Function SQLCloseCursor Lib "odbc32.dll" Alias "SQLCloseCursor"(ByVal lStmt As Long) As Short
Declare Function SQLDrivers Lib "odbc32.dll" Alias "SQLDrivers"(ByVal env As Long, ByVal dir As Integer, ByVal descrip as Long, ByVal bflen as Integer, descriplen as Integer, ByVal attrib as Long, ByVal bfattrlen as Integer, attriblen as Integer) As Short

Type qODBC EXTENDS Qobject

  glEnv As Long
  glDbc As Long
  glStmt as LONG
  sSQL As String
  SQLRet As Short
  sConnect as String
  DriverCount as Integer
  Driver(100) as String
  TableCount as integer
  Table(100) as String
  FieldCount as Integer
  Field.Name(100) as String
  Field.TypeNum(100) as integer
  Field.TypeStr(100) as string
  Field.Size(100) as Integer
  Field.DecDigits(100) as Integer
  Field.Nullav(100) as Integer    
  Field.Data(100) as String


 Sub ODBCInit
   DIM iStatus As Short
   
  '1. Allocate ODBC Environment Handle
  SQLRet = SQLAllocEnv(VARPTR(qODBC.glEnv))
  If SQLRet <> SQL_SUCCESS Then
	MessageBox("Unable to initialize ODBC API drivers!", "Error", 0) 
  Else
   '2. Allocate ODBC Database Handle
   SQLRet=SQLAllocConnect(qODBC.glEnv, VARPTR(qODBC.glDbc))
   If SQLRet<> SQL_SUCCESS Then
     MessageBox("Could not allocate memory for connection Handle!", "Error", 0)
     ' Free the Environment
     iStatus = SQLFreeEnv(qODBC.glEnv)
     If iStatus = SQL_ERROR Then
	  MessageBox("Error Freeing Environment From ODBC Drivers", "Error", 0)
     End If
   Else
     '2.1 Get Drivers
    Dim dir as Integer
    Dim descrip as STRING * MAX_DATA_BUFFER
    Dim descriplen as Integer
    Dim attrib as STRING * MAX_DATA_BUFFER
    Dim attriblen as Integer

    dir=2'First
    SQLRet=0
    qODBC.DriverCount=0
    While SQLRet=SQL_SUCCESS
     If SQLRet=SQL_SUCCESS then qODBC.DriverCount++
     SQLRet=SQLDrivers(qODBC.glEnv, dir, VARPTR(descrip),MAX_DATA_BUFFER, VARPTR(descriplen), VARPTR(attrib), MAX_DATA_BUFFER, VARPTR(attriblen))
     dir =1'Next
     qODBC.Driver(qODBC.DriverCount)=Left$(descrip,descriplen)
    Wend
   End If 
  End if
 End Sub

 Sub Connect (sConn as String)
   qODBC.sConnect=sConn
  'Connect using the sConnect string - SQLDriverConnect
   DIM sResult As String * 256
   DIM iSize As Integer
  
   SQLRet = SQLDriverConnect(qODBC.glDbc, 0&, VARPTR(qODBC.sConnect), Len(qODBC.sConnect), VARPTR(sResult), 255, VARPTR(iSize), 1)
      If SQLRet < 0 Then
    MessageBox("Could not establish connection to ODBC driver!", "Error", 0)
   Else
    '4. Allocate ODBC Statement Handle
    SQLRet=SQLAllocStmt(qODBC.glDbc, VARPTR(qODBC.glStmt))
    If SQLRet<> SQL_SUCCESS Then
      MessageBox("Could not allocate memory for a statement handle!", "Error", 0)
    Else
     '4.1 Get tables
     Dim tPerform As Long
     Dim catalog As String * 0
     Dim schema As String * 0
     Dim tablename As String * 0
     Dim tabletype As String * 5
     Dim iTable as integer
     Dim tData As String * MAX_DATA_BUFFER
     Dim tOutLen As Long

     qODBC.TableCount=0
     tabletype="TABLE"
     SQLRet=SQLTables(qODBC.glStmt, VARPTR(catalog), 0, VARPTR(schema),0, VARPTR(tablename),0, VARPTR(tabletype),5)
     If SQLRet<> SQL_SUCCESS Then
      MessageBox("Could not get Tables!", "Error", 0)
     Else
      tPerform = SQL_SUCCESS
      While tPerform = SQL_SUCCESS
       tPerform = SQLFetch(qODBC.glStmt)		' Get the next row of data
       If tPerform = 65535 or tPerform = SQL_ERROR then 
           Exit While  
       Else
	  If tPerform = SQL_SUCCESS or tPerform = SQL_SUCCESS_WITH_INFO THEN
    	    qODBC.TableCount++
	    For iTable = 1 to 3
            iStatus = SQLGetData(qODBC.glStmt, iTable, 1, VARPTR(tData), MAX_DATA_BUFFER, VARPTR(tOutLen))
          Next
          qODBC.Table(qODBC.TableCount) = Left$(tData, tOutlen) ' tOutlen = -1 if no data or Null data
	  End If
       End If
      Wend
      'close cursor of tables query
      tPerform = SQLCloseCursor(qODBC.glStmt)
     End If 
    End If
   End If
 End Sub
 
 Sub Query(sSQL as string)
  '5. Execute ODBC Statement - SQLExecDirect
    qODBC.sSQL=sSQL
    Dim lRet As Long, lErrNo As Long
    Dim iLen As Integer
    Dim sSQLState As String * MAX_DATA_BUFFER
    Dim sErrorMsg As String * MAX_DATA_BUFFER
    Dim sMsg As String


    qODBC.SQLRet=SQLExecDirect(qODBC.glStmt, VARPTR(qODBC.sSQL), Len(qODBC.sSQL))
    If qODBC.SQLRet <> SQL_SUCCESS and qODBC.SQLRet <> SQL_SUCCESS_WITH_INFO Then
        'Also Check for ODBC Error message - SQLError
        lRet = SQLError(glEnv, gldbc, glStmt, VARPTR(sSQLState), VARPTR(lErrNo), VARPTR(sErrorMsg), MAX_DATA_BUFFER, VARPTR(iLen))
        sMsg = "Error Executing SQL Statement" & Chr$(13) & Chr$(10)
        sMsg = sMsg & "ODBC State = " & Trim$(Left$(sSQLState, InStr(sSQLState, Chr$(0)) - 1)) & Chr$(13) & Chr$(10)
        sMsg = sMsg & "ODBC Error Message = " & Left$(sErrorMsg, iLen)
        MessageBox(sMsg, "Error", 0)
    End If
    
    Dim bPerform As Long
    Dim NumCols As Integer
    
    'Get number of columns
    bPerform = SQLNumResultCols (qODBC.glStmt, VARPTR(NumCols))
    If bPerform <> SQL_SUCCESS Then
        MessageBox("Could not get columns quantity!", "Error", 0)
        End
    Else
        qODBC.FieldCount= NumCols
    End if

    'Get column descriptor
    Dim icolnum as Integer
    Dim colname as String * MAX_DATA_BUFFER
    Dim colnamelen as Integer
    Dim dtype as Integer
    Dim colsize as Long
    Dim decdigits as Integer
    Dim nullav as Integer

    For icolnum = 1 to qODBC.FieldCount
      bPerform = SQLDescribeCol(qODBC.glStmt, icolnum, VARPTR(colname), MAX_DATA_BUFFER, VARPTR(colnamelen), VARPTR(dtype), VARPTR(colsize), VARPTR(decdigits), VARPTR(nullav))
      If bPerform <> SQL_SUCCESS Then
        MessageBox("Could not get column descriptor!", "Error", 0)
      Else
    	 Select case dtype
    	   case 1
		tipo$= "CHAR"
	   case 2
		tipo$=" NUMERIC"
	   case 3
		tipo$="DECIMAL"
	   case 4
		tipo$="INTEGER"
	   case 5
		tipo$="SMALLINT"
	   case 6
		tipo$="FLOAT"
	   case 7
		tipo$="REAL"
	   case 8
		tipo$="DOUBLE"
	   case 9
		tipo$="DATE"
	   case 10
		tipo$="TIME"
	   case 11
		tipo$="TIMESTAMP"
	   case 12
		tipo$="VARCHAR"
	   case 65535
		tipo$="LONGVARCHAR"
	   case 65534
		tipo$="BINARY"
	   case 65533
		tipo$="VARBINARY"
	   case 65532
		tipo$="LONGVARBINARY"
	   case 65531
		tipo$="BIGINT"
	   case 65530
		tipo$="TINYINT"
	   case 65529
		tipo$="BIT"
	   case 65456
		tipo$="TYPE_DRIVER_START"
	   case else
		tipo$="UNKNOWN TYPE"
	 End select
        qODBC.Field.Name(icolnum)=Left$(colname,colnamelen)
        qODBC.Field.TypeNum(icolnum)=dtype
        qODBC.Field.TypeStr(icolnum)=tipo$
        qODBC.Field.TypeNum(icolnum)=dtype
        qODBC.Field.Size(icolnum)=colsize
        qODBC.Field.DecDigits(icolnum)=decdigits
        qODBC.Field.Nullav(icolnum)=nullav
     End if
    Next
  End Sub
  
  Sub CloseQuery
    'Close cursor of query
    bPerform = SQLCloseCursor(qODBC.glStmt)
  End Sub
 
  Function GetRecord as Integer
   '6. Fetch one row of results from executed ODBC Statement - SQLFetch
    'Code in Step 7.

    '7. Get the Data in each field of the Fetched row - SQLGetData

    Dim bPerform As Long
    Dim iColumn as Integer
    Dim sData As String * MAX_DATA_BUFFER
    Dim lOutLen As Long
    Dim campo As String
    Dim iStatus as Long

    bPerform = SQLFetch(qODBC.glStmt)		' Get the next row of data
    If bPerform = 65535 or bPerform = SQL_ERROR then 
      Result= 0
    Else
     If bPerform = SQL_SUCCESS or bPerform = SQL_SUCCESS_WITH_INFO THEN
       Result = 1
    	 For iColumn = 1 to qODBC.FieldCount
          iStatus = SQLGetData(qODBC.glStmt, iColumn, 1, VARPTR(sData), MAX_DATA_BUFFER, VARPTR(lOutLen))
            ' lOutlen = length of the valid data in sData
        	campo = Left$(sData, lOutlen) ' lOutlen = -1 if no data or Null data
             ' Add the Field Data to Correponding Data Display Controls for this row
            qODBC.Field.Data(iColumn)= campo
       Next
      Else
        Result= 0
     	End If
     End If
  End Function

  Sub CloseDB
    Dim bPerform As Short
    Dim iStatus As Short

    'Release the ODBC Statement Handle
    bPerform = SQLFreeStmt(qODBC.glStmt, SQL_DROP)

    '8. Release the ODBC Statement Handle - SQLFreeSTmt
    'Code in Step 7.
    '***********************************************************************
    'The steps 9 - 11 are for Disconnecting from the SQL Server DataBase
    '***********************************************************************
    '9. Disconnect from ODBC Database - SQLDisconnect
    iStatus = SQLDisconnect(qODBC.glDbc)
  End Sub 
  
  Sub CloseODBC
    Dim iStatus As Short
    '10. Release the ODBC Database Handle - SQLFreeConnect
    iStatus = SQLFreeConnect(qODBC.glDbc)

    '11. Release the ODBC Environment Handle - SQLFreeEnv
    iStatus = SQLFreeEnv(qODBC.glEnv)
  End Sub
End Type


Dim myDB as qODBC
Dim sSQL as String

myDB.ODBCInit
PRINT "There are = ";myDB.DriverCount;" Installed Drivers"
FOR I = 1 TO myDB.DriverCount
   PRINT i;":";myDB.Driver(I)
NEXT
' Change the Driver and Database information in your Connexion String
sCon$ = "DRIVER={Microsoft Access Driver (*.mdb)};DBQ=adodemo.mdb;PWD=;UID=admin;"
myDB.Connect(sCon$)
PRINT "There are =";myDB.TableCount;" Tables"
FOR I = 1 TO myDB.TableCount
   PRINT i;": ";myDB.Table(I)
   q$="select * from "+ myDB.Table(I)
   myDB.Query(q$)
   Print " There are ";myDB.FieldCount;" Columns"
   For y=1 to myDB.FieldCount
       PRINT "  Field ";y;" :";myDB.Field.Name(y)
       PRINT "   TypeNum : ";myDB.Field.TypeNum(y)
       PRINT "   TypeStr : ";myDB.Field.TypeStr(y)
       PRINT "   TypeNum : ";myDB.Field.Size(y)
       PRINT "   DecDigits : ";myDB.Field.DecDigits(y)
       PRINT "   NullAvail : ";myDB.Field.Nullav(y)
   Next
   myDB.CloseQuery
NEXT

' Change Query String to fit your needs, here are some examples
'  sSQL = "insert into nummesa (nummesa) values("+STR$(MESA&[IP])+")"
sSQL = "select codigo,monto_contrato from obras where monto_contrato > 40 order by monto_contrato"
'  sSQL = "create table datos (LE CHAR(10) NULL, LM INTEGER, MONTO NUMERIC)"

myDB.Query(sSQL)
Print " There are ";myDB.FieldCount;" Columns"
For y=1 to myDB.FieldCount
   PRINT myDB.Field.Name(y);" ";
Next
PRINT
I=1
WHILE myDB.GetRecord = 1
 PRINT I;" : ";
 For y=1 to myDB.FieldCount
     PRINT myDB.Field.Data(y);" ";
 Next
 print
 I++
WEND
myDB.CloseQuery   
myDB.CloseDB
myDB.CloseODBC
PRINT "FINISH"

End



