#include <stdio.h> #include <stdarg.h> #include <string.h> #include <time.h> #include <stdlib.h> #ifdef WINDOWS #include <windows.h> #endif #include <mysql.h> #include "xmlapi.h" #include "xmlobj.h" #include "../wftk.h" #include "../wftk_internals.h" XML * wftk_sql_build_select (XML * list, char * key, WFTK_SQL_FORCE_MAPPING force); XML * wftk_sql_build_insert (XML * list, XML * object, WFTK_SQL_FORCE_MAPPING force); XML * wftk_sql_build_update (XML * list, XML * object, WFTK_SQL_FORCE_MAPPING force); XML * wftk_sql_build_delete (XML * list, char * key, WFTK_SQL_FORCE_MAPPING force); static void _LIST_mysql_force_mapping (XML * field); |
#ifdef WINDOWS #define _LIST_mysql_strlwr(x) strlwr(x) #else void _LIST_mysql_strlwr (char * str) { if (!str) return; while (*str) { if (*str >= 'A' && *str <= 'Z') { *str += 'a' - 'A'; } str++; } } #endif |
adaptor_info
structure is used to pass adaptor info (duh) back to the
config module when it's building an adaptor instance. Here's what it contains:
static char *names[] = { "init", "free", "info", "create", "destroy", "add", "update", "delete", "get", "query", "first", "next", "rewind", "prev", "last", "attach_open", "attach_write", "attach_close", "attach_cancel", "retrieve_open", "retrieve_read", "retrieve_close" }; XML * LIST_mysql_init (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_free (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_info (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_create (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_destroy (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_add (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_update (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_delete (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_get (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_query (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_first (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_next (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_rewind (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_prev (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_last (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_attach_open (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_attach_write (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_attach_close (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_attach_cancel (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_retrieve_open (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_retrieve_read (WFTK_ADAPTOR * ad, va_list args); XML * LIST_mysql_retrieve_close (WFTK_ADAPTOR * ad, va_list args); static WFTK_API_FUNC vtab[] = { LIST_mysql_init, LIST_mysql_free, LIST_mysql_info, LIST_mysql_create, LIST_mysql_destroy, LIST_mysql_add, LIST_mysql_update, LIST_mysql_delete, LIST_mysql_get, LIST_mysql_query, LIST_mysql_first, LIST_mysql_next, LIST_mysql_rewind, LIST_mysql_prev, LIST_mysql_last, LIST_mysql_attach_open, LIST_mysql_attach_write, LIST_mysql_attach_close, LIST_mysql_attach_cancel, LIST_mysql_retrieve_open, LIST_mysql_retrieve_read, LIST_mysql_retrieve_close }; static struct wftk_adaptor_info _LIST_mysql_info = { 22, names, vtab }; |
struct wftk_adaptor_info * LIST_mysql_get_info () { return & _LIST_mysql_info; } |
struct _LIST_mysql_handles { MYSQL * mysql; int ok; }; void LIST_mysql_cleanup (struct _LIST_mysql_handles *handles) { if (handles) { mysql_close (handles->mysql); free (handles); } } XML * LIST_mysql_init (WFTK_ADAPTOR * ad, va_list args) { const char * parms; char * mark; XML * repository = (XML *) ad->session; XML * conn; struct _LIST_mysql_handles *handles; int rslt; char buf[1024]; parms = xml_attrval (ad->parms, "parm"); if (!*parms) { xml_set (ad->parms, "error", "No MySQL datasource specified"); return NULL; } /* Find or create connection. */ xml_setf (repository, "scratch", "mysql:%s", parms); conn = xml_search (repository, "connection", "storage", xml_attrval (repository, "scratch")); if (!conn) { conn = xml_create ("connection"); xml_set (conn, "storage", xml_attrval (repository, "scratch")); xml_set (conn, "database", parms); /* Default, if no predefined connection: parms name local database. */ xml_append (repository, conn); } /* Is connection already made? If not, connect. */ handles = (struct _LIST_mysql_handles *) xml_getbin (conn); if (!handles) { handles = (struct _LIST_mysql_handles *) malloc (sizeof (struct _LIST_mysql_handles)); if (!handles) { xml_set (ad->parms, "error", "Unable to allocate binary stash."); return NULL; } handles->mysql = NULL; handles->ok = 0; } ad->bindata = (void *) handles; if (!handles->ok) { handles->mysql = mysql_init (NULL); if (!(handles->mysql)) { xml_set (ad->parms, "error", "Unable to allocate MySQL environment handle."); free ((void *) handles); xml_setbin (conn, NULL, NULL); ad->bindata = 0; return (XML *) 0; } if (!mysql_real_connect (handles->mysql, *xml_attrval (conn, "host") ? xml_attrval (conn, "host") : "localhost", *xml_attrval (conn, "user") ? xml_attrval (conn, "user") : NULL, *xml_attrval (conn, "password") ? xml_attrval (conn, "password") : NULL, *xml_attrval (conn, "database") ? xml_attrval (conn, "database") : NULL, *xml_attrval (conn, "port") ? atoi (xml_attrval (conn, "port")): MYSQL_PORT, NULL, CLIENT_FOUND_ROWS)) { xml_setf (ad->parms, "error", "Unable to connect to database specified: %s", mysql_error (handles->mysql)); mysql_close (handles->mysql); free ((void *) handles); xml_setbin (conn, NULL, NULL); ad->bindata = 0; return (XML *) 0; } handles->ok = 1; xml_setbin (conn, handles, (XML_SETBIN_FREE_FN *)LIST_mysql_cleanup); } return (XML *) 0; } XML * LIST_mysql_free (WFTK_ADAPTOR * ad, va_list args) { return (XML *) 0; } |
XML * LIST_mysql_info (WFTK_ADAPTOR * ad, va_list args) { XML * info; info = xml_create ("info"); xml_set (info, "type", "list"); xml_set (info, "name", "mysql"); xml_set (info, "ver", "1.0.0"); xml_setnum (info, "mysql_ver", MYSQL_VERSION_ID); xml_set (info, "compiled", __TIME__ " " __DATE__); xml_set (info, "author", "Michael Roberts"); xml_set (info, "contact", "wftk@vivtek.com"); xml_set (info, "extra_functions", "0"); return (info); } |
static void _LIST_mysql_builderrmsg (XML * ap, MYSQL * mysql) { xml_setf (ap, "_error", "%s[%d] %s", xml_attrval (ap, "error"), mysql_errno (mysql), mysql_error (mysql)); xml_set (ap, "error", xml_attrval (ap, "_error")); xml_set (ap, "_error", ""); } static void sql_escaped_append (XML * s, const char * q, const char * v) { char * mark; if (!v) return; while (mark = strchr (v, '\'')) { xml_attrncat (s, q, v, mark - v); xml_attrcat (s, q, "'"); v = mark + 1; } xml_attrcat (s, q, v); } |
static XML * _LIST_mysql_add (WFTK_ADAPTOR * ad, XML * list, XML * object) { struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata; XML * rest_xml = NULL; if (!handles) { xml_set (ad->parms, "error", "Database connection not initialized."); return 0; } if (!handles->ok) { xml_set (ad->parms, "error", "Database connection not initialized."); return 0; } xml_set (list, "_now", "NOW()"); xml_set (list, "_blob", "NULL"); xml_set (list, "_keygen", ""); xml_set (list, "_keycol", ""); rest_xml = wftk_sql_build_insert (list, object, (WFTK_SQL_FORCE_MAPPING) _LIST_mysql_force_mapping); /* Now execute the query we just built. */ (ad->log) (ad->session, ad, 5, xml_attrval (list, "_insert")); if (mysql_query (handles->mysql, xml_attrval (list, "_insert"))) { xml_setf (ad->parms, "error", "SQL error from [%s] ", xml_attrval (list, "_insert")); _LIST_mysql_builderrmsg (ad->parms, handles->mysql); } else { if (*xml_attrval (list, "_keycol")) { xml_setnum (list, "_newkey", (int)mysql_insert_id(handles->mysql)); xmlobj_set (object, NULL, xml_attrval (list, "_keycol"), xml_attrval (list, "_newkey")); xml_unset (list, "_newkey"); } } /* TODO: handle links. */ /* TODO: BLOB/rest_xml stuff. */ xml_free (rest_xml); return NULL; } XML * LIST_mysql_add (WFTK_ADAPTOR * ad, va_list args) { XML * list = NULL; XML * obj = NULL; if (args) list = va_arg (args, XML *); if (!list) { xml_set (ad->parms, "error", "No list descriptor given."); return (XML *) 0; } obj = va_arg (args, XML *); if (!obj) { xml_set (ad->parms, "error", "No object given."); return (XML *) 0; } return _LIST_mysql_add (ad, list, obj); } |
static void _LIST_mysql_force_mapping (XML * field) { const char * id; if (*xml_attrval (field, "alias")) return; /* TODO: encode reserved words. */ if (!strncmp (xml_attrval (field, "storage"), "record:", 7)) { xml_set (field, "alias", xml_attrval (field, "storage") + 7); } if (!*xml_attrval (field, "alias")) xml_set (field, "alias", xml_attrval (field, "id")); } |
static XML * _LIST_mysql_update (WFTK_ADAPTOR * ad, XML * list, XML * object) { struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata; XML * rest_xml; if (!handles) { xml_set (ad->parms, "error", "Database connection not initialized."); return 0; } if (!handles->ok) { xml_set (ad->parms, "error", "Database connection not initialized."); return 0; } xml_set (list, "_now", "NOW()"); xml_set (list, "_blob", "NULL"); xml_set (list, "_keygen", ""); rest_xml = wftk_sql_build_update (list, object, (WFTK_SQL_FORCE_MAPPING) _LIST_mysql_force_mapping); /* Now execute the query we just built. */ (ad->log) (ad->session, ad, 5, xml_attrval (list, "_update")); if (mysql_query (handles->mysql, xml_attrval (list, "_update"))) { xml_setf (ad->parms, "error", "SQL error from [%s] ", xml_attrval (list, "_update")); _LIST_mysql_builderrmsg (ad->parms, handles->mysql); } else { if (!xml_is (object, "diff")) { /* Note: we only allow an insertion for a complete record, not a diff. */ if (mysql_affected_rows (handles->mysql) == 0) { xml_free (rest_xml); return _LIST_mysql_add (ad, list, object); } } } xml_free (rest_xml); return 0; } XML * LIST_mysql_update (WFTK_ADAPTOR * ad, va_list args) { XML * list; XML * obj; if (args) list = va_arg (args, XML *); if (!list) { xml_set (ad->parms, "error", "No list descriptor given."); return (XML *) 0; } obj = va_arg (args, XML *); if (!obj) { xml_set (ad->parms, "error", "No object given."); return (XML *) 0; } return _LIST_mysql_update (ad, list, obj); } static XML * _LIST_mysql_delete (WFTK_ADAPTOR * ad, XML * list, char * key) { struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata; if (!handles) { xml_set (ad->parms, "error", "Database connection not initialized."); return 0; } if (!handles->ok) { xml_set (ad->parms, "error", "Database connection not initialized."); return 0; } wftk_sql_build_delete (list, key, (WFTK_SQL_FORCE_MAPPING) _LIST_mysql_force_mapping); /* Now execute the query we just built. */ (ad->log) (ad->session, ad, 5, xml_attrval (list, "_delete")); if (mysql_query (handles->mysql, xml_attrval (list, "_delete"))) { xml_setf (ad->parms, "error", "SQL error from [%s] ", xml_attrval (list, "_delete")); _LIST_mysql_builderrmsg (ad->parms, handles->mysql); } return 0; } XML * LIST_mysql_delete (WFTK_ADAPTOR * ad, va_list args) { XML * list; char * key; if (args) list = va_arg (args, XML *); if (!list) { xml_set (ad->parms, "error", "No list descriptor given."); return (XML *) 0; } key = va_arg (args, char *); if (!key) { xml_set (ad->parms, "error", "No key given."); return (XML *) 0; } return _LIST_mysql_delete (ad, list, key); } |
static void _LIST_mysql_select (WFTK_ADAPTOR * ad, XML * list, char * key) { struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata; XML * scratch; XML * field; char * value; XML * mark; MYSQL_RES * result; if (!handles) return; if (!handles->ok) return; wftk_sql_build_select (list, key, (WFTK_SQL_FORCE_MAPPING) _LIST_mysql_force_mapping); /* Execute the query. */ (ad->log) (ad->session, ad, 5, xml_attrval (list, "_select")); if (mysql_query (handles->mysql, xml_attrval (list, "_select"))) { xml_set (ad->parms, "error", "SQL error selecting data "); xml_set (list, "_status", "error"); _LIST_mysql_builderrmsg (ad->parms, handles->mysql); } else { result = mysql_store_result (handles->mysql); xml_set (list, "_status", "ok"); xml_setnum (list, "_numcols", mysql_field_count (handles->mysql)); xml_setbin (list, result, (XML_SETBIN_FREE_FN *)mysql_free_result); } } |
static XML * _LIST_mysql_retrieve (WFTK_ADAPTOR * ad, XML * list, int save_row) { struct _LIST_mysql_handles *handles = (struct _LIST_mysql_handles *) ad->bindata; int numcols; int col; long value_len; MYSQL_RES * result; MYSQL_ROW row; MYSQL_FIELD * fields; XML * ret; char column[256]; char value[1024]; XML * field; if (!handles) return (XML *) 0; if (!handles->ok) return (XML *) 0; result = xml_getbin (list); if (!result) return (XML *) 0; /* Retrieve a row; if none, free the statement and return NULL. */ row = mysql_fetch_row (result); if (!row) { xml_set (list, "_status", "complete"); mysql_free_result (result); xml_setbin (list, NULL, NULL); return (XML *) 0; } fields = mysql_fetch_fields (result); numcols = xml_attrvalnum (list, "_numcols"); ret = xml_create (*xml_attrval (list, "element") ? xml_attrval (list, "element") : "record"); if (save_row) xml_append (list, ret); for (col=0; col < numcols; col++) { strcpy (column, fields[col].name); _LIST_mysql_strlwr (column); field = xml_search (list, "field", "id", column); if (field) { xmlobj_set (ret, list, xml_attrval (field, "id"), row[col]); } } xml_set_nodup (ret, "id", xmlobj_getkey (ret, list)); return ret; } |
XML * LIST_mysql_query (WFTK_ADAPTOR * ad, va_list args) { XML * list; int count = 0; if (args) list = va_arg (args, XML *); if (!list) { xml_set (ad->parms, "error", "No list descriptor given."); return (XML *) 0; } _LIST_mysql_select (ad, list, NULL); /* NULL = no specific key. */ /* Retrieve all results. */ while (_LIST_mysql_retrieve (ad, list, 1)) { /* 1 = save row in list. */ count ++; } xml_setnum (list, "count", count); return list; } |
XML * LIST_mysql_first (WFTK_ADAPTOR * ad, va_list args) { XML * list; if (args) list = va_arg (args, XML *); if (!list) { xml_set (ad->parms, "error", "No list descriptor given."); return (XML *) 0; } _LIST_mysql_select (ad, list, NULL); return _LIST_mysql_retrieve (ad, list, 0); } XML * LIST_mysql_next (WFTK_ADAPTOR * ad, va_list args) { XML * list; if (args) list = va_arg (args, XML *); if (!list) { xml_set (ad->parms, "error", "No list descriptor given."); return (XML *) 0; } return _LIST_mysql_retrieve (ad, list, 0); } XML * LIST_mysql_rewind (WFTK_ADAPTOR * ad, va_list args) { xml_set (ad->parms, "error", "'rewind' command not supported under mysql"); } XML * LIST_mysql_prev (WFTK_ADAPTOR * ad, va_list args) { xml_set (ad->parms, "error", "'prev' command not supported under mysql"); } XML * LIST_mysql_last (WFTK_ADAPTOR * ad, va_list args) { xml_set (ad->parms, "error", "'last' command not supported under mysql"); } |
XML * LIST_mysql_get (WFTK_ADAPTOR * ad, va_list args) { XML * list; char * key; XML * copy; XML * ret; if (args) list = va_arg (args, XML *); if (!list) { xml_set (ad->parms, "error", "No list descriptor given."); return (XML *) 0; } key = va_arg (args, char *); copy = xml_copy (list); _LIST_mysql_select (ad, copy, key); ret = _LIST_mysql_retrieve (ad, copy, 0); xml_free (copy); return ret; } |
XML * LIST_mysql_create (WFTK_ADAPTOR * ad, va_list args) { return NULL; } XML * LIST_mysql_destroy (WFTK_ADAPTOR * ad, va_list args) { return NULL; } |
XML * LIST_mysql_attach_open (WFTK_ADAPTOR * ad, va_list args) { XML * list; char * key; char * field; char * filename; XML * mark; XML * ret; FILE * file; if (args) list = va_arg (args, XML *); if (!list) { xml_set (ad->parms, "error", "No list descriptor given."); return (XML *) 0; } key = va_arg (args, char *); field = va_arg (args, char *); filename = va_arg (args, char *); /* If we're not given a fieldname, then we'll just scan the list definition to find the first "document"-type field. */ if (!field) { mark = xml_search (list, "field", "type", "document"); if (!mark) { xml_set (ad->parms, "error", "No attachment field given and no default exists."); return NULL; } field = (char *) xml_attrval (mark, "id"); } ret = xml_create ("attachment-handle"); xml_set (ret, "dir", xml_attrval (ad->parms, "basedir")); if (!*xml_attrval (ad->parms, "subdir")) xml_set (ad->parms, "subdir", xml_attrval (list, "id")); if (strcmp (xml_attrval (ad->parms, "subdir"), ".")) { xml_attrcat (ret, "dir", xml_attrval (ad->parms, "subdir")); xml_attrcat (ret, "dir", "/"); } xml_setf (ret, "adaptor", "mysql:%s", xml_attrval (ret, "dir")); /* If we're supplied with a filename, then that file can't already exist in our controlled directory. */ if (filename && *filename) { xml_setf (ret, "location", filename); xml_setf (ret, "file", "%s%s", xml_attrval (ret, "dir"), filename); xml_set (ret, "tempfile", xml_attrval (ret, "file")); } else { xml_setf (ret, "location", "_att_%s_%s.dat", key, field); xml_setf (ret, "file", "%s%s", xml_attrval (ret, "dir"), xml_attrval (ret, "location")); xml_setf (ret, "tempfile", "%s_newatt_%s_%s.dat", xml_attrval (ret, "dir"), key, field); } file = fopen (xml_attrval (ret, "file"), "w"); if (!file) { xml_setf (ad->parms, "error", "Unable to open file %s for writing.", xml_attrval (ret, "file")); xml_free (ret); return NULL; } xml_setbin (ret, file, (XML_SETBIN_FREE_FN *)fclose); xml_set (ret, "content-type", "text/plain"); return (ret); } |
XML * LIST_mysql_attach_write (WFTK_ADAPTOR * ad, va_list args) { void * buffer; int size, number; XML * handle; if (!args) { xml_set (ad->parms, "error", "No arguments given."); return NULL; } buffer = va_arg (args, void *); size = va_arg (args, int); number = va_arg (args, int); handle = va_arg (args, XML *); xml_setnum (handle, "last-write", fwrite (buffer, size, number, xml_getbin(handle))); return NULL; } XML * LIST_mysql_attach_cancel (WFTK_ADAPTOR * ad, va_list args) { XML * handle; if (!args) { xml_set (ad->parms, "error", "No arguments given."); return NULL; } handle = va_arg (args, XML *); fclose (xml_getbin (handle)); unlink (xml_attrval (handle, "tempfile")); return NULL; } XML * LIST_mysql_attach_close (WFTK_ADAPTOR * ad, va_list args) { XML * handle; if (!args) { xml_set (ad->parms, "error", "No arguments given."); return NULL; } handle = va_arg (args, XML *); fclose (xml_getbin (handle)); rename (xml_attrval (handle, "tempfile"), xml_attrval (handle, "file")); return NULL; } |
XML * LIST_mysql_retrieve_open (WFTK_ADAPTOR * ad, va_list args) { XML * list = NULL; XML * fld; char * key; char * field; XML * mark; XML * ret; FILE * file; WFTK_ADAPTOR * ad2; if (args) list = va_arg (args, XML *); if (!list) { xml_set (ad->parms, "error", "No list given."); return (XML *) 0; } key = va_arg (args, char *); fld = va_arg (args, XML *); ret = xml_create ("attachment-handle"); xml_set (ret, "dir", xml_attrval (ad->parms, "basedir")); if (!*xml_attrval (ad->parms, "subdir")) xml_set (ad->parms, "subdir", xml_attrval (list, "id")); if (strcmp (xml_attrval (ad->parms, "subdir"), ".")) { xml_attrcat (ret, "dir", xml_attrval (ad->parms, "subdir")); xml_attrcat (ret, "dir", "/"); } xml_setf (ret, "adaptor", "mysql:%s", xml_attrval (ret, "dir")); if (fld) { xml_set (ret, "location", xml_attrval (fld, "location")); } if (!*xml_attrval (ret, "location")) xml_setf (ret, "location", "_att_%s_%s.dat", key, xml_attrval (fld, "id")); xml_setf (ret, "file", "%s%s", xml_attrval (ret, "dir"), xml_attrval (ret, "location")); file = fopen (xml_attrval (ret, "file"), "r"); if (!file) { xml_setf (ad->parms, "error", "Unable to open file %s for reading.", xml_attrval (ret, "location")); xml_free (ret); return NULL; } xml_setbin (ret, file, (XML_SETBIN_FREE_FN *)fclose); xml_set (ret, "content-type", "text/plain"); return (ret); } XML * LIST_mysql_retrieve_read (WFTK_ADAPTOR * ad, va_list args) { void * buffer; int size, number; XML * handle; if (!args) { xml_set (ad->parms, "error", "No arguments given."); return NULL; } buffer = va_arg (args, void *); size = va_arg (args, int); number = va_arg (args, int); handle = va_arg (args, XML *); xml_setnum (handle, "last-read", fread (buffer, size, number, xml_getbin(handle))); return NULL; } XML * LIST_mysql_retrieve_close (WFTK_ADAPTOR * ad, va_list args) { XML * handle; if (!args) { xml_set (ad->parms, "error", "No arguments given."); return NULL; } handle = va_arg (args, XML *); fclose (xml_getbin (handle)); return NULL; } |
This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2002-2004, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. This presentation was prepared with LPML. Try literate programming. You'll like it. |