builder.c

00001 /********************************************************************\
00002  * builder.c : compile SQL queries from C language data             *
00003  * Copyright (C) 2001 Linas Vepstas <linas@linas.org>               *
00004  *                                                                  *
00005  * This program is free software; you can redistribute it and/or    *
00006  * modify it under the terms of the GNU General Public License as   *
00007  * published by the Free Software Foundation; either version 2 of   *
00008  * the License, or (at your option) any later version.              *
00009  *                                                                  *
00010  * This program is distributed in the hope that it will be useful,  *
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00013  * GNU General Public License for more details.                     *
00014  *                                                                  *
00015  * You should have received a copy of the GNU General Public License*
00016  * along with this program; if not, contact:                        *
00017  *                                                                  *
00018  * Free Software Foundation           Voice:  +1-617-542-5942       *
00019  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00020  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00021 \********************************************************************/
00022 
00023 /*
00024  * FILE:
00025  * builder.c
00026  *
00027  * FUNCTION:
00028  * generic postgres backend query builder
00029  * compiles data types into sql queries
00030  *
00031  * Note: Postgres documentation states that the 
00032  * maximum length of a query is 8192 bytes, and that
00033  * longer queries are ignored ...
00034  *
00035  * TBD hack alert XXX FIXME:
00036  * -- check for buffer overflow at end of each setter
00037  */
00038 
00039 #include "config.h"
00040 #include <glib.h>
00041 #include <string.h>
00042 
00043 #include "qof.h"
00044 #include "escape.h"
00045 #include "builder.h"
00046 #include "gnc-engine.h"
00047 
00048 static QofLogModule log_module = GNC_MOD_BACKEND; 
00049 
00050 /* ================================================ */
00051 
00052 struct _builder {
00053    sqlBuild_QType qtype;
00054 
00055    /* pointers the the tail end of two different assembly areas */
00056    char * ptag;
00057    char * pval;
00058 
00059    /* sql needs commas to separate values */
00060    short  tag_need_comma;
00061    short  val_need_comma;
00062    short  where_need_and;
00063 
00064    /* pointers to the start of two different assembly areas. */
00065    char * tag_base;
00066    char * val_base;
00067    size_t buflen;
00068 
00069    /* pointer to temp memory used for escaping arguments */
00070    sqlEscape *escape;
00071 };
00072 
00073 /* ================================================ */
00074 
00075 #define INITIAL_BUFSZ 16300
00076 
00077 sqlBuilder *
00078 sqlBuilder_new (void)
00079 {
00080    sqlBuilder *b = g_new (sqlBuilder, 1);
00081 
00082    b->qtype = SQL_INSERT;
00083 
00084    b->tag_base = g_malloc (INITIAL_BUFSZ);
00085    b->val_base = g_malloc (INITIAL_BUFSZ);
00086    b->buflen = INITIAL_BUFSZ;
00087 
00088    b->ptag = b->tag_base;
00089    b->pval = b->val_base;
00090 
00091    /* null terminated strings */
00092    *(b->ptag) = 0x0;
00093    *(b->pval) = 0x0;
00094 
00095    b->tag_need_comma = 0;
00096    b->val_need_comma = 0;
00097    b->where_need_and = 0;
00098 
00099    /* the escape area */
00100    b->escape = sqlEscape_new ();
00101    return (b);
00102 }
00103 
00104 /* ================================================ */
00105 
00106 void
00107 sqlBuilder_destroy (sqlBuilder *b)
00108 {
00109    if (!b) return;
00110    g_free (b->tag_base);   b->tag_base = NULL;
00111    g_free (b->val_base);   b->val_base = NULL;
00112    sqlEscape_destroy (b->escape);     b->escape = NULL;
00113    g_free (b);
00114 }
00115 
00116 /* ================================================ */
00117 
00118 void
00119 sqlBuild_Table (sqlBuilder *b, const char *tablename, sqlBuild_QType qt)
00120 {
00121    if (!b || !tablename) return;
00122    b->qtype = qt;
00123 
00124    b->ptag = b->tag_base;
00125    b->pval = b->val_base;
00126 
00127    /* null terminated strings */
00128    *(b->ptag) = 0x0;
00129    *(b->pval) = 0x0;
00130 
00131    b->tag_need_comma = 0;
00132    b->val_need_comma = 0;
00133    b->where_need_and = 0;
00134 
00135    switch (qt) 
00136    {
00137       case SQL_INSERT:
00138          b->ptag = stpcpy(b->ptag, "INSERT INTO ");
00139          b->ptag = stpcpy(b->ptag, tablename);
00140          b->ptag = stpcpy(b->ptag, " (");
00141 
00142          b->pval = stpcpy(b->pval, ") VALUES (");
00143          break;
00144 
00145       case SQL_UPDATE:
00146          b->ptag = stpcpy(b->ptag, "UPDATE ");
00147          b->ptag = stpcpy(b->ptag, tablename);
00148          b->ptag = stpcpy(b->ptag, " SET ");
00149 
00150          b->pval = stpcpy(b->pval, " WHERE ");
00151          break;
00152 
00153       case SQL_SELECT:
00154          b->ptag = stpcpy(b->ptag, "SELECT ");
00155 
00156          b->pval = stpcpy(b->pval, " FROM ");
00157          b->pval = stpcpy(b->pval, tablename);
00158          b->pval = stpcpy(b->pval, " WHERE ");
00159          break;
00160 
00161       case SQL_DELETE:
00162          b->ptag = stpcpy(b->ptag, "DELETE ");
00163 
00164          b->pval = stpcpy(b->pval, " FROM ");
00165          b->pval = stpcpy(b->pval, tablename);
00166          b->pval = stpcpy(b->pval, " WHERE ");
00167          break;
00168 
00169    };
00170 
00171 }
00172 
00173 /* ================================================ */
00174 /* note that val may be NULL if a SELECT statement in being built */
00175 
00176 void
00177 sqlBuild_Set_Str (sqlBuilder *b, const char *tag, const char *val)
00178 {
00179    if (!b || !tag) return;
00180    if (!val) val= "";
00181 
00182    val = sqlEscapeString (b->escape, val);
00183 
00184    if (b->tag_need_comma) b->ptag = stpcpy(b->ptag, ", ");
00185    b->tag_need_comma = 1;
00186 
00187    switch (b->qtype) 
00188    {
00189       case SQL_INSERT:
00190          b->ptag = stpcpy(b->ptag, tag);
00191 
00192          if (b->val_need_comma) b->pval = stpcpy(b->pval, ", ");
00193          b->val_need_comma = 1;
00194          b->pval = stpcpy(b->pval, "'");
00195          b->pval = stpcpy(b->pval, val);
00196          b->pval = stpcpy(b->pval, "'");
00197          break;
00198 
00199       case SQL_UPDATE:
00200          b->ptag = stpcpy(b->ptag, tag);
00201          b->ptag = stpcpy(b->ptag, "='");
00202          b->ptag = stpcpy(b->ptag, val);
00203          b->ptag = stpcpy(b->ptag, "' ");
00204          break;
00205 
00206       case SQL_SELECT:
00207          b->ptag = stpcpy(b->ptag, tag);
00208          break;
00209 
00210       case SQL_DELETE:
00211          break;
00212 
00213       default:
00214          PERR ("mustn't happen");
00215    };
00216    
00217 }
00218 
00219 /* ================================================ */
00220 
00221 void
00222 sqlBuild_Set_Char (sqlBuilder *b, const char *tag, char val)
00223 {
00224   char buf[2];
00225   buf[0] = val;
00226   buf[1] = 0x0;
00227   sqlBuild_Set_Str (b, tag, buf);
00228 }
00229 
00230 /* ================================================ */
00231 
00232 void
00233 sqlBuild_Set_GUID (sqlBuilder *b, const char *tag, const GUID *val)
00234 {
00235   if (val) {
00236      char guid_str[GUID_ENCODING_LENGTH+1];
00237      guid_to_string_buff(val, guid_str);
00238      sqlBuild_Set_Str (b, tag, guid_str);
00239   } else {
00240      /* if a SELECT statement is being built, then val may be null */
00241      sqlBuild_Set_Str (b, tag, "");
00242   }
00243 }
00244 
00245 /* ================================================ */
00246 
00247 void
00248 sqlBuild_Set_Date (sqlBuilder *b, const char *tag, Timespec ts)
00249 {
00250   char buf[120];
00251   gnc_timespec_to_iso8601_buff (ts, buf);
00252   sqlBuild_Set_Str (b, tag, buf);
00253 }
00254 
00255 /* ================================================ */
00256 
00257 void
00258 sqlBuild_Set_Double (sqlBuilder *b, const char *tag, double flt)
00259 {
00260   char buf[120];
00261   snprintf (buf, 120, SQL_DBL_FMT, flt);
00262   sqlBuild_Set_Str (b, tag, buf);
00263 }
00264 
00265 /* ================================================ */
00266 
00267 void
00268 sqlBuild_Set_Int64 (sqlBuilder *b, const char *tag, gint64 nval)
00269 {
00270    char val[100];
00271    if (!b || !tag) return;
00272 
00273    snprintf (val, 100, "%" G_GINT64_FORMAT, nval);
00274    if (b->tag_need_comma) b->ptag = stpcpy(b->ptag, ", ");
00275    b->tag_need_comma = 1;
00276 
00277    switch (b->qtype) 
00278    {
00279       case SQL_INSERT:
00280          b->ptag = stpcpy(b->ptag, tag);
00281 
00282          if (b->val_need_comma) b->pval = stpcpy(b->pval, ", ");
00283          b->val_need_comma = 1;
00284          b->pval = stpcpy(b->pval, val);
00285          break;
00286 
00287       case SQL_UPDATE:
00288          b->ptag = stpcpy(b->ptag, tag);
00289          b->ptag = stpcpy(b->ptag, "=");
00290          b->ptag = stpcpy(b->ptag, val);
00291          break;
00292 
00293       case SQL_SELECT:
00294          b->ptag = stpcpy(b->ptag, tag);
00295          break;
00296 
00297       case SQL_DELETE:
00298          break;
00299 
00300       default:
00301          PERR ("mustn't happen");
00302    };
00303 }
00304 
00305 /* ================================================ */
00306 
00307 void
00308 sqlBuild_Set_Int32 (sqlBuilder *b, const char *tag, gint32 nval)
00309 {
00310    sqlBuild_Set_Int64 (b, tag, (gint64) nval);
00311 }
00312 
00313 /* ================================================ */
00314 
00315 void
00316 sqlBuild_Where_Str (sqlBuilder *b, const char *tag, const char *val)
00317 {
00318    if (!b || !tag || !val) return;
00319 
00320    switch (b->qtype) 
00321    {
00322       case SQL_INSERT:
00323          /* ther is no where clasue, so we do the set as a utility */
00324          sqlBuild_Set_Str (b, tag, val);
00325          break;
00326 
00327       case SQL_UPDATE:
00328       case SQL_SELECT:
00329       case SQL_DELETE:
00330          val = sqlEscapeString (b->escape, val);
00331 
00332          if (b->where_need_and) b->pval = stpcpy(b->pval, " AND ");
00333          b->where_need_and = 1;
00334 
00335          b->pval = stpcpy(b->pval, tag);
00336          b->pval = stpcpy(b->pval, "='");
00337          b->pval = stpcpy(b->pval, val);
00338          b->pval = stpcpy(b->pval, "'");
00339 
00340          break;
00341 
00342       default:
00343          PERR ("mustn't happen");
00344    };
00345 }
00346 
00347 /* ================================================ */
00348 
00349 void
00350 sqlBuild_Where_GUID (sqlBuilder *b, const char *tag, const GUID *val)
00351 {
00352   char guid_str[GUID_ENCODING_LENGTH+1];
00353   guid_to_string_buff(val, guid_str);
00354   sqlBuild_Where_Str (b, tag, guid_str);
00355 }
00356 
00357 /* ================================================ */
00358 
00359 void
00360 sqlBuild_Where_Int32 (sqlBuilder *b, const char *tag, gint32 val)
00361 {
00362   char str[40];
00363   snprintf (str, 40, "%d", val);
00364   sqlBuild_Where_Str (b, tag, str);
00365 }
00366 
00367 /* ================================================ */
00368 
00369 const char *
00370 sqlBuild_Query (sqlBuilder *b)
00371 {
00372    if (!b) return NULL;
00373 
00374    switch (b->qtype) 
00375    {
00376       case SQL_INSERT:
00377          b->ptag = stpcpy(b->ptag, b->val_base);
00378          b->ptag = stpcpy(b->ptag, ");");
00379          break;
00380 
00381       case SQL_UPDATE:
00382       case SQL_SELECT:
00383       case SQL_DELETE:
00384          b->ptag = stpcpy(b->ptag, b->val_base);
00385          b->ptag = stpcpy(b->ptag, ";");
00386          break;
00387 
00388       default:
00389          PERR ("mustn't happen");
00390    };
00391    
00392    PINFO ("%s\n", b->tag_base);
00393    return b->tag_base;
00394 }
00395 
00396 /* ================ END OF FILE ==================== */

Generated on Sun Oct 12 05:06:59 2008 for GnuCash by  doxygen 1.5.2