20000724-22:45 GMT+2 Maurilio Longo <maurilio.longo@libero.it>

This commit is contained in:
Maurilio Longo
2000-07-24 20:49:27 +00:00
parent d407f71aa5
commit 06fbb5b23b
10 changed files with 1940 additions and 1 deletions

View File

@@ -1,3 +1,19 @@
20000724-22:45 GMT+2 Maurilio Longo <maurilio.longo@libero.it>
+ contrib/mysql
+ contrib/mysql/mysql.c
+ contrib/mysql/mysql.h
+ contrib/mysql/mysql_com.h
+ contrib/mysql/mysql_version.h
+ contrib/mysql/mysql.ch
+ contrib/mysql/tmysql.prg
+ contrib/mysql/dbf2mysql.prg
+ contrib/mysql/test.prg
+ contrib/mysql/readme.txt
I've ported mSQL access classes to MySQL. There is a lot of work to do to finish them, but, nonetheless,
in their present state I've been able to use them to port to MySQL a couple of programs written for
mSQL.
2000-07-24 22:28 UTC+0100 Victor Szakats <info@szelvesz.hu>
* rdd/dbcmd.c
@@ -18,7 +34,7 @@
* source/rtl/errorapi.c
* source/rtl/tobject.prg
* source/vm/classes.c
* TOBJECT_ERROR() implemented in Clipper/Harbour instead of C to keep it
* TOBJECT_ERROR() implemented in Clipper/Harbour instead of C to keep it
clean.
+ __errRT_SBASE() internal function added to throw a substitutable (?)
runtime error.

View File

@@ -0,0 +1,168 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* dbf2mysql.prg - converts a .dbf file into a MySQL table
*
* 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 of the License, or
* (at your option) any later version, with one exception:
*
* The exception is that if you link the Harbour Runtime Library (HRL)
* and/or the Harbour Virtual Machine (HVM) 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 HRL
* and/or HVM code into it.
*
* 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 program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
* their web site at http://www.gnu.org/).
*
*/
#include "inkey.ch"
procedure main(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14)
local cTok, nTok := 1
local cHostName := "localhost"
local cUser := "root"
local cPassWord := ""
local cDataBase, cTable, cFile
local aDbfStruct, i
local lCreateTable := .F.
local oServer, oTable, oRecord
SET CENTURY ON
SET EPOCH TO 1960
if PCount() < 6
help()
quit
endif
i := 1
// Scan parameters and setup workings
while (i <= PCount())
cTok := hb_PValue(i++)
do case
case cTok == "-h"
cHostName := hb_PValue(i++)
case cTok == "-d"
cDataBase := hb_PValue(i++)
case cTok == "-t"
cTable := hb_PValue(i++)
case cTok == "-f"
cFile := hb_PValue(i++)
case cTok == "-u"
cUser := hb_PValue(i++)
case cTok == "-p"
cPassWord := hb_PValue(i++)
case cTok == "-c"
lCreateTable := .T.
otherwise
help()
quit
endcase
enddo
dbUseArea(.T.,, cFile, "dbffile",, .T.)
aDbfStruct := dbffile->(dbStruct())
oServer := TMySQLServer():New(cHostName, cUser, cPassWord)
if oServer:NetErr()
? oServer:Error()
quit
endif
oServer:SelectDB(cDataBase)
if oServer:NetErr()
? oServer:Error()
quit
endif
if lCreateTable
if Ascan(oServer:ListTables(), cTable) > 0
oServer:DeleteTable(cTable)
if oServer:NetErr()
? oServer:Error()
quit
endif
endif
oServer:CreateTable(cTable, aDbfStruct)
if oServer:NetErr()
? oServer:Error()
quit
endif
endif
// Initialize MySQL table
oTable := oServer:Query("SELECT * FROM " + cTable + " LIMIT 1")
if oTable:NetErr()
Alert(oTable:Error())
quit
endif
while !dbffile->(eof()) .AND. Inkey() <> K_ESC
oRecord := oTable:GetBlankRow()
for i := 1 to dbffile->(FCount())
oRecord:FieldPut(i, dbffile->(FieldGet(i)))
next
oTable:Append(oRecord)
if oTable:NetErr()
Alert(oTable:Error())
endif
dbffile->(dbSkip())
DevPos(Row(), 1)
if (dbffile->(RecNo()) % 100) == 0
DevOut("imported recs: " + Str(dbffile->(RecNo())))
endif
enddo
dbffile->(dbCloseArea())
oTable:Destroy()
oServer:Destroy()
return
procedure Help()
? "dbf2MySQL - dbf file to MySQL table conversion utility"
? "-h hostname (default: localhost)"
? "-u user (default: root)"
? "-p password (default no password)"
? "-d name of database to use"
? "-t name of table to add records to"
? "-c delete existing table and create a new one"
? "-f name of .dbf file to import"
? "all parameters but -h -u -p -c are mandatory"
? ""
return

View File

@@ -0,0 +1,268 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* MySQL DBMS low level (client api) interface code.
*
* 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 of the License, or
* (at your option) any later version, with one exception:
*
* The exception is that if you link the Harbour Runtime Library (HRL)
* and/or the Harbour Virtual Machine (HVM) 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 HRL
* and/or HVM code into it.
*
* 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 program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
* their web site at http://www.gnu.org/).
*
*/
/* NOTE: we need this to prevent base types redefinition */
#define _CLIPDEFS_H
#include "extend.api"
#include "item.api"
#include "mysql.h"
/* NOTE: OS/2 EMX port of MySQL needs libmysqlclient.a from 3.21.33b build which has st and mt
versions of client library. I'm using ST version since harbour is single threaded. You need
also .h files from same distribution
*/
HB_FUNC(SQLCONNECT) // MYSQL *mysql_real_connect(MYSQL*, char * host, char * user, char * password, char * db, uint port, char *, uint flags)
{
MYSQL * mysql = NULL;
mysql = mysql_real_connect(NULL, _parc(1), _parc(2), _parc(3), 0, NULL, 0);
_retnl((long) mysql);
}
HB_FUNC(SQLCLOSE) // void mysql_close(MYSQL *mysql)
{
mysql_close((MYSQL *)_parnl(1));
_ret();
}
HB_FUNC(SQLSELECTD) // int mysql_select_db(MYSQL *, char *)
{
_retnl(mysql_select_db((MYSQL *)_parnl(1), _parc(2)));
}
HB_FUNC(SQLQUERY) // int mysql_query(MYSQL *, char *)
{
_retnl((long) mysql_query((MYSQL *)_parnl(1), _parc(2)));
}
HB_FUNC(SQLSTORER) // MYSQL_RES *mysql_store_result(MYSQL *)
{
_retnl((long) mysql_store_result((MYSQL *)_parnl(1)));
}
HB_FUNC(SQLFREER) // void mysql_free_result(MYSQL_RES *)
{
mysql_free_result((MYSQL_RES *)_parnl(1));
_ret();
}
HB_FUNC(SQLFETCHR) // MYSQL_ROW *mysql_fetch_row(MYSQL_RES *)
{
MYSQL_RES *mresult = (MYSQL_RES *)_parnl(1);
int num_fields = mysql_num_fields(mresult);
ITEM aRow = _itemArrayNew(num_fields);
ITEM temp;
MYSQL_ROW mrow;
int i;
mrow = mysql_fetch_row(mresult);
for (i = 0; i < num_fields; i++) {
/* if field is not empty */
if (mrow[i] != NULL) {
temp = _itemPutC(NULL, mrow[i]);
} else {
temp = _itemPutC(NULL, "");
}
_itemArrayPut(aRow, i + 1, temp);
_itemRelease(temp);
}
_itemReturn(aRow);
_itemRelease(aRow);
}
HB_FUNC(SQLDATAS) // void mysql_data_seek(MYSQL_RES *, unsigned int)
{
mysql_data_seek((MYSQL_RES *)_parnl(1), (unsigned int)_parni(2));
_ret();
}
HB_FUNC(SQLNROWS) // my_ulongulong mysql_num_rows(MYSQL_RES *)
{
/* NOTE: I receive a my_ulongulong which I convert to a long, so I could lose precision */
_retnl((long)mysql_num_rows(((MYSQL_RES *)_parnl(1))));
}
HB_FUNC(SQLFETCHF) // MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *)
{
/* NOTE: field structure of MySQL has 8 members as of MySQL 3.22.x */
ITEM aField = _itemArrayNew(8);
ITEM temp;
MYSQL_FIELD *mfield;
mfield = mysql_fetch_field((MYSQL_RES *)_parnl(1));
if (!(mfield == NULL)) {
temp = _itemPutC(NULL, mfield->name);
_itemArrayPut(aField, 1, temp);
_itemRelease(temp);
temp = _itemPutC(NULL, mfield->table);
_itemArrayPut(aField, 2, temp);
_itemRelease(temp);
temp = _itemPutC(NULL, mfield->def);
_itemArrayPut(aField, 3, temp);
_itemRelease(temp);
temp = _itemPutNL(NULL, (long)mfield->type);
_itemArrayPut(aField, 4, temp);
_itemRelease(temp);
temp = _itemPutNL(NULL, mfield->length);
_itemArrayPut(aField, 5, temp);
_itemRelease(temp);
temp = _itemPutNL(NULL, mfield->max_length);
_itemArrayPut(aField, 6, temp);
_itemRelease(temp);
temp = _itemPutNL(NULL, mfield->flags);
_itemArrayPut(aField, 7, temp);
_itemRelease(temp);
temp = _itemPutNL(NULL, mfield->decimals);
_itemArrayPut(aField, 8, temp);
_itemRelease(temp);
}
_itemReturn(aField);
_itemRelease(aField);
}
HB_FUNC(SQLFSEEK) // MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *, MYSQL_FIELD_OFFSET)
{
mysql_field_seek((MYSQL_RES *)_parnl(1), (MYSQL_FIELD_OFFSET)_parni(2));
_ret();
}
HB_FUNC(SQLNUMFI) // unsigned int mysql_num_fields(MYSQL_RES *)
{
_retnl(mysql_num_fields(((MYSQL_RES *)_parnl(1))));
}
HB_FUNC(SQLLISTF) // MYSQL_RES *mysql_list_fields(MYSQL *, char *);
{
_retnl((long) mysql_list_fields((MYSQL *)_parnl(1), _parc(2), NULL));
}
HB_FUNC(SQLGETERR) // char *mysql_error(MYSQL *);
{
_retc(mysql_error((MYSQL *)_parnl(1)));
}
HB_FUNC(SQLLISTDB) // MYSQL_RES * mysql_list_dbs(MYSQL *, char * wild);
{
MYSQL * mysql = (MYSQL *)_parnl(1);
MYSQL_RES * mresult;
MYSQL_ROW mrow;
long nr, i;
ITEM aDBs;
ITEM temp;
mresult = mysql_list_dbs(mysql, NULL);
nr = mysql_num_rows(mresult);
aDBs = _itemArrayNew(nr);
for (i = 0; i < nr; i++) {
mrow = mysql_fetch_row(mresult);
temp = _itemPutC(NULL, mrow[0]);
_itemArrayPut(aDBs, i + 1, temp);
_itemRelease(temp);
}
mysql_free_result(mresult);
_itemReturn(aDBs);
_itemRelease(aDBs);
}
HB_FUNC(SQLLISTTBL) // MYSQL_RES * mysql_list_tables(MYSQL *, char * wild);
{
MYSQL * mysql = (MYSQL *)_parnl(1);
MYSQL_RES * mresult;
MYSQL_ROW mrow;
long nr, i;
ITEM aTables;
ITEM temp;
mresult = mysql_list_tables(mysql, NULL);
nr = mysql_num_rows(mresult);
aTables = _itemArrayNew(nr);
for (i = 0; i < nr; i++) {
mrow = mysql_fetch_row(mresult);
temp = _itemPutC(NULL, mrow[0]);
_itemArrayPut(aTables, i + 1, temp);
_itemRelease(temp);
}
mysql_free_result(mresult);
_itemReturn(aTables);
_itemRelease(aTables);
}
// returns bitwise and of first parameter with second
HB_FUNC(SQLAND)
{
_retnl(_parnl(1) & _parnl(2));
}

View File

@@ -0,0 +1,102 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* MySQL DBMS defines
* These defines are clipper code level equivalent of mysql.h and mysql_com.h
*
* 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 of the License, or
* (at your option) any later version, with one exception:
*
* The exception is that if you link the Harbour Runtime Library (HRL)
* and/or the Harbour Virtual Machine (HVM) 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 HRL
* and/or HVM code into it.
*
* 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 program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
* their web site at http://www.gnu.org/).
*
*/
// MySQL field types
#define MYSQL_DECIMAL_TYPE 0
// NOTE: TINY is used to map clipper logical values to MySQL tables, so 0 == .F., 1 == .T.
#define MYSQL_TINY_TYPE 1
#define MYSQL_SHORT_TYPE 2
#define MYSQL_LONG_TYPE 3
#define MYSQL_FLOAT_TYPE 4
#define MYSQL_DOUBLE_TYPE 5
#define MYSQL_NULL_TYPE 6
#define MYSQL_TIMESTAMP_TYPE 7
#define MYSQL_LONGLONG_TYPE 8
#define MYSQL_INT24_TYPE 9
#define MYSQL_DATE_TYPE 10
#define MYSQL_TIME_TYPE 11
#define MYSQL_DATETIME_TYPE 12
#define MYSQL_YEAR_TYPE 13
#define MYSQL_NEWDATE_TYPE 14
#define MYSQL_ENUMTYPE 247
#define MYSQL_SET_TYPE 248
#define MYSQL_TINY_BLOB_TYPE 249
#define MYSQL_MEDIUM_BLOB_TYPE 250
#define MYSQL_LONG_BLOB_TYPE 251
#define MYSQL_BLOB_TYPE 252
#define MYSQL_VAR_STRING_TYPE 253
#define MYSQL_STRING_TYPE 254
// MySQL field structure item number (C level structure is translated
// to a clipper array)
#define MYSQL_FS_NAME 1 /* Name of column */
#define MYSQL_FS_TABLE 2 /* Table of column if column was a field */
#define MYSQL_FS_DEF 3 /* Default value (set by mysql_list_fields) */
#define MYSQL_FS_TYPE 4 /* Type of field. Se mysql_com.h for types */
#define MYSQL_FS_LENGTH 5 /* Width of column */
#define MYSQL_FS_MAXLEN 6 /* Max width of selected set */
#define MYSQL_FS_FLAGS 7 /* Div flags */
#define MYSQL_FS_DECIMALS 8 /* Number of decimals in field */
// MySQL field flags
#define NOT_NULL_FLAG 1 /* Field can't be NULL */
#define PRI_KEY_FLAG 2 /* Field is part of a primary key */
#define UNIQUE_KEY_FLAG 4 /* Field is part of a unique key */
#define MULTIPLE_KEY_FLAG 8 /* Field is part of a key */
#define BLOB_FLAG 16 /* Field is a blob */
#define UNSIGNED_FLAG 32 /* Field is unsigned */
#define ZEROFILL_FLAG 64 /* Field is zerofill */
#define BINARY_FLAG 128
/* The following are only sent to new clients */
#define ENUM_FLAG 256 /* field is an enum */
#define AUTO_INCREMENT_FLAG 512 /* field is a autoincrement field */
#define TIMESTAMP_FLAG 1024 /* Field is a timestamp */
#define PART_KEY_FLAG 16384 /* Intern; Part of some key */
#define GROUP_FLAG 32768 /* Intern group field */
// Extension to DBS_xxx defines to encompass NOT NULL fields, needed by indexes
#define DBS_NOTNULL 5 /* True if field has to be NOT NULL */

View File

@@ -0,0 +1,205 @@
/*
* $Id$
*/
/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
This file is public domain and comes with NO WARRANTY of any kind */
/* defines for libmysql */
#ifndef _mysql_h
#define _mysql_h
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _global_h /* If not standard header */
#include <sys/types.h>
typedef char my_bool;
#if !defined(__WIN32__) && !defined(WIN32)
#define STDCALL
typedef char byte;
#else
#define STDCALL __stdcall
#endif
typedef char * gptr;
#ifndef ST_USED_MEM_DEFINED
#define ST_USED_MEM_DEFINED
typedef struct st_used_mem { /* struct for once_alloc */
struct st_used_mem *next; /* Next block in use */
unsigned int left; /* memory left in block */
unsigned int size; /* size of block */
} USED_MEM;
typedef struct st_mem_root {
USED_MEM *free;
USED_MEM *used;
unsigned int min_malloc;
unsigned int block_size;
void (*error_handler)(void);
} MEM_ROOT;
#endif
#ifndef Socket_defined
#ifdef __WIN32__
#define Socket SOCKET
#else
typedef int Socket;
#endif
#endif
#endif
#include "mysql_com.h"
#include "mysql_version.h"
extern unsigned int mysql_port;
extern char *mysql_unix_port;
#define IS_PRI_KEY(n) ((n) & PRI_KEY_FLAG)
#define IS_NOT_NULL(n) ((n) & NOT_NULL_FLAG)
#define IS_BLOB(n) ((n) & BLOB_FLAG)
#define IS_NUM(t) ((t) <= FIELD_TYPE_INT24 || (t) == FIELD_TYPE_YEAR)
typedef struct st_mysql_field {
char *name; /* Name of column */
char *table; /* Table of column if column was a field */
char *def; /* Default value (set by mysql_list_fields) */
enum enum_field_types type; /* Type of field. Se mysql_com.h for types */
unsigned int length; /* Width of column */
unsigned int max_length; /* Max width of selected set */
unsigned int flags; /* Div flags */
unsigned int decimals; /* Number of decimals in field */
} MYSQL_FIELD;
typedef byte **MYSQL_ROW; /* return data as array of strings */
typedef unsigned int MYSQL_FIELD_OFFSET; /* offset to current field */
typedef struct st_mysql_rows {
struct st_mysql_rows *next; /* list of rows */
MYSQL_ROW data;
} MYSQL_ROWS;
typedef MYSQL_ROWS *MYSQL_ROW_OFFSET; /* offset to current row */
typedef struct st_mysql_data {
unsigned int rows;
unsigned int fields;
MYSQL_ROWS *data;
MEM_ROOT alloc;
} MYSQL_DATA;
enum mysql_status { MYSQL_STATUS_READY,MYSQL_STATUS_GET_RESULT,
MYSQL_STATUS_USE_RESULT};
typedef struct st_mysql {
NET net; /* Communication parameters */
char *host,*user,*passwd,*unix_socket,*server_version,*host_info,
*info,*db;
unsigned int port,client_flag,server_capabilities;
unsigned int protocol_version;
unsigned int field_count;
unsigned long thread_id; /* Id for connection in server */
unsigned long affected_rows;
unsigned long insert_id; /* id if insert on table with NEXTNR */
unsigned long extra_info; /* Used by mysqlshow */
enum mysql_status status;
MYSQL_FIELD *fields;
MEM_ROOT field_alloc;
my_bool free_me; /* If free in mysql_close */
my_bool reconnect; /* set to 1 if automatic reconnect */
} MYSQL;
typedef struct st_mysql_res {
unsigned long row_count;
unsigned int field_count, current_field;
MYSQL_FIELD *fields;
MYSQL_DATA *data;
MYSQL_ROWS *data_cursor;
MEM_ROOT field_alloc;
MYSQL_ROW row; /* If unbuffered read */
MYSQL_ROW current_row; /* buffer to current row */
unsigned int *lengths; /* column lengths of current row */
MYSQL *handle; /* for unbuffered reads */
my_bool eof; /* Used my mysql_fetch_row */
} MYSQL_RES;
#define mysql_num_rows(res) (res)->row_count
#define mysql_num_fields(res) (res)->field_count
#define mysql_eof(res) (res)->eof
#define mysql_fetch_field_direct(res,fieldnr) ((res)->fields[fieldnr])
#define mysql_fetch_fields(res) (res)->fields
#define mysql_row_tell(res) (res)->data_cursor
#define mysql_field_tell(res) (res)->current_field
#define mysql_affected_rows(mysql) (mysql)->affected_rows
#define mysql_insert_id(mysql) (mysql)->insert_id
#define mysql_error(mysql) (mysql)->net.last_error
#define mysql_errno(mysql) (mysql)->net.last_errno
#define mysql_info(mysql) (mysql)->info
#define mysql_reload(mysql) mysql_refresh((mysql),REFRESH_GRANT)
#define mysql_thread_id(mysql) (mysql)->thread_id
/* void STDCALL mysql_init(MYSQL *mysql); */
MYSQL * STDCALL mysql_connect(MYSQL *mysql, const char *host,
const char *user, const char *passwd);
#if MYSQL_VERSION_ID >= 32200
MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
const char *user,
const char *passwd,
const char *db,
unsigned int port,
const char *unix_socket,
unsigned int clientflag);
#else
MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
const char *user,
const char *passwd,
unsigned int port,
const char *unix_socket,
unsigned int clientflag);
#endif
void STDCALL mysql_close(MYSQL *sock);
int STDCALL mysql_select_db(MYSQL *mysql, const char *db);
int STDCALL mysql_query(MYSQL *mysql, const char *q);
int STDCALL mysql_real_query(MYSQL *mysql, const char *q,
unsigned int length);
int STDCALL mysql_create_db(MYSQL *mysql, const char *DB);
int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB);
int STDCALL mysql_shutdown(MYSQL *mysql);
int STDCALL mysql_dump_debug_info(MYSQL *mysql);
int STDCALL mysql_refresh(MYSQL *mysql,
unsigned int refresh_options);
int STDCALL mysql_kill(MYSQL *mysql,unsigned long pid);
char * STDCALL mysql_stat(MYSQL *mysql);
char * STDCALL mysql_get_server_info(MYSQL *mysql);
char * STDCALL mysql_get_client_info(void);
char * STDCALL mysql_get_host_info(MYSQL *mysql);
unsigned int STDCALL mysql_get_proto_info(MYSQL *mysql);
MYSQL_RES * STDCALL mysql_list_dbs(MYSQL *mysql,const char *wild);
MYSQL_RES * STDCALL mysql_list_tables(MYSQL *mysql,const char *wild);
MYSQL_RES * STDCALL mysql_list_fields(MYSQL *mysql, const char *table,
const char *wild);
MYSQL_RES * STDCALL mysql_list_processes(MYSQL *mysql);
MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql);
MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql);
void STDCALL mysql_free_result(MYSQL_RES *result);
void STDCALL mysql_data_seek(MYSQL_RES *mysql,unsigned int offset);
MYSQL_ROW_OFFSET STDCALL mysql_row_seek(MYSQL_RES *mysql, MYSQL_ROW_OFFSET);
MYSQL_FIELD_OFFSET STDCALL mysql_field_seek(MYSQL_RES *mysql,
MYSQL_FIELD_OFFSET offset);
MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *mysql);
unsigned int * STDCALL mysql_fetch_lengths(MYSQL_RES *mysql);
MYSQL_FIELD * STDCALL mysql_fetch_field(MYSQL_RES *handle);
unsigned int STDCALL mysql_escape_string(char *to,const char *from,
unsigned int from_length);
void STDCALL mysql_debug(char *debug);
/* new api functions */
#define HAVE_MYSQL_REAL_CONNECT
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,160 @@
/*
* $Id$
*/
/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
This file is public domain and comes with NO WARRANTY of any kind */
/*
** Common definition between mysql server & client
*/
#ifndef _mysql_com_h
#define _mysql_com_h
#ifdef __cplusplus
extern "C" {
#endif
#define NAME_LEN 64 /* Field/table name length */
#define LOCAL_HOST "localhost"
#define MYSQL_PORT 3306 /* Alloced by ISI for MySQL */
#define MYSQL_UNIX_ADDR "\\socket\\mysql.sock"
enum enum_server_command {COM_SLEEP,COM_QUIT,COM_INIT_DB,COM_QUERY,
COM_FIELD_LIST,COM_CREATE_DB,COM_DROP_DB,COM_REFRESH,
COM_SHUTDOWN,COM_STATISTICS,
COM_PROCESS_INFO,COM_CONNECT,COM_PROCESS_KILL,
COM_DEBUG};
#define NOT_NULL_FLAG 1 /* Field can't be NULL */
#define PRI_KEY_FLAG 2 /* Field is part of a primary key */
#define UNIQUE_KEY_FLAG 4 /* Field is part of a unique key */
#define MULTIPLE_KEY_FLAG 8 /* Field is part of a key */
#define BLOB_FLAG 16 /* Field is a blob */
#define UNSIGNED_FLAG 32 /* Field is unsigned */
#define ZEROFILL_FLAG 64 /* Field is zerofill */
#define BINARY_FLAG 128
/* The following are only sent to new clients */
#define ENUM_FLAG 256 /* field is an enum */
#define AUTO_INCREMENT_FLAG 512 /* field is a autoincrement field */
#define TIMESTAMP_FLAG 1024 /* Field is a timestamp */
#define PART_KEY_FLAG 16384 /* Intern; Part of some key */
#define GROUP_FLAG 32768 /* Intern group field */
#define REFRESH_GRANT 1 /* Refresh grant tables */
#define REFRESH_LOG 2 /* Start on new log file */
#define REFRESH_TABLES 4 /* close all tables */
#define REFRESH_HOSTS 8 /* Flush host cache */
#define REFRESH_FAST 32768 /* Intern flag */
#define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */
#define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */
#define CLIENT_LONG_FLAG 4 /* Get all column flags */
#define CLIENT_CONNECT_WITH_DB 8 /* One can specify db on connect */
#define CLIENT_NO_SCHEMA 16 /* Don't allow database.table.column */
#define MYSQL_ERRMSG_SIZE 200
#define NET_READ_TIMEOUT 30 /* Timeout on read */
#define NET_WRITE_TIMEOUT 60 /* Timeout on write */
#define NET_WAIT_TIMEOUT 8*60*60 /* Wait for new query */
typedef struct st_net {
Socket fd;
int fcntl;
unsigned char *buff,*buff_end,*write_pos;
char last_error[MYSQL_ERRMSG_SIZE];
unsigned int last_errno,max_packet,timeout,pkt_nr;
my_bool error,return_errno;
} NET;
#define packet_error ((unsigned int) -1)
enum enum_field_types { FIELD_TYPE_DECIMAL, FIELD_TYPE_TINY,
FIELD_TYPE_SHORT, FIELD_TYPE_LONG,
FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE,
FIELD_TYPE_NULL, FIELD_TYPE_TIMESTAMP,
FIELD_TYPE_LONGLONG,FIELD_TYPE_INT24,
FIELD_TYPE_DATE, FIELD_TYPE_TIME,
FIELD_TYPE_DATETIME, FIELD_TYPE_YEAR,
FIELD_TYPE_NEWDATE,
FIELD_TYPE_ENUM=247,
FIELD_TYPE_SET=248,
FIELD_TYPE_TINY_BLOB=249,
FIELD_TYPE_MEDIUM_BLOB=250,
FIELD_TYPE_LONG_BLOB=251,
FIELD_TYPE_BLOB=252,
FIELD_TYPE_VAR_STRING=253,
FIELD_TYPE_STRING=254
};
#define FIELD_TYPE_CHAR FIELD_TYPE_TINY /* For compability */
#define FIELD_TYPE_INTERVAL FIELD_TYPE_ENUM /* For compability */
extern unsigned long max_allowed_packet;
extern unsigned long net_buffer_length;
#define net_new_transaction(net) ((net)->pkt_nr=0)
int my_net_init(NET *net,Socket fd);
void net_end(NET *net);
void net_clear(NET *net);
int net_flush(NET *net);
int my_net_write(NET *net,const byte *packet,unsigned int len);
int net_write_command(NET *net,unsigned char command,const byte *packet,
unsigned int len);
int net_real_write(NET *net,const byte *packet,unsigned int len);
unsigned int my_net_read(NET *net);
struct rand_struct {
unsigned long seed,seed2,max_value;
double max_value_dbl;
};
/* The following is for user defined functions */
enum Item_result {STRING_RESULT,REAL_RESULT,INT_RESULT};
typedef struct st_udf_args
{
unsigned int arg_count; /* Number of arguments */
enum Item_result *arg_type; /* Pointer to item_results */
char **args; /* Pointer to argument */
unsigned long *lengths; /* Length of string arguments */
} UDF_ARGS;
/* This holds information about the result */
typedef struct st_udf_init
{
my_bool maybe_null; /* 1 if function can return NULL */
unsigned int decimals; /* for real functions */
unsigned int max_length; /* For string functions */
char *ptr; /* free pointer for function data */
} UDF_INIT;
/* Prototypes to password functions */
void randominit(struct rand_struct *rand,unsigned long seed1,
unsigned long seed2);
double rnd(struct rand_struct *rand);
void make_scrambled_password(char *to,const char *password);
void get_salt_from_password(unsigned long *res,const char *password);
char *scramble(char *to,const char *message,const char *password,
my_bool old_ver);
my_bool check_scramble(const char *scramble,const char *message,
unsigned long *salt,my_bool old_ver);
char *get_tty_password(char *opt_message);
#define NULL_LENGTH ((unsigned long) ~0) /* For net_store_length */
#ifdef __WIN32__
#define socket_errno WSAGetLastError()
#else
#define socket_errno errno
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,13 @@
/*
* $Id$
*/
/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
This file is public domain and comes with NO WARRANTY of any kind */
/* Version numbers for protocol & mysqld */
#define MYSQL_SERVER_VERSION "3.21.33b"
#define FRM_VER 6
#define MYSQL_VERSION_ID 32133

View File

@@ -0,0 +1,55 @@
/*
* $Id$
*/
24/july/2000 - Harbour MySQL access classes - readme file
This is work in progress, so it has to be fully tested and needs a few more methods to cover MySQL possibilities.
This set of files gives you a mean to access a MySQL server, I've developed and tested them on a OS/2 platform,
so changes to Makefile and import library for different platforms are not present.
In their present state MySQL classes are made up of these files:
mysql.c: low level wrapper around MySQL client API. It requires libmysqlclient.a library
mysql.h,
mysql_com.h,
mysql_version.h: from MySQL distribution, type and defines of MySQL client api (under OS/2 with OS/2 port of
MySql you need to use the one from 3.21.33b build which is the only one with a single
threaded libmysqlclient.a client library and works ok even with latest MySQL/2 availble).
mysql.ch: clipper level defines of MySQL types
tmysql.prg: MySQL access classes
test.prg: a little test program which wont work for you :-) since it uses a .dbf file not
provided. Use it as a small tutorial of tmysql.prg provided functions.
Makefile: my makefile for OS/2 gcc, you'll surely need to change it to adapt to your needs/platform.
tmysql.prg defines four classes:
TMySQLServer: manages access to a MySQL server and returns an oServer object to which you'll send all your
queries;
TMySQLQuery: a standard query to an oServer with joins. Every query has a GetRow() method
which on every call returns a TMySQLRow object which, in turn, contains requested fields.
Query objects convert MySQL answers (which is an array of strings) to clipper level types.
At present time N (with decimals), L, D, and C clipper types are supported.
TMySQLTable: It's a descendant of a TMySQLQuery and you'll receive it when your query has no joins.
It adds Update(), Append() and Delete() methods which receive a TMySQLRow object and
reflect changes to the MySQL table from which they come.
Please note that TMySQLQuery objects don't have these methods, so, if you want to change
a row received from a TMySQLQuery object you need to construct a valid SQL query and submit
it to an oServer object.
TMySQLRow: Every row returned by a SELECT is converted to a TMySQLRow object. This object handles
fields and has methods to access fields given a field name or position.
I'm aware that this brief document doesn't explain a lot about MySQL access classes and I'm sorry for that.
I'll try to update it as work on these classes goes by and I'll like to receive feedbak and suggestions
from users (if any :-))
Excuse my poor english and happy selecting :-)
Maurilio Longo - maurilio.longo@libero.it

View File

@@ -0,0 +1,117 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* MySQL DBMS test program
*
* 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 of the License, or
* (at your option) any later version, with one exception:
*
* The exception is that if you link the Harbour Runtime Library (HRL)
* and/or the Harbour Virtual Machine (HVM) 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 HRL
* and/or HVM code into it.
*
* 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 program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
* their web site at http://www.gnu.org/).
*
*/
#include "dbstruct.ch"
procedure main(cArg)
local oServer, oQuery, oQuery2, oRow, i, aStru
SET CENTURY ON
SET EPOCH TO 1960
oServer := TMySQLServer():New("localhost", "root", "")
if oServer:NetErr()
Alert(oServer:Error())
endif
oServer:SelectDB("ims")
oQuery:=oServer:Query("SELECT * from maga limit 10")
oRow := oQuery:GetRow()
dbUseArea(.T.,, cArg, "wn", .F.)
if !oServer:DeleteTable("test")
Alert(oServer:Error())
endif
aStru := dbStruct()
if oServer:CreateTable("test", aStru)
Alert("test created successfully")
else
Alert(oServer:Error())
endif
oQuery:=oServer:Query("SELECT C111, C116, C134 from maga limit 10")
oRow := oQuery:GetRow()
oServer:Destroy()
while !wn->(eof())
oQuery2 := oServer:Query("SELECT * from test where CODF='" + wn->CODF + "' and CODP='" + wn->CODP + "'")
if oQuery2:LastRec() > 0
? "found "
oRow := oQuery2:GetRow()
oRow:FieldPut(oRow:FieldPos("GIACENZA"), oRow:FieldGet(oRow:FieldPos("GIACENZA")) + wn->GIACENZA)
oRow:FieldPut(oRow:FieldPos("ACQGR"), oRow:FieldGet(oRow:FieldPos("ACQGR")) + wn->ACQGR)
oRow:FieldPut(oRow:FieldPos("ACQDI"), oRow:FieldGet(oRow:FieldPos("ACQDI")) + wn->ACQDI)
if !oQuery2:Update(oRow)
Alert(oQuery2:Error())
endif
else
? wn->CODF + " " + wn->CODP
oRow := oQuery:GetBlankRow()
oRow:FieldPut(oRow:FieldPos("CODF"), wn->CODF)
oRow:FieldPut(oRow:FieldPos("CODP"), wn->CODP)
oRow:FieldPut(oRow:FieldPos("GIACENZA"), wn->GIACENZA)
oRow:FieldPut(oRow:FieldPos("DATA"), wn->DATA + 365 * 100)
oRow:FieldPut(oRow:FieldPos("ACQGR"), wn->ACQGR)
oRow:FieldPut(oRow:FieldPos("ACQDI"), wn->ACQDI)
if !oQuery:Append(oRow)
Alert(oQuery:Error())
endif
endif
wn->(dbSkip())
enddo
wn->(dbCloseArea())
return

View File

@@ -0,0 +1,835 @@
/*
* $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 of the License, or
* (at your option) any later version, with one exception:
*
* The exception is that if you link the Harbour Runtime Library (HRL)
* and/or the Harbour Virtual Machine (HVM) 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 HRL
* and/or HVM code into it.
*
* 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 program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
* their web site at http://www.gnu.org/).
*
*/
#include "hbclass.ch"
#include "common.ch"
#include "dbstruct.ch"
#include "mysql.ch"
// Returns an SQL string with clipper value converted ie. Date() -> "'YYYY-MM-DD'"
static function ClipValue2SQL(Value)
local cValue := ""
do case
case Valtype(Value) == "N"
cValue := AllTrim(Str(Value))
case Valtype(Value) == "D"
if !Empty(Value)
// MySQL dates are like YYYY-MM-DD
cValue := "'" + Str(Year(Value), 4) + "-" + PadL(Month(Value), 2, "0") + "-" + PadL(Day(Value), 2, "0") + "'"
else
cValue := "''"
endif
case Valtype(Value) == "C"
cValue := "'" + StrTran(Value, "'", "\'") + "'"
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
// 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
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(nNum) // Same as clipper ones
METHOD FieldPut(nNum, Value)
METHOD FieldName(nPosition)
METHOD FieldPos(cFieldName)
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 {}
::cTable := cTableName
::aFieldStruct := aFStruct
::aRow := aRow
::aFieldStruct := aFStruct
::aDirty := Array(Len(::aRow))
::aOldValue := Array(Len(::aRow))
AFill(::aDirty, .F.)
return Self
METHOD FieldGet(nNum) CLASS TMySQLRow
if nNum > 0 .AND. nNum <= Len(::aRow)
return ::aRow[nNum]
else
return nil
endif
return
METHOD FieldPut(nNum, Value) CLASS TMySQLRow
if nNum > 0 .AND. nNum <= Len(::aRow)
if Valtype(Value) == Valtype(::aRow[nNum]) .OR. Empty(::aRow[nNum])
::aRow[nNum] := Value
::aDirty[nNum] := .T.
return Value
endif
endif
return nil
// Given a field name returns it's position
METHOD FieldPos(cFieldName) CLASS TMySQLRow
local cUpperName, nPos := 1
cUpperName := Upper(cFieldName)
/* NOTE: this code block kills harbour if called a few thousand times
nPos := AScan(::aFieldStruct, {|aItem| iif(Upper(aItem[MYSQL_FS_NAME]) == cUpperName, .T., .F.)})
*/
while nPos <= Len(::aFieldStruct)
if Upper(::aFieldStruct[nPos][MYSQL_FS_NAME]) == cUpperName
exit
endif
nPos++
enddo
return nPos
// Returns name of field N
METHOD FieldName(nPosition) CLASS TMySQLRow
if nPosition >=1 .AND. nPosition <= Len(::aFieldStruct)
return ::aFieldStruct[nPosition][MYSQL_FS_NAME]
else
return ""
endif
return
// 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
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
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 GetRow(nRow) // return Row n of answer
METHOD Skip(nRows) // Same as clipper ones
METHOD Bof() INLINE ::nCurRow == 1
METHOD Eof() INLINE ::nCurRow > ::nNumRows
METHOD RecNo() INLINE ::nCurRow
METHOD LastRec() INLINE ::nNumRows
METHOD FCount()
METHOD NetErr() INLINE ::lError // Returns .T. if something went wrong
METHOD Error() // Returns textual description of last error and clears ::lError
ENDCLASS
METHOD New(nSocket, cQuery) CLASS TMySQLQuery
local nI, aField, rc
::aFieldStruct := {}
::nSocket := nSocket
::cQuery := cQuery
::nCurRow := 1
::lError := .F.
if (rc := sqlQuery(nSocket, cQuery)) == 0
// save result set
::nResultHandle := sqlStoreR(nSocket)
::nNumRows := sqlNRows(::nResultHandle)
::nNumFields := sqlNumFi(::nResultHandle)
for nI := 1 to ::nNumFields
aField := sqlFetchF(::nResultHandle)
AAdd(::aFieldStruct, aField)
next
else
::nResultHandle := nil
::nNumFields := 0
::nNumRows := 0
::lError := .T.
endif
return Self
METHOD Skip(nRows) CLASS TMySQLQuery
// NOTE: MySQL row count starts from 0
default nRows to 1
if nRows == 0
// No move
elseif nRows < 0
// Negative movement
::nCurRow := Max(::nCurRow - nRows, 1)
else
// positive movement
::nCurRow := Min(::nCurRow + nRows, ::nNumRows + 1)
endif
sqlDataS(::nResultHandle, ::nCurRow - 1)
return Self
/* 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" }
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
local aRow := NIL
local oRow := NIL
local i
default nRow to 0
if ::nResultHandle <> NIL
if nRow >= 1 .AND. nRow <= ::nNumRows
// NOTE: row count starts from 0
sqlDataS(::nResultHandle, nRow - 1)
::nCurRow := nRow
else
::nCurRow++
endif
aRow := sqlFetchR(::nResultHandle, ::nNumFields)
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
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
aRow[i] := Val(aRow[i])
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_DOUBLE_TYPE .OR.;
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_FLOAT_TYPE
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
aRow[i] := CToD(__StrToken(aRow[i], 2, ",") + "-" + __StrToken(aRow[i], 3, ",") + "-" + __StrToken(aRow[i], 1, ","))
endif
otherwise
endcase
next
oRow := TMySQLRow():New(aRow, ::aFieldStruct)
endif
endif
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)
/* ----------------------------------------------------------------------------------------*/
// 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
METHOD New(nSocket, cQuery, cTableName)
METHOD GetRow(nRow)
METHOD Update(oRow) // Gets an oRow and updates changed fields
METHOD Delete(oRow) // Deletes passed row from table
METHOD Append(oRow) // Inserts passed row into table
METHOD GetBlankRow() // Returns an empty row with all available fields empty
ENDCLASS
/* TODO: change to reflect MySQL lack of _rowid field */
METHOD New(nSocket, cQuery, cTableName) CLASS TMySQLTable
local cTrimmedQuery, nRes, i, aField
::cTable := cTableName
cTrimmedQuery := AllTrim(cQuery)
super:New(nSocket, cTrimmedQuery)
return Self
METHOD GetRow(nRow) CLASS TMySQLTable
local oRow := super:GetRow(nRow)
if oRow <> NIL
oRow:cTable := ::cTable
endif
return oRow
/* Creates an update query for changed fields and submits it to server */
METHOD Update(oRow) CLASS TMySQLTable
local cUpdateQuery := "UPDATE " + ::cTable + " SET "
local i, cField
// is this a row of this table ?
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)
cUpdateQuery += oRow:MakePrimaryKeyWhere()
if sqlQuery(::nSocket, cUpdateQuery) == 0
// All values are commited
Afill(oRow:aDirty, .F.)
return .T.
else
::lError := .T.
endif
endif
return .F.
METHOD Delete(oRow) CLASS TMySQLTable
local cDeleteQuery := "DELETE FROM " + ::cTable
// is this a row of this table ?
if oRow:cTable == ::cTable
cDeleteQuery += oRow:MakePrimaryKeyWhere()
if sqlQuery(::nSocket, cDeleteQuery) == 1
return .T.
else
::lError := .T.
endif
endif
return .F.
// Adds a row with values passed into oRow
METHOD Append(oRow) CLASS TMySQLTable
local cInsertQuery := "INSERT INTO " + ::cTable + " ("
local i, cField
// is this a row of this table ?
if oRow:cTable == ::cTable
// field names
for i := 1 to Len(oRow:aRow)
cInsertQuery += oRow:aFieldStruct[i][MYSQL_FS_NAME] + ","
next
// remove last comma from list
cInsertQuery := Left(cInsertQuery, Len(cInsertQuery) -1) + ") VALUES ("
// field values
for i := 1 to Len(oRow:aRow)
cInsertQuery += ClipValue2SQL(oRow:aRow[i]) + ","
next
// remove last comma from list of values and add closing parenthesis
cInsertQuery := Left(cInsertQuery, Len(cInsertQuery) -1) + ")"
if sqlQuery(::nSocket, cInsertQuery) == 0
return .T.
else
::lError := .T.
endif
endif
return .F.
METHOD GetBlankRow() CLASS TMySQLTable
local i
local aRow := Array(::FCount())
// crate an array of empty fields
for i := 1 to ::FCount()
do case
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_STRING_TYPE .OR.;
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_VAR_STRING_TYPE
aRow[i] := ""
case ::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_SHORT_TYPE .OR.;
::aFieldStruct[i][MYSQL_FS_TYPE] == MYSQL_LONG_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
return TMySQLRow():New(aRow, ::aFieldStruct, ::cTable, .F.)
return nil
/* ----------------------------------------------------------------------------------------*/
// 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
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) // 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
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.
// 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) CLASS TMySQLServer
local cCreateQuery := "CREATE TABLE " + cTable + " ("
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 ", ""), "")}
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]) + ","
case aStruct[i][DBS_TYPE] == "N"
if aStruct[i][DBS_DEC] == 0
cCreateQuery += aStruct[i][DBS_NAME] + " int " + Eval(cNN, aStruct[i]) + ","
else
cCreateQuery += aStruct[i][DBS_NAME] + " real " + 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]) + ","
otherwise
cCreateQuery += aStruct[i][DBS_NAME] + " char(" + AllTrim(Str(aStruct[i][DBS_LEN])) + ")" + Eval(cNN, aStruct[i]) + ","
endcase
next
// 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 " + 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 " + cTable
if sqlQuery(::nSocket, cDropQuery) == 0
return .T.
endif
return .F.
METHOD DeleteTable(cTable) CLASS TMySQLServer
local cDropQuery := "DROP TABLE " + cTable
if sqlQuery(::nSocket, cDropQuery) == 0
return .T.
endif
return .F.
METHOD Query(cQuery) CLASS TMySQLServer
local oQuery, cTableName, i, cUpperQuery, nNumTables, cToken
cUpperQuery := Upper(AllTrim(cQuery))
i := 1
nNumTables := 0
while __StrToken(cUpperQuery, i++, " ") <> "FROM"
enddo
while (cToken := __StrToken(cUpperQuery, i++, " ")) <> "WHERE" .AND. cToken <> "LIMIT" .AND. !Empty(cToken)
cTableName := __StrToken(cQuery, i - 1, " ")
nNumTables++
enddo
if nNumTables == 1
oQuery := TMySQLTable():New(::nSocket, cQuery, cTableName)
else
oQuery := TMySQLQuery():New(::nSocket, cQuery)
endif
if oQuery:nNumRows < 0
::lError := .T.
endif
return oQuery
METHOD Error() CLASS TMySQLServer
::lError := .F.
return sqlGetErr(::nSocket)
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
otherwise
endcase
AAdd(aStruct, aSField)
endif
next
sqlFreeR(nRes)
endif*/
return aStruct