QOF 0.8.4
qofsql.c
Go to the documentation of this file.
00001 /********************************************************************\
00002  * qofsql.c -- QOF client-side SQL parser                           *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020  *                                                                  *
00021 \********************************************************************/
00022 
00034 #include "config.h"
00035 #include <stdlib.h>             /* for working atoll */
00036 #include <errno.h>
00037 #include <glib.h>
00038 #include <libintl.h>
00039 #ifdef HAVE_GDA
00040 #include <libsql/sql_parser.h>
00041 #else
00042 #include "sql_parser.h"
00043 #endif
00044 #include <time.h>
00045 #include "qof.h"
00046 #include "qofsql-p.h"
00047 #include "qofquery-p.h"
00048 
00049 #define _(String) dgettext (GETTEXT_PACKAGE, String)
00050 
00051 static gchar * kvp_table_name = NULL;
00052 
00053 #define QSQL_KVP_TABLE "sql_kvp"
00054 
00055 #define END_DB_VERSION " dbversion int );"
00056 
00057 static QofLogModule log_module = QOF_MOD_QUERY;
00058 
00059 /* =================================================================== */
00060 
00061 struct _QofSqlQuery
00062 {
00063     sql_statement *parse_result;
00064     QofQuery *qof_query;
00065     QofBook *book;
00066     gchar *single_global_tablename;
00067     KvpFrame *kvp_join;
00068     GList *param_list;
00069     QofEntity *inserted_entity;
00070 };
00071 
00072 /* ========================================================== */
00073 
00074 QofSqlQuery *
00075 qof_sql_query_new (void)
00076 {
00077     QofSqlQuery *sqn = (QofSqlQuery *) g_new0 (QofSqlQuery, 1);
00078 
00079     sqn->qof_query = NULL;
00080     sqn->parse_result = NULL;
00081     sqn->book = NULL;
00082     sqn->single_global_tablename = NULL;
00083     sqn->kvp_join = NULL;
00084 
00085     return sqn;
00086 }
00087 
00088 /* ========================================================== */
00089 
00090 void
00091 qof_sql_query_destroy (QofSqlQuery * q)
00092 {
00093     if (!q)
00094         return;
00095     qof_query_destroy (q->qof_query);
00096     sql_destroy (q->parse_result);
00097     g_free (q);
00098 }
00099 
00100 /* ========================================================== */
00101 
00102 QofQuery *
00103 qof_sql_query_get_query (QofSqlQuery * q)
00104 {
00105     if (!q)
00106         return NULL;
00107     return q->qof_query;
00108 }
00109 
00110 /* ========================================================== */
00111 
00112 void
00113 qof_sql_query_set_book (QofSqlQuery * q, QofBook * book)
00114 {
00115     if (!q)
00116         return;
00117     q->book = book;
00118 }
00119 
00120 /* ========================================================== */
00121 
00122 void
00123 qof_sql_query_set_kvp (QofSqlQuery * q, KvpFrame * kvp)
00124 {
00125     if (!q)
00126         return;
00127     q->kvp_join = kvp;
00128 }
00129 
00130 /* ========================================================== */
00131 
00132 static inline void
00133 get_table_and_param (char *str, char **tab, char **param)
00134 {
00135     char *end = strchr (str, '.');
00136     if (!end)
00137     {
00138         *tab = 0;
00139         *param = str;
00140         return;
00141     }
00142     *end = 0;
00143     *tab = str;
00144     *param = end + 1;
00145 }
00146 
00147 static inline char *
00148 dequote_string (char *str)
00149 {
00150     size_t len;
00151     /* strip out quotation marks ...  */
00152     if (('\'' == str[0]) || ('\"' == str[0]))
00153     {
00154         str++;
00155         len = strlen (str);
00156         str[len - 1] = 0;
00157     }
00158     return str;
00159 }
00160 
00161 static QofQuery *
00162 handle_single_condition (QofSqlQuery * query, sql_condition * cond)
00163 {
00164     gchar tmpbuff[128];
00165     GSList *param_list;
00166     GList *guid_list;
00167     QofQueryPredData *pred_data;
00168     sql_field_item *sparam, *svalue;
00169     gchar *qparam_name, *qvalue_name, *table_name, *param_name;
00170     gchar *sep, *path, *str, *p;
00171     QofQuery *qq;
00172     KvpValue *kv, *kval;
00173     KvpValueType kvt;
00174     QofQueryCompare qop;
00175     guint len;
00176     QofType param_type;
00177     QofGuidMatch gm;
00178 
00179     pred_data = NULL;
00180     if (NULL == cond)
00181     {
00182         PWARN ("missing condition");
00183         return NULL;
00184     }
00185     /* -------------------------------- */
00186     /* field to match, assumed, for now to be on the left */
00187     /* XXX fix this so it can be either left or right */
00188     if (NULL == cond->d.pair.left)
00189     {
00190         PWARN ("missing left parameter");
00191         return NULL;
00192     }
00193     sparam = cond->d.pair.left->item;
00194     if (SQL_name != sparam->type)
00195     {
00196         PWARN ("we support only parameter names at this time (parsed %d)",
00197             sparam->type);
00198         return NULL;
00199     }
00200     qparam_name = sparam->d.name->data;
00201     if (NULL == qparam_name)
00202     {
00203         PWARN ("missing parameter name");
00204         return NULL;
00205     }
00206 
00207     /* -------------------------------- */
00208     /* value to match, assumed, for now, to be on the right. */
00209     /* XXX fix this so it can be either left or right */
00210     if (NULL == cond->d.pair.right)
00211     {
00212         PWARN ("missing right parameter");
00213         return NULL;
00214     }
00215     svalue = cond->d.pair.right->item;
00216     if (SQL_name != svalue->type)
00217     {
00218         PWARN ("we support only simple values (parsed as %d)",
00219             svalue->type);
00220         return NULL;
00221     }
00222     qvalue_name = svalue->d.name->data;
00223     if (NULL == qvalue_name)
00224     {
00225         PWARN ("missing value");
00226         return NULL;
00227     }
00228     qvalue_name = dequote_string (qvalue_name);
00229     qvalue_name = (char *) qof_util_whitespace_filter (qvalue_name);
00230 
00231     /* Look to see if its the special KVP value holder.
00232      * If it is, look up the value. */
00233     if (0 == strncasecmp (qvalue_name, "kvp://", 6))
00234     {
00235         if (NULL == query->kvp_join)
00236         {
00237             PWARN ("missing kvp frame");
00238             return NULL;
00239         }
00240         kv = kvp_frame_get_value (query->kvp_join, qvalue_name + 5);
00241         /* If there's no value, its not an error; 
00242          * we just don't do this predicate */
00243         if (!kv)
00244             return NULL;
00245         kvt = kvp_value_get_type (kv);
00246 
00247         tmpbuff[0] = 0x0;
00248         qvalue_name = tmpbuff;
00249         switch (kvt)
00250         {
00251         case KVP_TYPE_GINT64:
00252         {
00253                 gint64 ival = kvp_value_get_gint64 (kv);
00254                 sprintf (tmpbuff, "%" G_GINT64_FORMAT "\n", ival);
00255                 break;
00256         }
00257         case KVP_TYPE_DOUBLE:
00258         {
00259                 double ival = kvp_value_get_double (kv);
00260                 sprintf (tmpbuff, "%26.18g\n", ival);
00261                 break;
00262         }
00263         case KVP_TYPE_STRING:
00264             /* If there's no value, its not an error; 
00265              * we just don't do this predicate */
00266             qvalue_name = kvp_value_get_string (kv);
00267             if (!qvalue_name)
00268                 return NULL;
00269             break;
00270         case KVP_TYPE_GUID:
00271         case KVP_TYPE_TIME :
00272         case KVP_TYPE_BOOLEAN :
00273         case KVP_TYPE_BINARY:
00274         case KVP_TYPE_GLIST:
00275         case KVP_TYPE_NUMERIC:
00276         case KVP_TYPE_FRAME:
00277             PWARN ("unhandled kvp type=%d", kvt);
00278             return NULL;
00279         }
00280     }
00281 
00282     /* -------------------------------- */
00283     /* Now start building the QOF parameter */
00284     param_list = qof_query_build_param_list (qparam_name, NULL);
00285 
00286     /* Get the where-term comparison operator */
00287     switch (cond->op)
00288     {
00289     case SQL_eq:
00290         qop = QOF_COMPARE_EQUAL;
00291         break;
00292     case SQL_gt:
00293         qop = QOF_COMPARE_GT;
00294         break;
00295     case SQL_lt:
00296         qop = QOF_COMPARE_LT;
00297         break;
00298     case SQL_geq:
00299         qop = QOF_COMPARE_GTE;
00300         break;
00301     case SQL_leq:
00302         qop = QOF_COMPARE_LTE;
00303         break;
00304     case SQL_diff:
00305         qop = QOF_COMPARE_NEQ;
00306         break;
00307     default:
00308         /* XXX for string-type queries, we should be able to
00309          * support 'IN' for substring search.  Also regex. */
00310         PWARN ("Unsupported compare op (parsed as %u)", cond->op);
00311         return NULL;
00312     }
00313 
00314     /* OK, need to know the type of the thing being matched 
00315      * in order to build the correct predicate.  Get the type 
00316      * from the object parameters. */
00317     get_table_and_param (qparam_name, &table_name, &param_name);
00318     if (NULL == table_name)
00319     {
00320         table_name = query->single_global_tablename;
00321     }
00322     if (NULL == table_name)
00323     {
00324         PWARN ("Need to specify an object class to query");
00325         return NULL;
00326     }
00327 
00328     if (FALSE == qof_class_is_registered (table_name))
00329     {
00330         PWARN ("The query object \'%s\' is not known", table_name);
00331         return NULL;
00332     }
00333 
00334     param_type = qof_class_get_parameter_type (table_name, param_name);
00335     if (!param_type)
00336     {
00337         PWARN ("The parameter \'%s\' on object \'%s\' is not known",
00338             param_name, table_name);
00339         return NULL;
00340     }
00341 
00342     if (!strcmp (param_type, QOF_TYPE_STRING))
00343     {
00344         pred_data = qof_query_string_predicate (qop,    /* comparison to make */
00345             qvalue_name,        /* string to match */
00346             QOF_STRING_MATCH_CASEINSENSITIVE,   /* case matching */
00347             FALSE);             /* use_regexp */
00348     }
00349     else if (!strcmp (param_type, QOF_TYPE_CHAR))
00350     {
00351         QofCharMatch cm = QOF_CHAR_MATCH_ANY;
00352         if (QOF_COMPARE_NEQ == qop)
00353             cm = QOF_CHAR_MATCH_NONE;
00354         pred_data = qof_query_char_predicate (cm, qvalue_name);
00355     }
00356     else if (!strcmp (param_type, QOF_TYPE_INT32))
00357     {
00358         gint32 ival = atoi (qvalue_name);
00359         pred_data = qof_query_int32_predicate (qop, ival);
00360     }
00361     else if (!strcmp (param_type, QOF_TYPE_INT64))
00362     {
00363         gint64 ival = atoll (qvalue_name);
00364         pred_data = qof_query_int64_predicate (qop, ival);
00365     }
00366     else if (!strcmp (param_type, QOF_TYPE_DOUBLE))
00367     {
00368         double ival = atof (qvalue_name);
00369         pred_data = qof_query_double_predicate (qop, ival);
00370     }
00371     else if (!strcmp (param_type, QOF_TYPE_BOOLEAN))
00372     {
00373         gboolean ival = qof_util_bool_to_int (qvalue_name);
00374         pred_data = qof_query_boolean_predicate (qop, ival);
00375     }
00376     else if (!safe_strcmp (param_type, QOF_TYPE_TIME))
00377     {
00378         QofDate *qd;
00379         QofTime *qt;
00380 
00381         qd = qof_date_parse (qvalue_name, QOF_DATE_FORMAT_UTC);
00382         qt = qof_date_to_qtime (qd);
00383         qof_date_free (qd);
00384         pred_data = 
00385             qof_query_time_predicate (qop, QOF_DATE_MATCH_NORMAL, 
00386             qt);
00387     }
00388     else if (!strcmp (param_type, QOF_TYPE_NUMERIC))
00389     {
00390         QofNumeric ival;
00391         qof_numeric_from_string (qvalue_name, &ival);
00392         pred_data =
00393             qof_query_numeric_predicate (qop, QOF_NUMERIC_MATCH_ANY, ival);
00394     }
00395     else if (!strcmp (param_type, QOF_TYPE_DEBCRED))
00396     {
00397         /* DEBCRED is likely to be deprecated before libqof2 */
00398         QofNumeric ival;
00399         qof_numeric_from_string (qvalue_name, &ival);
00400         pred_data =
00401             qof_query_numeric_predicate (qop, QOF_NUMERIC_MATCH_ANY, ival);
00402     }
00403     else if (!strcmp (param_type, QOF_TYPE_GUID))
00404     {
00405         GUID guid;
00406         gboolean rc = string_to_guid (qvalue_name, &guid);
00407         if (0 == rc)
00408         {
00409             PWARN ("unable to parse guid: %s", qvalue_name);
00410             return NULL;
00411         }
00412 
00413         // XXX less, than greater than don't make sense,
00414         // should check for those bad conditions
00415 
00416         gm = QOF_GUID_MATCH_ANY;
00417         if (QOF_COMPARE_NEQ == qop)
00418             gm = QOF_GUID_MATCH_NONE;
00419         guid_list = g_list_append (NULL, &guid);
00420         pred_data = qof_query_guid_predicate (gm, guid_list);
00421 
00422         g_list_free (guid_list);
00423     }
00424     else if (!strcmp (param_type, QOF_TYPE_KVP))
00425     {
00426         /* We are expecting an encoded value that looks like
00427          * /some/path/string:value
00428          */
00429         sep = strchr (qvalue_name, ':');
00430         if (!sep)
00431             return NULL;
00432         *sep = 0;
00433         path = qvalue_name;
00434         str = sep + 1;
00435         /* If str has only digits, we know its a plain number.
00436          * If its numbers and a decimal point, assume a float
00437          * If its numbers and a slash, assume numeric
00438          * If its 32 bytes of hex, assume GUID
00439          * If it looks like an iso date ... 
00440          * else assume its a string.
00441          */
00442         kval = NULL;
00443         len = strlen (str);
00444         if ((32 == len) && (32 == strspn (str, "0123456789abcdef")))
00445         {
00446             GUID guid;
00447             string_to_guid (str, &guid);
00448             kval = kvp_value_new_guid (&guid);
00449         }
00450         else if (len == strspn (str, "0123456789"))
00451         {
00452             kval = kvp_value_new_gint64 (atoll (str));
00453         }
00454         else if ((p = strchr (str, '.')) &&
00455             ((len - 1) == (strspn (str, "0123456789") +
00456                     strspn (p + 1, "0123456789"))))
00457         {
00458             kval = kvp_value_new_double (atof (str));
00459         }
00460 
00461         else if ((p = strchr (str, '/')) &&
00462             ((len - 1) == (strspn (str, "0123456789") +
00463                     strspn (p + 1, "0123456789"))))
00464         {
00465             QofNumeric num;
00466             qof_numeric_from_string (str, &num);
00467             kval = kvp_value_new_numeric (num);
00468         }
00469         else if ((p = strchr (str, '-')) &&
00470             (p = strchr (p + 1, '-')) &&
00471             (p = strchr (p + 1, ' ')) &&
00472             (p = strchr (p + 1, ':')) && (p = strchr (p + 1, ':')))
00473         {
00474             QofDate *qd;
00475             QofTime *qt;
00476 
00477             qd = qof_date_parse (str, QOF_DATE_FORMAT_UTC);
00478             qt = qof_date_to_qtime (qd);
00479             kval =
00480                 kvp_value_new_time (qt);
00481             qof_date_free (qd);
00482         }
00483 
00484         /* The default handler is a string */
00485         if (NULL == kval)
00486         {
00487             kval = kvp_value_new_string (str);
00488         }
00489         pred_data = qof_query_kvp_predicate_path (qop, path, kval);
00490     }
00491     else
00492     {
00493         PWARN ("The predicate type \"%s\" is unsupported for now",
00494             param_type);
00495         return NULL;
00496     }
00497 
00498     qq = qof_query_create ();
00499     qof_query_add_term (qq, param_list, pred_data, QOF_QUERY_FIRST_TERM);
00500     return qq;
00501 }
00502 
00503 /* ========================================================== */
00504 
00505 static QofQuery *
00506 handle_where (QofSqlQuery * query, sql_where * swear)
00507 {
00508     QofQueryOp qop;
00509     QofQuery *qq;
00510 
00511     switch (swear->type)
00512     {
00513     case SQL_pair:
00514         {
00515             QofQuery *qleft = handle_where (query, swear->d.pair.left);
00516             QofQuery *qright = handle_where (query, swear->d.pair.right);
00517             if (NULL == qleft)
00518                 return qright;
00519             if (NULL == qright)
00520                 return qleft;
00521             switch (swear->d.pair.op)
00522             {
00523             case SQL_and:
00524                 qop = QOF_QUERY_AND;
00525                 break;
00526             case SQL_or:
00527                 qop = QOF_QUERY_OR;
00528                 break;
00529                 /* XXX should add support for nand, nor, xor */
00530             default:
00531                 qof_query_destroy (qleft);
00532                 qof_query_destroy (qright);
00533                 return NULL;
00534             }
00535             qq = qof_query_merge (qleft, qright, qop);
00536             qof_query_destroy (qleft);
00537             qof_query_destroy (qright);
00538             return qq;
00539         }
00540     case SQL_negated:
00541         {
00542             QofQuery *qq = handle_where (query, swear->d.negated);
00543             QofQuery *qneg = qof_query_invert (qq);
00544             qof_query_destroy (qq);
00545             return qneg;
00546         }
00547 
00548     case SQL_single:
00549         {
00550             sql_condition *cond = swear->d.single;
00551             return handle_single_condition (query, cond);
00552         }
00553     }
00554     return NULL;
00555 }
00556 
00557 /* ========================================================== */
00558 
00559 static void
00560 handle_sort_order (QofSqlQuery * query, GList * sorder_list)
00561 {
00562     GSList *qsp[3];
00563     GList *n;
00564     gboolean direction[3];
00565     int i;
00566     sql_order_field *sorder;
00567     char *qparam_name;
00568 
00569     if (!sorder_list)
00570         return;
00571 
00572     for (i = 0; i < 3; i++)
00573     {
00574         qsp[i] = NULL;
00575         direction[i] = 0;
00576 
00577         if (sorder_list)
00578         {
00579             sorder = sorder_list->data;
00580 
00581             /* Set the sort direction */
00582             if (SQL_asc == sorder->order_type)
00583                 direction[i] = TRUE;
00584 
00585             /* Find the parameter name */
00586             qparam_name = NULL;
00587             n = sorder->name;
00588             if (n)
00589             {
00590                 qparam_name = n->data;
00591                 if (qparam_name)
00592                 {
00593                     qsp[i] =
00594                         qof_query_build_param_list (qparam_name, NULL);
00595                 }
00596                 n = n->next;    /* next parameter */
00597             }
00598             else
00599             {
00600                 /* if no next parameter, then next order-by */
00601                 sorder_list = sorder_list->next;
00602             }
00603         }
00604     }
00605 
00606     qof_query_set_sort_order (query->qof_query, qsp[0], qsp[1], qsp[2]);
00607     qof_query_set_sort_increasing (query->qof_query, direction[0],
00608         direction[1], direction[2]);
00609 }
00610 
00611 /* INSERT INTO handlers =================================================== */
00612 
00613 static void
00614 qof_sql_insertCB (const QofParam * param, const gchar * insert_string,
00615     QofSqlQuery * query)
00616 {
00617     QofIdTypeConst type;
00618     sql_insert_statement *sis;
00619     gboolean G_GNUC_UNUSED registered_type;
00620     QofEntity *ent;
00621     /* cm_ prefix used for variables that hold the data to commit */
00622     QofNumeric cm_numeric;
00623     gdouble cm_double;
00624     gboolean cm_boolean;
00625     gint32 cm_i32;
00626     gint64 cm_i64;
00627     gchar cm_char, *tail;
00628     GUID *cm_guid;
00629 /*  KvpFrame       *cm_kvp;
00630     KvpValue       *cm_value;
00631     KvpValueType   cm_type;*/
00632     void (*string_setter) (QofEntity *, const gchar *);
00633     void (*time_setter) (QofEntity *, QofTime *);
00634     void (*numeric_setter) (QofEntity *, QofNumeric);
00635     void (*double_setter) (QofEntity *, gdouble);
00636     void (*boolean_setter) (QofEntity *, gboolean);
00637     void (*i32_setter) (QofEntity *, gint32);
00638     void (*i64_setter) (QofEntity *, gint64);
00639     void (*char_setter) (QofEntity *, gchar);
00640 /*  void (*kvp_frame_setter) (QofEntity*, KvpFrame*);*/
00641 
00642     g_return_if_fail (param || insert_string || query);
00643     ent = query->inserted_entity;
00644     sis = query->parse_result->statement;
00645     type = g_strdup_printf ("%s", sis->table->d.simple);
00646 
00647     ENTER (" param=%s param_type=%s type=%s content=%s",
00648         param->param_name, param->param_type, type, insert_string);
00649     if (safe_strcmp (param->param_type, QOF_TYPE_STRING) == 0)
00650     {
00651         string_setter =
00652             (void (*)(QofEntity *, const char *)) param->param_setfcn;
00653         if (string_setter != NULL)
00654         {
00655             string_setter (ent, insert_string);
00656         }
00657         registered_type = TRUE;
00658     }
00659     if (safe_strcmp (param->param_type, QOF_TYPE_TIME) == 0)
00660     {
00661         QofDate *qd;
00662         QofTime *qt;
00663         time_setter = 
00664             (void (*)(QofEntity *, QofTime *)) param->param_setfcn;
00665         qd = qof_date_parse (insert_string, QOF_DATE_FORMAT_UTC);
00666         qt = qof_date_to_qtime (qd);
00667         if((time_setter != NULL) && (qof_time_is_valid(qt)))
00668         {
00669             time_setter (ent, qt);
00670         }
00671     }
00672     if ((safe_strcmp (param->param_type, QOF_TYPE_NUMERIC) == 0) ||
00673         (safe_strcmp (param->param_type, QOF_TYPE_DEBCRED) == 0))
00674     {
00675         numeric_setter =
00676             (void (*)(QofEntity *, QofNumeric)) param->param_setfcn;
00677         qof_numeric_from_string (insert_string, &cm_numeric);
00678         if (numeric_setter != NULL)
00679         {
00680             numeric_setter (ent, cm_numeric);
00681         }
00682     }
00683     if (safe_strcmp (param->param_type, QOF_TYPE_GUID) == 0)
00684     {
00685         cm_guid = g_new (GUID, 1);
00686         if (TRUE != string_to_guid (insert_string, cm_guid))
00687         {
00688             LEAVE (" string to guid failed for %s", insert_string);
00689             return;
00690         }
00691 /*          reference_type = xmlGetProp(node, QSF_OBJECT_TYPE);
00692         if(0 == safe_strcmp(QOF_PARAM_GUID, reference_type)) 
00693         {
00694             qof_entity_set_guid(qsf_ent, cm_guid);
00695         }
00696         else {
00697             reference = qof_entity_get_reference_from(qsf_ent, cm_param);
00698             if(reference) {
00699                 params->referenceList = g_list_append(params->referenceList, reference);
00700             }
00701         }*/
00702     }
00703     if (safe_strcmp (param->param_type, QOF_TYPE_INT32) == 0)
00704     {
00705         errno = 0;
00706         cm_i32 = (gint32) strtol (insert_string, &tail, 0);
00707         if (errno == 0)
00708         {
00709             i32_setter =
00710                 (void (*)(QofEntity *, gint32)) param->param_setfcn;
00711             if (i32_setter != NULL)
00712             {
00713                 i32_setter (ent, cm_i32);
00714             }
00715         }
00716         else
00717         {
00718             QofBackend *backend;
00719             QofBook *book;
00720 
00721             book = qof_instance_get_book ((QofInstance *) ent);
00722             backend = qof_book_get_backend (book);
00723             qof_error_set_be (backend, qof_error_register 
00724             (_("When converting SQLite strings into numbers, an "
00725             "overflow has been detected. The SQLite database "
00726             "'%s' contains invalid data in a field that is meant "
00727             "to hold a number."), TRUE));
00728         }
00729     }
00730     if (safe_strcmp (param->param_type, QOF_TYPE_INT64) == 0)
00731     {
00732         errno = 0;
00733         cm_i64 = strtoll (insert_string, &tail, 0);
00734         if (errno == 0)
00735         {
00736             i64_setter =
00737                 (void (*)(QofEntity *, gint64)) param->param_setfcn;
00738             if (i64_setter != NULL)
00739             {
00740                 i64_setter (ent, cm_i64);
00741             }
00742         }
00743         else
00744         {
00745             QofBackend *backend;
00746             QofBook *book;
00747 
00748             book = qof_instance_get_book ((QofInstance *) ent);
00749             backend = qof_book_get_backend (book);
00750             qof_error_set_be (backend, qof_error_register 
00751             (_("When converting SQLite strings into numbers, an "
00752             "overflow has been detected. The SQLite database "
00753             "'%s' contains invalid data in a field that is meant "
00754             "to hold a number."), TRUE));
00755         }
00756     }
00757     if (safe_strcmp (param->param_type, QOF_TYPE_DOUBLE) == 0)
00758     {
00759         errno = 0;
00760         cm_double = strtod (insert_string, &tail);
00761         if (errno == 0)
00762         {
00763             double_setter =
00764                 (void (*)(QofEntity *, double)) param->param_setfcn;
00765             if (double_setter != NULL)
00766             {
00767                 double_setter (ent, cm_double);
00768             }
00769         }
00770     }
00771     if (safe_strcmp (param->param_type, QOF_TYPE_BOOLEAN) == 0)
00772     {
00773         gint b;
00774         b = qof_util_bool_to_int (insert_string);
00775         if (b == 1)
00776         {
00777             cm_boolean = TRUE;
00778         }
00779         else
00780         {
00781             cm_boolean = FALSE;
00782         }
00783         boolean_setter =
00784             (void (*)(QofEntity *, gboolean)) param->param_setfcn;
00785         if (boolean_setter != NULL)
00786         {
00787             boolean_setter (ent, cm_boolean);
00788         }
00789     }
00790     if (safe_strcmp (param->param_type, QOF_TYPE_KVP) == 0)
00791     {
00792 
00793     }
00794     if (safe_strcmp (param->param_type, QOF_TYPE_CHAR) == 0)
00795     {
00796         cm_char = *insert_string;
00797         char_setter = (void (*)(QofEntity *, char)) param->param_setfcn;
00798         if (char_setter != NULL)
00799         {
00800             char_setter (ent, cm_char);
00801         }
00802     }
00803     LEAVE (" ");
00804 }
00805 
00806 static void
00807 qof_query_set_insert_table (QofSqlQuery * query)
00808 {
00809     sql_insert_statement *sis;
00810     sql_table *sis_t;
00811     sis = query->parse_result->statement;
00812     switch (sis->table->type)
00813     {
00814     case SQL_simple:
00815         {
00816             sis_t = sis->table;
00817             query->single_global_tablename =
00818                 g_strdup_printf ("%s", sis_t->d.simple);
00819             qof_query_search_for (query->qof_query,
00820                 query->single_global_tablename);
00821             PINFO (" insert set to table: %s", sis_t->d.simple);
00822             break;
00823         }
00824     default:
00825         {
00826             PWARN ("SQL insert only handles simple statements");
00827         }
00828     }
00829 }
00830 
00831 static QofEntity *
00832 qof_query_insert (QofSqlQuery * query)
00833 {
00834     GList *field_list, *value_list, *cur;
00835     const gchar *param_name;
00836     gchar *value;
00837     QofIdType type;
00838     const QofParam *param;
00839     QofInstance *inst;
00840     sql_insert_statement *sis;
00841     sql_field *field;
00842     sql_field_item *item;
00843 
00844     ENTER (" ");
00845     query->param_list = NULL;
00846     type = NULL;
00847     param = NULL;
00848     value = NULL;
00849     field_list = NULL;
00850     value_list = NULL;
00851     param_name = NULL;
00852     sis = query->parse_result->statement;
00853     if (!sis->fields || !sis->values)
00854     {
00855         LEAVE (" NULL insert statement");
00856         return NULL;
00857     }
00858     type = g_strdup (query->single_global_tablename);
00859     inst = (QofInstance *) qof_object_new_instance (type, query->book);
00860     if (inst == NULL)
00861     {
00862         LEAVE (" unable to create instance of type %s", type);
00863         return NULL;
00864     }
00865     query->inserted_entity = &inst->entity;
00866     value_list = sis->values;
00867     for (field_list = sis->fields; field_list != NULL;
00868         field_list = field_list->next)
00869     {
00870         field = value_list->data;
00871         item = field->item;
00872         for (cur = item->d.name; cur != NULL; cur = cur->next)
00873         {
00874             value =
00875                 g_strdup_printf ("%s",
00876                 dequote_string ((char *) cur->data));
00877         }
00878         field = field_list->data;
00879         item = field->item;
00880         for (cur = item->d.name; cur != NULL; cur = cur->next)
00881         {
00882             param_name = g_strdup_printf ("%s", (char *) cur->data);
00883             param = qof_class_get_parameter (type, param_name);
00884         }
00885         if (param && value)
00886         {
00887             qof_sql_insertCB (param, value, query);
00888         }
00889         value_list = g_list_next (value_list);
00890     }
00891     LEAVE (" ");
00892     return query->inserted_entity;
00893 }
00894 
00895 static const char *
00896 sql_type_as_string (sql_statement_type type)
00897 {
00898     switch (type)
00899     {
00900     case SQL_select:
00901         {
00902             return "SELECT";
00903         }
00904     case SQL_insert:
00905         {
00906             return "INSERT";
00907         }
00908     case SQL_delete:
00909         {
00910             return "DELETE";
00911         }
00912     case SQL_update:
00913         {
00914             return "UPDATE";
00915         }
00916     default:
00917         {
00918             return "unknown";
00919         }
00920     }
00921 }
00922 
00923 void
00924 qof_sql_query_parse (QofSqlQuery * query, const char *str)
00925 {
00926     GList *tables;
00927     char *buf;
00928     sql_select_statement *sss;
00929     sql_where *swear;
00930 
00931     if (!query)
00932         return;
00933     ENTER (" ");
00934     /* Delete old query, if any */
00935     if (query->qof_query)
00936     {
00937         qof_query_destroy (query->qof_query);
00938         sql_destroy (query->parse_result);
00939         query->qof_query = NULL;
00940     }
00941 
00942     /* Parse the SQL string */
00943     buf = g_strdup (str);
00944     query->parse_result = sql_parse (buf);
00945     g_free (buf);
00946 
00947     if (!query->parse_result)
00948     {
00949         LEAVE ("parse error");
00950         return;
00951     }
00952 
00953     if ((SQL_select != query->parse_result->type)
00954         && (SQL_insert != query->parse_result->type))
00955     {
00956         LEAVE
00957             ("currently, only SELECT or INSERT statements are supported, "
00958             "got type=%s", sql_type_as_string (query->parse_result->type));
00959         return;
00960     }
00961 
00962     /* If the user wrote "SELECT * FROM tablename WHERE ..."
00963      * then we have a single global tablename.  But if the 
00964      * user wrote "SELECT * FROM tableA, tableB WHERE ..."
00965      * then we don't have a single unique table-name.
00966      */
00967     tables = sql_statement_get_tables (query->parse_result);
00968     if (1 == g_list_length (tables))
00969     {
00970         query->single_global_tablename = tables->data;
00971     }
00972     /* if this is an insert, we're done with the parse. */
00973     if (SQL_insert == query->parse_result->type)
00974     {
00975         query->qof_query = qof_query_create ();
00976         qof_query_set_insert_table (query);
00977         LEAVE (" insert statement parsed OK");
00978         return;
00979     }
00980     sss = query->parse_result->statement;
00981     swear = sss->where;
00982     if (swear)
00983     {
00984         /* Walk over the where terms, turn them into QOF predicates */
00985         query->qof_query = handle_where (query, swear);
00986         if (NULL == query->qof_query)
00987         {
00988             LEAVE (" no query found");
00989             return;
00990         }
00991     }
00992     else
00993     {
00994         query->qof_query = qof_query_create ();
00995     }
00996     /* Provide support for different sort orders */
00997     handle_sort_order (query, sss->order);
00998 
00999     /* We also want to set the type of thing to search for.
01000      * SELECT * FROM table1, table2, ... is not supported.
01001      * Use sequential queries and build a partial book.
01002      */
01003     qof_query_search_for (query->qof_query,
01004         query->single_global_tablename);
01005     LEAVE (" success");
01006 }
01007 
01008 /* ========================================================== */
01009 
01010 GList *
01011 qof_sql_query_run (QofSqlQuery * query, const char *str)
01012 {
01013     GList *results;
01014 
01015     if (!query)
01016         return NULL;
01017 
01018     qof_sql_query_parse (query, str);
01019     if (NULL == query->qof_query)
01020     {
01021         PINFO (" Null query");
01022         return NULL;
01023     }
01024 
01025     qof_query_set_book (query->qof_query, query->book);
01026     /* Maybe log this sucker */
01027     if (qof_log_check (log_module, QOF_LOG_DETAIL))
01028     {
01029         qof_query_print (query->qof_query);
01030     }
01031     if (SQL_insert == query->parse_result->type)
01032     {
01033         results = NULL;
01034         results = g_list_append (results, qof_query_insert (query));
01035         return results;
01036     }
01037 
01038     results = qof_query_run (query->qof_query);
01039 
01040     return results;
01041 }
01042 
01043 GList *
01044 qof_sql_query_rerun (QofSqlQuery * query)
01045 {
01046     GList *results;
01047 
01048     if (!query)
01049         return NULL;
01050 
01051     if (NULL == query->qof_query)
01052         return NULL;
01053 
01054     qof_query_set_book (query->qof_query, query->book);
01055 
01056     /* Maybe log this sucker */
01057     if (qof_log_check (log_module, QOF_LOG_DETAIL))
01058     {
01059         qof_query_print (query->qof_query);
01060     }
01061 
01062     results = qof_query_run (query->qof_query);
01063 
01064     return results;
01065 }
01066 
01068 typedef struct ent_and_string
01069 {
01070     QofEntity * ent;
01071     gchar * str;
01072     gchar * kvp_str;
01073     gchar * full_kvp_path;
01074 }eas;
01075 
01076 static gulong kvp_id = 0;
01077 static gboolean kvp_table_exists = FALSE;
01078 
01079 static void
01080 create_sql_from_param_cb (QofParam * param, gpointer user_data)
01081 {
01082     eas * data;
01083     gchar * value;
01084     GList * references;
01085 
01086     g_return_if_fail (param);
01087     data = (eas*) user_data;
01088     ENTER ("%s %s", data->ent->e_type, param->param_name);
01089     g_return_if_fail (data->ent);
01090     if (!data->str)
01091         data->str = g_strdup ("");
01092     /* avoid creating database fields for calculated values */
01093     if (!param->param_setfcn)
01094         return;
01095     /* avoid setting KVP even if a param_setfcn has been set
01096        because a QofSetterFunc for KVP is quite pointless. */
01097     if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
01098         return;
01099     references = qof_class_get_referenceList (data->ent->e_type);
01100     if (g_list_find (references, param))
01101     {
01102         /* \bug will need to use QofEntityReference here
01103         if partial books are actually to be supported. */
01104         QofEntity *e;
01105         e = param->param_getfcn (data->ent, param);
01106         value = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
01107         guid_to_string_buff (qof_entity_get_guid (e), value);
01108         PINFO (" ref=%p GUID=%s", e, value);
01109     }
01110     else
01111         value = qof_util_param_to_string (data->ent, param);
01112     if (value)
01113         g_strescape (value, NULL);
01114     if (!value)
01115         value = g_strdup ("");
01116     if (!g_str_has_suffix (data->str, "("))
01117     {
01118         gchar *tmp;
01119         tmp = g_strjoin ("", data->str, ", '", value, "'", NULL);
01120         g_free (data->str);
01121         data->str = tmp;
01122     }
01123     else
01124     {
01125         gchar * tmp;
01126         tmp = g_strjoin ("", data->str, "'", value, "'", NULL);
01127         g_free (data->str);
01128         data->str = tmp;
01129     }
01130     LEAVE ("%s", data->str);
01131 }
01132 
01133 static gchar *
01134 string_param_to_sql (QofParam * param)
01135 {
01142     /* Handle the entity GUID. Ensure that reference GUIDs
01143        must not also try to be primary keys and can be NULL. */
01144     if ((0 == safe_strcmp (param->param_type, QOF_TYPE_GUID)) &&
01145         (0 == safe_strcmp (param->param_name, QOF_PARAM_GUID)))
01146         return g_strdup_printf (" %s char(32) primary key not null",
01147             param->param_name);
01148     if (0 == safe_strcmp (param->param_type, QOF_TYPE_GUID))
01149         return g_strdup_printf (" %s char(32)", param->param_name);
01150     /* avoid creating database fields for calculated values */
01151     if (!param->param_setfcn)
01152         return NULL;
01153     if (0 == safe_strcmp (param->param_type, QOF_TYPE_STRING))
01154         return g_strdup_printf (" %s mediumtext", param->param_name);
01155     if (0 == safe_strcmp (param->param_type, QOF_TYPE_BOOLEAN))
01156         return g_strdup_printf (" %s int", param->param_name);
01157     if ((0 == safe_strcmp (param->param_type, QOF_TYPE_NUMERIC))
01158         || (0 == safe_strcmp (param->param_type, QOF_TYPE_DOUBLE))
01159         || (0 == safe_strcmp (param->param_type, QOF_TYPE_DEBCRED)))
01160     {
01161         return g_strdup_printf (" %s text", param->param_name);
01162     }
01163     if (0 == safe_strcmp (param->param_type, QOF_TYPE_INT32))
01164         return g_strdup_printf (" %s int", param->param_name);
01165     if (0 == safe_strcmp (param->param_type, QOF_TYPE_TIME))
01166         return g_strdup_printf (" %s datetime", param->param_name);
01167     if (0 == safe_strcmp (param->param_type, QOF_TYPE_CHAR))
01168         return g_strdup_printf (" %s char(1)", param->param_name);
01169     /* kvp data is stored separately - actually this is really
01170        a no-op because entities do not need a param_setfcn for kvp data. */
01171     if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
01172         return g_strdup ("");
01175     if (0 == safe_strcmp (param->param_type, QOF_TYPE_COLLECT))
01176         return g_strdup ("");
01177 //      return g_strdup_printf (" %s char(32)", param->param_name);
01178     /* catch references */
01179     return g_strdup_printf (" %s char(32)", param->param_name);
01180 }
01181 
01182 /* used by create/insert */
01183 static void
01184 string_param_foreach (QofParam * param, gpointer user_data)
01185 {
01186     gchar *p_str, * tmp;
01187     eas * data;
01188 
01189     PINFO ("string_param_foreach:%s", param->param_name);
01190     data = (eas *)user_data;
01191     if ((0 == safe_strcmp (param->param_type, QOF_TYPE_KVP)) &&
01192         (kvp_table_exists == FALSE))
01193     {
01194         g_free (data->kvp_str);
01195         data->kvp_str = 
01196             g_strdup_printf (" CREATE TABLE %s (%s, %s, %s, %s, %s, %s",
01197             kvp_table_name, "kvp_id int primary key not null",
01198             "guid char(32)", "path mediumtext", "type mediumtext",
01199             "value text", END_DB_VERSION);
01200         return;
01201     }
01202     p_str = string_param_to_sql (param);
01203     /* skip empty values (no param_setfcn) */
01204     if (!p_str)
01205         return;
01206     if (!data->str)
01207         data->str = g_strdup("");
01208     tmp = g_strjoin ("", data->str, p_str, ",", NULL);
01209     g_free (data->str);
01210     data->str = tmp;
01211     g_free (p_str);
01212 }
01213 
01219 static void
01220 create_param_list (QofParam * param, gpointer user_data)
01221 {
01222     eas * data = (eas *)user_data;
01223     g_return_if_fail (data->str);
01224     /* avoid creating database fields for calculated values */
01225     if (!param->param_setfcn)
01226         return;
01227     /* avoid setting KVP even if a param_setfcn has been set
01228        because a QofSetterFunc for KVP is quite pointless. */
01229     if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
01230     {
01231         PINFO (" kvp support tag");
01232         return;
01233     }
01234     if (!g_str_has_suffix (data->str, "("))
01235     {
01236         gchar *add;
01237         add = g_strconcat (data->str, ", ", param->param_name, NULL);
01238         g_free (data->str);
01239         data->str = add;
01240     }
01241     else
01242     {
01243         gchar * add;
01244         add = g_strjoin ("", data->str, param->param_name, NULL);
01245         g_free (data->str);
01246         data->str = add;
01247     }
01248 }
01249 
01251 static void
01252 kvpvalue_to_sql_insert (const gchar * key, KvpValue * val, gpointer user_data)
01253 {
01254     eas * data;
01255     KvpValueType n;
01256     gchar * path;
01257 
01258     path = g_strdup("");
01259     ENTER (" ");
01260     data = (eas*)user_data;
01261     g_return_if_fail (key && val && data);
01262     n = kvp_value_get_type (val);
01263     switch (n)
01264     {
01265     case KVP_TYPE_GINT64:
01266     case KVP_TYPE_DOUBLE:
01267     case KVP_TYPE_NUMERIC:
01268     case KVP_TYPE_STRING:
01269     case KVP_TYPE_GUID:
01270     case KVP_TYPE_TIME:
01271     case KVP_TYPE_BOOLEAN:
01272         {
01273             path = g_strjoin ("/", data->full_kvp_path, key, NULL);
01274             data->str =
01275                 g_strdup_printf ("'%s', '%s', '%s'", key, path,
01276                 kvp_value_to_bare_string (val));
01277             DEBUG (" %s", data->str);
01278             break;
01279         }
01280     case KVP_TYPE_FRAME:
01281         {
01282             path = g_strjoin ("/", data->full_kvp_path, key, NULL);
01283             g_free (data->full_kvp_path);
01284             data->full_kvp_path = path;
01285             kvp_frame_for_each_slot (kvp_value_get_frame (val),
01286                 kvpvalue_to_sql_insert, data);
01287             break;
01288         }
01289     default:
01290         {
01291             PERR (" unsupported value = %d", kvp_value_get_type (val));
01292             break;
01293         }
01294     }
01295     LEAVE (" %s", data->str);
01296 }
01297 
01299 static void
01300 kvpvalue_to_sql_update (const gchar * key, KvpValue * val, gpointer user_data)
01301 {
01302     eas * data;
01303     KvpValueType n;
01304     gchar * path;
01305 
01306     ENTER (" key=%s", key);
01307     data = (eas*)user_data;
01308     g_return_if_fail (key && val && data);
01309     n = kvp_value_get_type (val);
01310     switch (n)
01311     {
01312     case KVP_TYPE_GINT64:
01313     case KVP_TYPE_DOUBLE:
01314     case KVP_TYPE_NUMERIC:
01315     case KVP_TYPE_STRING:
01316     case KVP_TYPE_GUID:
01317     case KVP_TYPE_TIME:
01318     case KVP_TYPE_BOOLEAN:
01319         {
01320             path = g_strjoin ("/", data->full_kvp_path, key, NULL);
01321             data->str =
01322                 g_strdup_printf ("type='%s', value='%s' WHERE path='%s' and ", 
01323                     key, kvp_value_to_bare_string (val), path);
01324             DEBUG (" %s", data->str);
01325             break;
01326         }
01327     case KVP_TYPE_FRAME:
01328         {
01329             path = g_strjoin ("/", data->full_kvp_path, key, NULL);
01330             g_free (data->full_kvp_path);
01331             data->full_kvp_path = path;
01332             kvp_frame_for_each_slot (kvp_value_get_frame (val),
01333                 kvpvalue_to_sql_update, data);
01334             break;
01335         }
01336     default:
01337         {
01338             PERR (" unsupported value = %d", kvp_value_get_type (val));
01339             break;
01340         }
01341     }
01342     LEAVE (" %s", data->str);
01343 }
01344 
01345 gchar *
01346 qof_sql_object_create_table (QofObject * obj)
01347 {
01348     gchar * sql_str, * start;
01349     eas data;
01350 
01351     if (!kvp_table_name)
01352         kvp_table_name = g_strdup(QSQL_KVP_TABLE);
01353     ENTER ("create table for %s", obj->e_type);
01354     start = g_strdup_printf ("CREATE TABLE %s (", obj->e_type);
01355     data.ent = NULL;
01356     data.str = g_strdup("");
01357     data.kvp_str = g_strdup("");
01358     qof_class_param_foreach (obj->e_type, string_param_foreach, &data);
01359     sql_str = g_strjoin ("", start, data.str, END_DB_VERSION, data.kvp_str, NULL);
01360     g_free (start);
01361     g_free (data.kvp_str);
01362     g_free (data.str);
01363     LEAVE ("sql_str=%s", sql_str);
01364     return sql_str;
01365 }
01366 
01367 gchar *
01368 qof_sql_entity_create_table (QofEntity * ent)
01369 {
01370     gchar * sql_str, * start;
01371     eas data;
01372 
01373     g_return_val_if_fail (ent, NULL);
01374     if (!kvp_table_name)
01375         kvp_table_name = g_strdup(QSQL_KVP_TABLE);
01376     ENTER ("create table for %s", ent->e_type);
01377     start = g_strdup_printf ("CREATE TABLE %s (", ent->e_type);
01378     data.ent = ent;
01379     data.str = g_strdup("");
01380     data.kvp_str = g_strdup("");
01381     qof_class_param_foreach (ent->e_type, string_param_foreach, &data);
01382     sql_str = g_strjoin ("", start, data.str, END_DB_VERSION, data.kvp_str, NULL);
01383     g_free (start);
01384     LEAVE ("sql_str=%s", sql_str);
01385     return sql_str;
01386 }
01387 
01388 gchar *
01389 qof_sql_entity_insert (QofEntity * ent)
01390 {
01391     KvpFrame * slots;
01392     eas data;
01393     gchar * command, * fields, * values, *id, *gstr, *sql_str, *kvp;
01394 
01395     data.ent = ent;
01396     data.str = g_strdup("");
01397     if (!kvp_table_name)
01398         kvp_table_name = g_strdup(QSQL_KVP_TABLE);
01399     ENTER (" insert a single '%s'", ent->e_type);
01400     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
01401     guid_to_string_buff (qof_instance_get_guid ((QofInstance *) ent), gstr);
01402     DEBUG (" guid=%s", gstr);
01403     command = g_strdup_printf ("INSERT into %s (guid ", ent->e_type);
01404     // store param list in fields
01405     qof_class_param_foreach (ent->e_type, create_param_list, &data);
01406     fields = g_strdup(data.str);
01407     // store param values in values
01408     g_free (data.str);
01409     data.str = g_strdup("");
01410     data.full_kvp_path = g_strdup("");
01411     qof_class_param_foreach (ent->e_type, create_sql_from_param_cb, &data);
01412     values = data.str;
01413     /* handle KVP */
01414     kvp = g_strdup("");
01415     slots = qof_instance_get_slots ((QofInstance *) ent);
01416     if (!kvp_frame_is_empty(slots))
01417     {
01418         id = g_strdup_printf ("%lu", kvp_id);
01419         g_free (kvp);
01420         kvp_frame_for_each_slot (slots, kvpvalue_to_sql_insert, &data);
01421         kvp = g_strconcat (" INSERT into ", kvp_table_name,
01422             "  (kvp_id, guid, type, path, value) VALUES ('", id, "', '", 
01423             gstr, "', ", data.str, ");", NULL);
01424         /* increment the index value of the KVP table */
01425         kvp_id++;
01426         g_free (data.str);
01427     }
01428     sql_str = g_strjoin ("", command, fields, ") VALUES ('", gstr, "' ", 
01429         values, ");", kvp, NULL);
01430     g_free (command);
01431     g_free (fields);
01432     g_free (gstr);
01433     g_free (values);
01434     g_free (data.full_kvp_path);
01435     LEAVE ("sql_str=%s", sql_str);
01436     return sql_str;
01437 }
01438 
01439 static void
01440 collect_kvp (QofEntity * ent, gpointer user_data)
01441 {
01442     KvpFrame * slots;
01443     KvpValue * collguid;
01444     gchar * gstr, * name, * key;
01445     const QofParam * G_GNUC_UNUSED param;
01446 
01447     name = (gchar *) user_data;
01448     param = qof_class_get_parameter (ent->e_type, name);
01449     key = g_strconcat ("collection/", name, "/guid/", NULL);
01450     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
01451     guid_to_string_buff (qof_instance_get_guid ((QofInstance*)ent), gstr);
01452     collguid = kvp_value_new_string(gstr);
01453     slots = qof_instance_get_slots((QofInstance*)ent);
01454     kvp_frame_set_value (slots, key, collguid);
01455     g_free (key);
01456     g_free (gstr);
01457 }
01458 
01459 gchar *
01460 qof_sql_entity_update (QofEntity * ent)
01461 {
01462     gchar *gstr, * sql_str, * param_str;
01463     QofInstance * inst;
01464     const QofParam * param;
01465     inst = (QofInstance*)ent;
01466 
01467     if (!inst->param)
01468         return NULL;
01469     ENTER (" modified %s param:%s", ent->e_type, inst->param->param_name);
01470     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
01471     guid_to_string_buff (qof_instance_get_guid (inst), gstr);
01472     param = inst->param;
01473     if (0 == safe_strcmp (param->param_type, QOF_TYPE_COLLECT))
01474     {
01475         gchar * name;
01476         QofCollection * coll;
01477 
01478         coll = param->param_getfcn (ent, param);
01479         name = g_strdup (param->param_name);
01480         qof_collection_foreach (coll, collect_kvp, name);
01481         g_free (name);
01482         return NULL;
01483     }
01484     param_str = qof_util_param_to_string (ent, inst->param);
01485     if (param_str)
01486         g_strescape (param_str, NULL);
01487     sql_str = g_strconcat ("UPDATE ", ent->e_type, " SET ",
01488         inst->param->param_name, " = '", param_str,
01489         "' WHERE ", QOF_TYPE_GUID, "='", gstr, "';", NULL);
01490     LEAVE ("sql_str=%s", sql_str);
01491     return sql_str;
01492 }
01493 
01494 gchar *
01495 qof_sql_entity_update_kvp (QofEntity * ent)
01496 {
01497     eas data;
01498     gchar *gstr, * sql_str;
01499     QofInstance * inst;
01500     gchar * start;
01501     KvpFrame * slots;
01502     inst = (QofInstance*)ent;
01503 
01504     if (!inst->param)
01505         return NULL;
01506     sql_str = NULL;
01507     if (kvp_frame_is_empty (qof_instance_get_slots ((QofInstance*)ent)))
01508         return NULL;
01509 
01510     ENTER (" modified %s param:%s", ent->e_type, inst->param->param_name);
01511     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
01512     guid_to_string_buff (qof_instance_get_guid (inst), gstr);
01513     data.str = g_strdup("");
01514     data.full_kvp_path = g_strdup("");
01515     slots = qof_instance_get_slots ((QofInstance*)ent);
01516     start = g_strjoin ("", "UPDATE ", kvp_table_name, " SET ", NULL);
01519     kvp_frame_for_each_slot (slots, kvpvalue_to_sql_update, &data);
01520     sql_str = g_strjoin ("", start, data.str, " guid='", gstr, "';", NULL);
01521     g_free (start);
01522     g_free (data.full_kvp_path);
01523     g_free (data.str);
01524     LEAVE ("sql_str=%s", sql_str);
01525     return sql_str;
01526 }
01527 
01528 gchar *
01529 qof_sql_entity_update_list (QofEntity * ent, GList **params)
01530 {
01531     return NULL;
01532 }
01533 
01534 gchar *
01535 qof_sql_entity_delete (QofEntity * ent)
01536 {
01537     gchar * gstr, * sql_str;
01538     ENTER (" %s", ent->e_type);
01539     gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' ');
01540     guid_to_string_buff (qof_entity_get_guid (ent), gstr);
01541     sql_str = g_strconcat ("DELETE from ", ent->e_type, " WHERE ",
01542         QOF_TYPE_GUID, "='", gstr, "';", "DELETE from ", kvp_table_name, 
01543         " WHERE kvp_id ", "='", gstr, "';", NULL);
01544     g_free (gstr);
01545     return sql_str;
01546 }
01547 
01548 gchar *
01549 qof_sql_entity_drop_table (QofEntity * ent)
01550 {
01551     gchar * sql_str;
01552     ENTER (" drop table for '%s'", ent->e_type);
01553     sql_str = g_strdup_printf ("DROP TABLE %s;", ent->e_type);
01554     LEAVE ("sql_str=%s", sql_str);
01555     return sql_str;
01556 }
01557 
01558 void qof_sql_entity_set_kvp_tablename (const gchar * name)
01559 {
01560     g_return_if_fail (name);
01561     kvp_table_name = g_strdup(name);
01562 }
01563 
01564 void qof_sql_entity_set_kvp_id (gulong id)
01565 {
01566     kvp_id = id;
01567 }
01568 
01569 gulong qof_sql_entity_get_kvp_id (void)
01570 {
01571     return kvp_id;
01572 }
01573 
01574 void qof_sql_entity_set_kvp_exists (gboolean exist)
01575 {
01576     kvp_table_exists = exist;
01577 }
01578 
01579 /* ========================== END OF FILE =================== */