MyC Reference Manual

Introduction

MyC is a set of simple C routines intended to make programming
applications for use with the MySQL database engine a bit easier,
especially for those familiar with Microsoft's DAO engine model (used with
Microsoft Access and Microsoft Visual Basic).

MyC uses a similar model to access data within a database - OpenDatabase
to open a database, OpenRecordset to open a result set, Move routines to
move around within the result set, and AddNew/Edit and Update/Delete to
add, edit or delete data within the database.

Current Version/Change Log

The current version of MyC is 0.0.1 ALPHA.

Restrictions

MyC is not intended to open more than one result set at a time - multiple
result set support will be forthcoming in a future release.

If a result set does not have a unique key associated with each record,
then the Delete() routine will cause only the first matching referenced
record to be deleted.  This is at variance with the DAO model, which will
delete only the record currently referenced, which may or may not be the
first matching record.

Currently, MyC will only run on Linux, but it should be straightforward to
port to other UNIX variants.  No attempt has been made to either port the
code or write the code with an idea of porting to any Windows platform.  
I haven't written C for Windows since Windows 2.1, so I haven't a clue how
difficult it would be, but the code doesn't use anything approaching
esoteric, so it shouldn't be too difficult.

Neither Purify nor any other sort of memory leak checker has been run on
MyC.

Please remember that this code is ALPHA, and is subject to change.  It's
probably got bugs in it somewhere, and if you push the boundaries, you'll
probably find one or two.  Test your code thoroughly, especially before
putting this code into any sort of production environment.  This code is
provided with the hope that it will be useful, but without warranty.  It
works for me, I hope it works for you.  I've got it running here,
manipulating several databases - one with news headlines that's got a web
interface, one doing registration for a convention in Toronto in August
(see http://www.rcw139.ca for details), and one automatically archiving
all incoming email addressed to me.

Header Files

Currently, all MyC routines are included in one header file, "database.h".
The name of this file is not critical and can be changed at any time to
suit a particular site's needs.

This file should be included first, and includes stdio.h and stdlib.h If
"#define MYC_DEBUG" is placed before including database.h, then extensive
debugging code will be written to the stderr stream.

Compiling

MyC programs are compiled the same way as other MySQL C API programs.  
For more information, see the MySQL Reference Manual at http://www.tcx.se.


Initialization

Calling OpenDatabase() with the name of the database to be opened, the
host name, the user name, and the password will initialize the application
and open the selected database.  EOF is returned on failure, NULL is
returned on success.

Calling OpenRecordset() with a valid SQL query string opens a result set
that contains the results of the query.  OpenRecordset() returns EOF on
failure or NULL on success.  The number of records in the result set can
be obtained by calling RecordCount(), which returns the number of records
in the result set as an unsigned integer.

Moving Around The Result Set

The result set can be traversed with MoveNext, MovePrev, MoveFirst, and
MoveLast, which move to the next, previous, first, and last records in the
result set, respectively.  If the result set is not valid, these routines
return EOF, otherwise, they return NULL.

Accessing Data

Data in the current row in a result set can be accessed by calling
GetField or GetFieldN.  GetField is called with the name of the field as
an argument, and returns a character pointer to the data, or a character
pointer to EOF on failure.  GetFieldN works in a similar fashion, except
that GetFieldN is called with the number of the desired field (origin 0).

Writing Data

Data can be written to a table in one of two ways - by either calling
OpenRecordset() with an "INSERT INTO" or "UPDATE" query (or any other
valid SQL statement, for that matter), or by use of the AddNew or Edit
functions.

AddNew() is called with no arguments, and sets internal flags such that a
subsequent call to Update() will generate an "INSERT INTO" SQL statement.  
Similarly, Edit() is also called with no arguments, and sets internal
flags such that a subsequent call to Update() will generate an "UPDATE"  
SQL statement.

To set the individual fields, SetField, SetFieldN, or SetFieldB is used.  
SetField is called with two arguments - the name of the field to be set,
and the value to set the named field.  Similarly, SetFieldN is called with
two arguments, but like GetFieldN, the first argument is the number of the
field, rather than the name.  SetFieldB is used with binary data, and will
convert binary data ending with a NULL into a form that can be stored.  
Note that SetFieldB will overwrite the supplied value string.  No
provision is made for converting pure binary data, although one could call
mysql_escape_string() and pass the returned pointer to SetField or
SetFieldN.

It is important to note that SetField and similar routines store a pointer
to the data to be written, not the actual data itself.

After all the fields are set, Update() is called with the name of the
table to be updated.  Update() returns EOF on failure or NULL on success.

After data is added to or updated in a table, sometimes it is useful to
have the current record set reflect that changed data.  Calling Refresh()
will re-run the query run by the last OpenRecordset() (up to 4096 bytes)  
and is equivalent to calling OpenRecordset(), except that the current row
is the same as before calling Refresh().


Deleting Data

Calling Delete() with the name of the affected table will delete the
current row from the table.  If there are rows with duplicate data, only
the first row will be deleted.

Getting Information

Currently, the following information routines are suppported:
RecordCount(), FieldCount(), AbsolutePosition(), and RecordsetEOF().  
RecordCount returns an unsigned integer indicating the current number of
records in the result set.  FieldCount returns the number of fields in a
row of the result set.  FieldValue is called with the number of a field
and returns its value, while FieldName is called with the number of a
field and returns its name.  AbsolutePosition returns the row number of
the current row in the result set.  RecordsetEOF returns either TRUE if
there are no valid records in the result set or if the current row is at
the end or the beginning of the result set and no valid records remain to
be read from the record set.  MoveLast and MoveFirst set RecordsetEOF to
TRUE.

Putting It All Together

Applications are very easy to write with MyC.  In the following examples,
consider there exists a table called "test" in the database "mysql",
consisting of 5 data records as follows:

+--------+-----+
| name   | num |
+--------+-----+
| item 0 |  0  |
| item 1 |  1  |
| item 2 |  2  |
| item 3 |  3  |
| item 4 |  4  |
+--------+-----+

Consider, for example, an application to query a database and write all
the data to the stdout stream:


#include "database.h"
main ()
{
  int i;

  OpenDatabase ("mysql", NULL, NULL, NULL);
  OpenRecordset ("select * from test2");
  while (RecordsetEOF () != EOF)
    {
      for (i = 0; i < FieldCount (); i++)
	printf ("%s\t", GetFieldN (i));
      puts ("");
      MoveNext ();
    }
  CloseRecordset ();
  CloseDatabase ();
  exit (0);
}

A slightly more complicated example will illustrate adding, changing, and
deleting data.  This example will build a table called "test", list all
the data in the table, then scan the table, changing "item 3" to "item 5"
and deleting "item 4":

#include "database.h"
main ()
{
  int i;
  char buf[10], buf2[5];

  OpenDatabase ("mysql", NULL, NULL, NULL);
  OpenRecordset ("delete from test");
  CloseRecordset ();
/*
   * populate the table
 */
  OpenRecordset ("select * from test");
  for (i = 0; i < 5; i++)
    {
      AddNew ();
      sprintf (buf, "item %d", i);
      sprintf (buf2, "%d", i);
      SetField ("name", buf);
      SetField ("num", buf2);
      Update ("test");
    }
/*
   * make the current result set match what's in the database
 */
  Refresh ();
  MoveFirst ();

  while (RecordsetEOF () != EOF)
    {
      for (i = 0; i < FieldCount (); i++)
	printf ("%s\t", GetFieldN (i));
      puts ("");
      MoveNext ();
    }
  MoveFirst ();
/*
   * change "item 3" in the database to "item 5".
   * delete "item 4" from the database
 */
  while (RecordsetEOF () != EOF)
    {
      printf ("Looking at '%s'\n", GetField ("name"));
      if (strcmp (GetField ("name"), "item 3") == 0)
	{
	  puts ("Changing data");
	  Edit ();
	  SetField ("name", "item 5");
	  Update ("test");
	}
      if (strcmp (GetField ("name"), "item 4") == 0)
	{
	  puts ("Deleting data");
	  Delete ("test");
	}
      MoveNext ();
    }
  CloseRecordset ();
  CloseDatabase ();
  exit (0);
}

Author

The author of this package is Ed Carp.  I can be reached at erc@pobox.com.  
My home page is www.pobox.com/~erc.  I am a technical manager and
commercial software developer, with extensive experience in managing
projects end-to-end, as well as software development in C and VB.

Credits

Thanks to Monty and the crew at tcx.se for writing such an awesome
database!  If you'd like to know more about MySQL, please see their brag
sheet at http://www.tcx.se/what-is-mysql.html.
