ZPlainMySQL5.pas: wrong definition for MYSQL_BIND record
Posted: 24.10.2007, 00:11
The definition of the MYSQL_BIND structure in ZPlainMySQL5.pas is wrong and leads to access violations when executing statements with bound parameters.
Current definition (SVN trunk):
Actual C definition
Fixed definition:
Sample program (tested with server 5.0.24-community-nt) :
Hopefully in the future zeos will support real bind parameter for MySQL.
Current definition (SVN trunk):
Code: Select all
MYSQL_BIND = record
length: PLongInt;
is_null: PByte;
buffer: PChar;
buffer_type: Cardinal;
buffer_length: LongInt;
inter_buffer: PByte;
offset: LongInt;
internal_length: LongInt;
param_number: Cardinal;
long_data_used: Byte;
binary_data: Byte;
null_field: Byte;
internal_is_null: Byte;
store_param_func: procedure(_net: NET; param: PMYSQL_BIND);
fetch_result: procedure(param: PMYSQL_BIND; row: PMYSQL_ROW);
end;
Code: Select all
typedef struct st_mysql_bind
{
unsigned long *length; /* output length pointer */
my_bool *is_null; /* Pointer to null indicator */
void *buffer; /* buffer to get/put data */
/* set this if you want to track data truncations happened during fetch */
my_bool *error;
enum enum_field_types buffer_type; /* buffer type */
/* output buffer length, must be set when fetching str/binary */
unsigned long buffer_length;
unsigned char *row_ptr; /* for the current data position */
unsigned long offset; /* offset position for char/binary fetch */
unsigned long length_value; /* Used if length is 0 */
unsigned int param_number; /* For null count and error messages */
unsigned int pack_length; /* Internal length for packed data */
my_bool error_value; /* used if error is 0 */
my_bool is_unsigned; /* set if integer type is unsigned */
my_bool long_data_used; /* If used with mysql_send_long_data */
my_bool is_null_value; /* Used if is_null is 0 */
void (*store_param_func)(NET *net, struct st_mysql_bind *param);
void (*fetch_result)(struct st_mysql_bind *, MYSQL_FIELD *,
unsigned char **row);
void (*skip_result)(struct st_mysql_bind *, MYSQL_FIELD *,
unsigned char **row);
} MYSQL_BIND;
Code: Select all
MYSQL_BIND = record
length: PLongInt;
is_null: PByte;
buffer: PChar;
error : PByte; // new
buffer_type: Cardinal;
buffer_length: LongInt;
inter_buffer: PByte;
offset: LongInt;
internal_length: LongInt;
param_number: Cardinal;
pack_length : Cardinal; // new
error_value : Byte; // new
is_unsigned: Byte;
long_data_used: Byte;
null_field: Byte;
store_param_func: procedure(_net: NET; param: PMYSQL_BIND); cdecl;
fetch_result: procedure(param: PMYSQL_BIND; fld : PMYSQL_FIELD; row: PMYSQL_ROW); cdecl;
skip_result : procedure(param: PMYSQL_BIND; fld : PMYSQL_FIELD; row: PMYSQL_ROW); cdecl;
end;
Code: Select all
program dbc;
{$APPTYPE CONSOLE}
uses
SysUtils,
Windows,
ZClasses,
ZDbcIntfs,
ZCompatibility,
ZDbcMySql,
ZPlainMySQLDriver,
ZPlainMySQL5;
var
conn : IZConnection;
stmt : IZStatement;
// mysql specific
drv : IZMySQLPlainDriver;
conn_handle : Pointer;
pstmt_handle : Pointer;
binds : array [0..1] of MYSQL_BIND;
buf : array [0..1023] of char;
str_param_length : Integer;
// end mysql specific
i : Integer;
query : string;
begin
try
conn := DriverManager.GetConnection('zdbc:mysql-5://localhost/db?UID=uid;PWD=pwd');
stmt := conn.CreateStatement;
stmt.ExecuteUpdate('DROP TABLE IF EXISTS t');
stmt.ExecuteUpdate('CREATE TABLE t ( x INTEGER, y VARCHAR(50) ) ENGINE=MEMORY');
stmt.Close;
stmt := nil;
// get mysql specific bits
drv := (conn as IZMySQLConnection).GetPlainDriver;
conn_handle := (conn as IZMySQLConnection).GetConnectionHandle;
pstmt_handle := drv.InitializePrepStmt(conn_handle);
ZeroMemory(@binds, SizeOf(binds));
query := 'INSERT INTO t (y, x) VALUES (?, ?)';
drv.PrepareStmt(pstmt_handle, PAnsiChar(query), Length(query));
binds[1].buffer_type := FIELD_TYPE_LONG;
binds[1].buffer := PAnsiChar(@i);
binds[1].is_null := nil;
binds[1].length := nil;
binds[0].buffer_type := FIELD_TYPE_STRING;
binds[0].buffer := buf;
binds[0].buffer_length := 1024;
binds[0].is_null := nil;
binds[0].length := @str_param_length;
writeln('markers: ', drv.GetPreparedBindMarkers(pstmt_handle));
drv.BindParameters(pstmt_handle, @binds);
for i := 1 to 3 do begin
buf := 'xyzzy';
str_param_length := 5;
drv.ExecuteStmt(pstmt_handle);
end;
drv.ClosePrepStmt(pstmt_handle);
conn.Close;
conn := nil;
writeln('press any key');
readln;
except
on e: Exception do begin writeln(e.message); readln; end;
end;
end.