** Rewrite camel search by converting sexp to sql.
authorSrinivasa Ragavan <sragavan@novell.com>
Thu, 24 Jul 2008 05:54:42 +0000 (05:54 +0000)
committerSrinivasa Ragavan <sragavan@src.gnome.org>
Thu, 24 Jul 2008 05:54:42 +0000 (05:54 +0000)
2008-07-24  Srinivasa Ragavan  <sragavan@novell.com>

** Rewrite camel search by converting sexp to sql.

* camel/camel-folder-search.c:
* camel/camel-folder-summary.c:
* camel/camel-search-sql.c:

svn path=/trunk/; revision=9175

camel/ChangeLog
camel/camel-folder-search.c
camel/camel-folder-summary.c
camel/camel-search-sql.c [new file with mode: 0644]
camel/camel-search-sql.h [new file with mode: 0644]

index 1ddd2f5..2a27406 100644 (file)
@@ -1,3 +1,11 @@
+2008-07-24  Srinivasa Ragavan  <sragavan@novell.com>
+
+       ** Rewrite camel search by converting sexp to sql.
+
+       * camel/camel-folder-search.c:
+       * camel/camel-folder-summary.c:
+       * camel/camel-search-sql.c:
+
 2008-07-23  Srinivasa Ragavan  <sragavan@novell.com>
 
        * Makefile.am: Add core camel search in sql support.
index 1564007..6718167 100644 (file)
@@ -52,6 +52,7 @@
 #include "camel-store.h"
 #include "camel-vee-folder.h"
 #include "camel-string-utils.h"
+#include "camel-search-sql.h"
 
 #define d(x) 
 #define r(x) 
@@ -459,6 +460,7 @@ camel_folder_search_search(CamelFolderSearch *search, const char *expr, GPtrArra
        GPtrArray *matches = NULL, *summary_set;
        int i;
        CamelDB *cdb;
+       char *sql_query, *tmp, *tmp1;
        struct _CamelFolderSearchPrivate *p = _PRIVATE(search);
 
        g_assert(search->folder);
@@ -494,8 +496,8 @@ camel_folder_search_search(CamelFolderSearch *search, const char *expr, GPtrArra
                g_free(search->last_search);
                search->last_search = g_strdup(expr);
        }
-       r = e_sexp_eval(search->sexp);
-       if (r == NULL) {
+       //r = e_sexp_eval(search->sexp);
+       if (0 && r == NULL) {
                if (!camel_exception_is_set(ex))
                        camel_exception_setv(ex, 1, _("Error executing search expression: %s:\n%s"), e_sexp_error(search->sexp), expr);
                goto fail;
@@ -503,11 +505,18 @@ camel_folder_search_search(CamelFolderSearch *search, const char *expr, GPtrArra
 
        printf ("\nsexp is : [%s]\n", expr);
        printf ("Something is returned in the top-level caller : [%s]\n", search->query->str);
-
+       sql_query = camel_sexp_to_sql (expr);
+       tmp1 = camel_db_sqlize_string(search->folder->full_name);
+       tmp = g_strdup_printf ("SELECT uid FROM %s WHERE %s", tmp1,  sql_query);
+       camel_db_free_sqlized_string (tmp1);
+       g_free (sql_query);
+       printf("tmp %s\n", tmp);
+       
        matches = g_ptr_array_new();
        cdb = (CamelDB *) (search->folder->cdb);
-       camel_db_select (cdb, search->query->str, (CamelDBSelectCB) read_uid_callback, matches, ex);
-       e_sexp_result_free(search->sexp, r);
+       camel_db_select (cdb, tmp, (CamelDBSelectCB) read_uid_callback, matches, ex);
+       g_free(tmp);
+       //e_sexp_result_free(search->sexp, r);
 
 fail:
        /* these might be allocated by match-threads */
index 5321ec1..5e89785 100644 (file)
@@ -2917,12 +2917,12 @@ message_info_to_db (CamelFolderSummary *s, CamelMessageInfo *info)
        record->uid = (char *) camel_pstring_strdup(camel_message_info_uid(mi));
        record->flags = mi->flags;
        
-       record->read =  ((mi->flags & (CAMEL_MESSAGE_SEEN|CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_JUNK)));
-       record->deleted = mi->flags & CAMEL_MESSAGE_DELETED;
-       record->replied = mi->flags & CAMEL_MESSAGE_ANSWERED;   
-       record->important = mi->flags & CAMEL_MESSAGE_FLAGGED;          
-       record->junk = mi->flags & CAMEL_MESSAGE_JUNK;
-       record->attachment = mi->flags & CAMEL_MESSAGE_ATTACHMENTS;
+       record->read =  ((mi->flags & (CAMEL_MESSAGE_SEEN|CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_JUNK))) ? 1 : 0;
+       record->deleted = mi->flags & CAMEL_MESSAGE_DELETED ? 1 : 0;
+       record->replied = mi->flags & CAMEL_MESSAGE_ANSWERED ? 1 : 0;   
+       record->important = mi->flags & CAMEL_MESSAGE_FLAGGED ? 1 : 0;          
+       record->junk = mi->flags & CAMEL_MESSAGE_JUNK ? 1 : 0;
+       record->attachment = mi->flags & CAMEL_MESSAGE_ATTACHMENTS ? 1 : 0;
        
        record->size = mi->size;
        record->dsent = mi->date_sent;
diff --git a/camel/camel-search-sql.c b/camel/camel-search-sql.c
new file mode 100644 (file)
index 0000000..ca55134
--- /dev/null
@@ -0,0 +1,870 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright (C) 2008 Novell, Inc. (www.novell.com)
+ *
+ *  Author: Srinivasa Ragavan  <sragavan@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* This is a helper class for folders to implement the search function.
+   It implements enough to do basic searches on folders that can provide
+   an in-memory summary and a body index. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include "camel-search-sql.h"
+
+#define d(x) 
+
+#ifdef TEST_MAIN
+#include <sqlite3.h>
+
+char * camel_db_get_column_name (const char *raw_name);
+
+char * 
+camel_db_sqlize_string (const char *string)
+{
+       return sqlite3_mprintf ("%Q", string);
+}
+
+void 
+camel_db_free_sqlized_string (char *string)
+{
+       sqlite3_free (string);
+       string = NULL;
+}
+#else
+#include "camel-db.h"
+#endif
+char * escape_values (char *str);
+
+static GScannerConfig config =
+        {
+                (
+                        " \t\n\r"
+                        )                       /* cset_skip_characters */,
+                (
+                        G_CSET_a_2_z
+                        G_CSET_A_2_Z
+                        )                       /* cset_identifier_first */,
+                (
+                        G_CSET_a_2_z
+                        "_-0123456789"
+                        G_CSET_A_2_Z
+                        )                       /* cset_identifier_nth */,
+                ( "#\n" )               /* cpair_comment_single */,
+                FALSE                    /* case_sensitive */,
+                TRUE                    /* skip_comment_multi */,
+                TRUE                    /* skip_comment_single */,
+                TRUE                    /* scan_comment_multi */,
+                TRUE                    /* scan_identifier */,
+                TRUE                    /* scan_identifier_1char */,
+                FALSE                   /* scan_identifier_NULL */,
+                TRUE                   /* scan_symbols */,
+                FALSE                   /* scan_binary */,
+                FALSE                   /* scan_octal */,
+                FALSE                    /* scan_float */,
+                FALSE                   /* scan_hex */,
+                FALSE                   /* scan_hex_dollar */,
+                TRUE                    /* scan_string_sq */,
+                TRUE                    /* scan_string_dq */,
+                FALSE                   /* numbers_2_int */,
+                FALSE                   /* int_2_float */,
+                FALSE                   /* identifier_2_string */,
+                TRUE                    /* char_2_token */,
+                FALSE                   /* symbol_2_token */,
+                FALSE                   /* scope_0_fallback */,
+        };
+
+typedef struct Node {
+       char *token; /* Token to search*/
+       char *exact_token; /* Token to substitute */
+       int nodes; /* Number of nodes to process */
+       char pre_token; /* Pre token to prepend with value substitute*/
+       char post_token; /* post token to apppend with substitute */
+       char rval; /* rhs value for binary ops */
+       int level; /* depth in the hier */
+       int prefix:1 ; /* unary operator to be searched ?*/
+       int sys_node:1 ; /* is it a predefined term ? */
+       int ignore_lhs:1; /* ignore lhs value ?*/
+       int swap :1;
+       int prenode :1;
+       int operator:1;
+       int execute:1;
+       int ref;
+}Node;
+
+/* 
+ * Design of the sexp parser
+ *
+ * Every node is a operator/operand (sysnode operand[like known headers] or normal operand)
+ * Every sysnode has a min nodes to operate and we operate at it. Every time a sysnode is encountered
+ * the further nodes's min nodes are reduced from the prev nodes and will go till it becomes 1. Then the nodes
+ * till the sysnode is popped from the stack executed and added to the stack.
+ *
+ * Once the entire parse is completed, we parse the stack, take operands, till you find a operator. If you find a operator
+ * then operate that on all the popped operands and push it back to the stack. Repeat till you have just one node on the stack.
+ * Sexp has at times single operand, in which case the operator is ignored.
+ *
+ * We use 3 stacks, all, operators and operand. 'All' is the stack that is used in the second iteration and operand/operators stack
+ * is used for the first iteration.
+ *
+ * Rest of the code sucks and might not work for all statements. Just could be Evolution specific.
+ *
+ * */
+
+/* Configuration of your sexp expression */
+static Node elements[] =  { {"header-contains", "LIKE", 3, '%', '%', 0, 0, 0 , 1, 0, 0, 0, 0, 0}, 
+                           {"system-flag", "=", 2, ' ', ' ', '1', 0, 1, 1, 0, 0, 0, 0, 0}, 
+                           {"match-all", "", 0, ' ', ' ', 0, 0, 0, 1, 0, 0, 0, 0, 0},
+                           {"cast-int", "", 0, ' ', ' ', 0, 0, 0, 1, 0, 0, 0, 0, 0},                       
+                           { "header-matches", "LIKE", 3, ' ', ' ', 0, 0, 1, 1, 0, 0, 0, 0, 0}, 
+                           { "header-ends-with", "LIKE", 3, '%', ' ', 0, 0, 0, 1, 0, 0, 0, 0, 0}, 
+                           { "header-exists", "NOTNULL", 2, ' ', ' ', ' ', 0, 0, 1, 0, 0, 0, 0, 0},
+                           { "user-tag", "usertags", 3, '%', '%', 0, 0, 1, 1, 1, 0, 0, 0, 0},
+                           { "user-flag", "labels LIKE", 2, '%', '%', 0, 0, 0, 1, 0, 1, 0, 0, 0},
+                           { "header-starts-with", "LIKE", 3, ' ', '%', 0, 0, 0, 1, 0, 0, 0, 0, 0},
+                           { "get-sent-date", "dsent", 2, ' ', ' ', 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 },
+                           { "get-received-date", "dreceived", 2, ' ', ' ', 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 },
+                           { "get-size", "size", 2, ' ', ' ', ' ', 0, 1, 1, 0, 0, 0, 0, 1}
+};
+#if 0           
+       { "get-sent-date", CAMEL_STRUCT_OFFSET(CamelFolderSearchClass, get_sent_date), 1 },
+       { "get-received-date", CAMEL_STRUCT_OFFSET(CamelFolderSearchClass, get_received_date), 1 },
+       { "get-current-date", CAMEL_STRUCT_OFFSET(CamelFolderSearchClass, get_current_date), 1 },
+       { "get-size", CAMEL_STRUCT_OFFSET(CamelFolderSearchClass, get_size), 1 },
+       { "uid", CAMEL_STRUCT_OFFSET(CamelFolderSearchClass, uid), 1 },
+
+#endif
+
+static void
+free_node (Node *node)
+{
+       node->ref--;
+
+       if (node->ref)
+               return;
+       
+       d(printf("freeing %s %s %p\n", node->token, node->exact_token, node));
+       g_free (node->token);
+       g_free (node->exact_token);
+       g_free (node);
+}
+
+static void
+g_list_dump (GList *l)
+{
+       while (l) {
+                printf("%s\t", (char *)l->data);
+               l = l->next;
+       }
+}
+
+static void
+g_node_dump (GList *l)
+{
+       Node *node;
+
+       while (l) {
+               node = l->data;
+               printf("%p (%d '%s')\t", node, node->ref, node->exact_token);
+               l = l->next;
+       }
+       printf("\n");
+
+}
+
+char *
+escape_values (char *str)
+{
+       char *sql = camel_db_sqlize_string (g_strstrip(str));
+        char *ret = g_strdup (sql);
+
+        camel_db_free_sqlized_string (sql);
+        /* I dont want to manage sql strings here */
+        
+        return ret;
+}
+
+/**
+ * camel_search_sexp_to_sql:
+ * @txt: A valid sexp expression
+ * 
+ * Converts a valid sexp expression to a sql statement with table fields converted into it.
+ * This is very specific to Evolution. It might crash is the sexp is invalid. The callers must ensure that the sexp is valid
+ **/
+
+char *
+camel_sexp_to_sql (const char *txt) 
+{
+       GScanner *scanner = g_scanner_new (&config);
+       char *sql;
+       int level = 0;
+       GList *tlist;
+       GList *operators=NULL, *operands=NULL, *all=NULL;
+       GList *tmp;
+       Node *n1=NULL, *n2=NULL, *n3=NULL, *op=NULL;
+       GList *res=NULL;
+       gboolean last_sysnode = FALSE;
+       
+       d(printf("len = %d\n", strlen (txt)));
+       
+       g_scanner_input_text (scanner, txt, strlen(txt));
+       while (!g_scanner_eof (scanner)) {
+               Node *mnode;
+               guint token = g_scanner_get_next_token (scanner);
+               
+               /* Extract and identify tokens */
+               if (token == G_TOKEN_IDENTIFIER || token == G_TOKEN_STRING) {
+                       char *token = scanner->value.v_string;
+
+                       d(printf("token %s\n", token));
+                       if (g_ascii_strcasecmp (token, "and") == 0 ||
+                               g_ascii_strcasecmp (token, "or") == 0 ||
+                                       g_ascii_strcasecmp (token, "not") == 0) {
+                               /* operator */
+                               Node *node = g_new0 (Node, 1);
+                               
+                               node->token = g_strdup (token);
+                               node->exact_token =  g_strdup (token);
+                               node->level = level;
+                               node->operator = 1;
+                               node->ref = 2;
+                               operators = g_list_prepend (operators, node);
+                               all = g_list_prepend (all, node);
+                       } else {
+                               /* Should be operand*/
+                               int i;
+                               Node *node;
+                               
+                               for (i=0; i < G_N_ELEMENTS(elements); i++) {
+                                       if (g_ascii_strcasecmp (elements[i].token, token) == 0) {
+                                                
+                                               if (!*elements[i].exact_token) /* Skip match-all */
+                                                       break;
+                                               
+                                               node = g_new0 (Node, 1);
+                                               node->token = g_strdup (elements[i].token);
+                                               node->exact_token = g_strdup (elements[i].exact_token);
+                                               node->nodes = elements[i].nodes;
+                                               node->prefix = elements[i].prefix;
+                                               node->pre_token = elements[i].pre_token;
+                                               node->post_token = elements[i].post_token;
+                                               node->rval = elements[i].rval;
+                                               node->sys_node = 1;
+                                               node->level = level;
+                                               node->ignore_lhs = elements[i].ignore_lhs;
+                                               node->swap = elements[i].swap;
+                                               node->prenode = elements[i].prenode;
+                                               node->operator = 0;
+                                               node->execute = elements[i].execute;
+                                               node->ref = 2;
+                                               operands = g_list_prepend (operands, node);
+                                               all = g_list_prepend (all, node);
+                                               last_sysnode = TRUE;
+                                               
+                                               break;
+                                       }
+                               }
+
+                               /* These should be normal tokens */
+                               if (i >= G_N_ELEMENTS(elements)) {
+                                       Node *pnode = operands->data;
+                                       
+                                       node = g_new0 (Node, 1);
+                                       node->token = g_strdup (token);
+                                       if (last_sysnode) {
+                                                last_sysnode = FALSE;
+                                                node->exact_token = camel_db_get_column_name (token);
+                                       } else
+                                                node->exact_token = g_strdup (token);
+                                       
+                                       node->nodes = pnode->nodes > 0 ? pnode->nodes - 1:0;
+                                       node->prefix = 0;
+                                       node->rval = ' ';
+                                       node->level = level;
+                                       node->sys_node = 0;
+                                       node->operator = 0;
+                                       node->execute = 0;
+                                       node->ref = 2;
+                                       operands = g_list_prepend (operands, node);
+                                       all = g_list_prepend (all, node);
+                               }
+                       }
+
+               } else if (token == G_TOKEN_LEFT_PAREN)  {
+                       d(printf("(\n"));
+                       level++;
+               } else if (token == G_TOKEN_RIGHT_PAREN) {
+                       d(printf(")\n"));
+                       level--;
+               } else if (token == G_TOKEN_EQUAL_SIGN) {
+                       Node *node = g_new0 (Node, 1); 
+
+                       node->token = g_strdup ("=");
+                       node->exact_token =  g_strdup ("=");
+                       node->level = level;
+                       node->ref = 2;
+                       operators = g_list_prepend (operators, node);           
+                       all = g_list_prepend (all, node);
+               } else if (token == '+') {
+                       char *astr=NULL, *bstr=NULL;
+                       Node *node, *pnode = operands->data;
+                       int lvl=0, lval=0;
+
+                       if (g_ascii_strcasecmp (pnode->token, "user-flag") == 0) {
+                                   /* Colloct all after '+' and append them to one token. Go till you find ')' */
+                                   token = g_scanner_get_next_token (scanner) ;
+                                   while (token != G_TOKEN_RIGHT_PAREN) {
+                                           astr = g_strdup_printf ("%s%s", bstr?bstr:"", scanner->value.v_string);
+                                           g_free (bstr); bstr = astr;
+                                           token = g_scanner_get_next_token (scanner);
+                                   }
+                       } else {
+                               /* should be the date fns*/
+                       /* Colloct all after '+' and append them to one token. Go till you find ')' */
+                               token = g_scanner_get_next_token (scanner) ;
+                               while (token >= 0 && !g_scanner_eof(scanner) && lvl >=0 ) {
+                                       if (token == G_TOKEN_RIGHT_PAREN) {
+                                               d(printf(")\n"));
+                                               lvl--;
+                                               token = g_scanner_get_next_token (scanner);
+                                               continue;
+                                       } else if (token == G_TOKEN_LEFT_PAREN) {
+                                               d(printf("(\n"));
+                                               lvl++;
+                                               token = g_scanner_get_next_token (scanner);
+                                               continue;
+                                       } else if (token == G_TOKEN_INT) {
+                                               d(printf("int %d\n", scanner->value.v_int));
+                                               lval = lval + scanner->value.v_int;
+                                       } else {
+                                               d(printf("str %s\n", scanner->value.v_string));
+                                               if (g_ascii_strcasecmp (scanner->value.v_string, "get-current-date") == 0) {
+                                                       lval = time(NULL); //Make this 12:00 am
+                                               } else
+                                                       lval = atol (scanner->value.v_string);
+                                               d(printf("str %d\n", lval));
+                                       }
+                                       token = g_scanner_get_next_token (scanner);
+                               }
+                               // (> (get-sent-date) (- (get-current-date) 100))) )
+                               d(printf("lvl = %d %ld\n", lvl, lval));
+                               bstr = g_strdup_printf ("%d", lval);
+                       }
+                       
+                       node = g_new0 (Node, 1);
+                       node->token = bstr;
+                       node->exact_token = g_strdup(bstr);
+                       node->nodes = pnode->nodes > 0 ? pnode->nodes - 1:0;
+                       node->prefix = 0;
+                       node->rval = ' ';
+                       node->level = level;
+                       node->sys_node = 0;
+                       node->ref = 2;
+                       operands = g_list_prepend (operands, node);     
+                       all = g_list_prepend (all, node);
+
+                       level--;
+               } else if (token == '-') {
+                       char *bstr=NULL;
+                       Node *node, *pnode = operands->data;
+                       int lvl=0, lval=0;
+                       
+                       /* Colloct all after '+' and append them to one token. Go till you find ')' */
+                       token = g_scanner_get_next_token (scanner) ;
+                       while (token >= 0 && !g_scanner_eof(scanner) && lvl >=0 ) {
+                               if (token == G_TOKEN_RIGHT_PAREN) {
+                                       lvl--;
+                                       token = g_scanner_get_next_token (scanner);
+                                       continue;
+                               } else if (token == G_TOKEN_LEFT_PAREN) {
+                                       lvl++;
+                                       token = g_scanner_get_next_token (scanner);
+                                       continue;
+                               } else if (token == G_TOKEN_INT) {
+                                       d(printf("int %d\n", scanner->value.v_int));
+                                       lval = lval - scanner->value.v_int;
+                               } else {
+                                       d(printf("str %s\n", scanner->value.v_string));
+                                       if (g_ascii_strcasecmp (scanner->value.v_string, "get-current-date") == 0) {
+                                               lval = time(NULL); //Make this 12:00 am
+                                       } else
+                                               lval = atol (scanner->value.v_string);
+                                       d(printf("str %d\n", lval));
+                               }
+                               token = g_scanner_get_next_token (scanner);
+                       }
+                       // (> (get-sent-date) (- (get-current-date) 100))) )
+                       d(printf("lvl = %ld\n", lval));
+                       node = g_new0 (Node, 1);
+                       node->token = bstr;
+                       node->exact_token = g_strdup_printf("%ld", (long)lval);
+                       node->nodes = pnode->nodes > 0 ? pnode->nodes - 1:0;
+                       node->prefix = 0;
+                       node->rval = ' ';
+                       node->level = level;
+                       node->sys_node = 0;
+                       node->ref = 2;
+                       operands = g_list_prepend (operands, node);     
+                       all = g_list_prepend (all, node);
+                       level--;
+                               //g_node_dump (all);printf("\n\n");
+                               //g_node_dump (operands);printf("\n\n");
+                               //g_node_dump (operators);printf("\n\n");
+                       
+               } else if (token == '>' || token == '<') {
+                       
+                               /* operator */
+                               Node *node = g_new0 (Node, 1);
+                               
+                               node->token = g_strdup_printf ("%c", token);
+                               node->exact_token =  g_strdup_printf ("%c", token);
+                               node->level = level;
+                               node->operator = 1;
+                               node->ref = 2;
+                               operators = g_list_prepend (operators, node);
+                               all = g_list_prepend (all, node);                       
+               } else if (token == G_TOKEN_INT) {
+                       Node *pnode = operands->data, *node;
+
+                       node = g_new0 (Node, 1);
+                       node->token = g_strdup_printf ("%ld", scanner->value.v_int) ;
+                       node->exact_token = g_strdup_printf ("%ld", scanner->value.v_int);
+                       node->nodes = pnode->nodes > 0 ? pnode->nodes - 1:0;
+                       node->prefix = 0;
+                       node->rval = ' ';
+                       node->level = level;
+                       node->sys_node = 0;
+                       node->operator = 0;
+                       node->execute = 0;
+                       node->ref = 2;
+                       operands = g_list_prepend (operands, node);
+                       all = g_list_prepend (all, node);
+                       
+               }
+                               //g_node_dump (all);printf("\n\n");
+                               //g_node_dump (operands);printf("\n\n");
+                               //g_node_dump (operators);printf("\n\n");
+
+               if (operands) {
+                       mnode = operands->data;
+                       d(printf("recalculating ? %d\n", mnode->nodes));
+                       
+                       /* If we reach the operating level, which is the exec min for last seen sys-header */
+                       if (mnode->nodes == 1) {
+                               /* lets evaluate */
+                               int len = 2;
+                               Node *pnode;
+
+                                       
+                               n1=NULL; n2=NULL; n3=NULL;
+                               tmp = operands;
+                               n1 = operands->data;
+                               operands = g_list_delete_link(operands, operands);
+                               all = g_list_delete_link (all, all);
+                               tmp = operands;
+                               n2 = operands->data;
+                               operands = g_list_delete_link(operands, operands);              
+                               all = g_list_delete_link (all, all);
+                               
+                               /* If it is a sysnode, then it is double operand */
+                               if (!n2->sys_node) {
+                                       /* This has to be a sysnode if not panic */
+                                       n3 = operands->data;
+                                       operands = g_list_delete_link(operands, operands);
+                                       /* this is a triple operand */                                  
+                                       len = 3;
+                                       all = g_list_delete_link (all, all);
+
+                               }
+
+                               if (operands)
+                                       pnode  = operands->data;
+                               else    
+                                       pnode = NULL;
+                               
+                               if (len == 3) {
+                                       char *prefix = NULL;
+                                       char *str, *sqstr, *escstr;
+                                       int dyn_lvl;
+                                       Node *opnode = operators->data;
+                                       char *temp_op="";
+                                       
+                                       if (n3->level < n2->level)
+                                               dyn_lvl = n2->level;
+                                       else
+                                               dyn_lvl = n3->level;
+                                       
+
+                                       if (n3->prefix && g_ascii_strcasecmp (opnode->token, "=") == 0) {
+                                               /* see if '=' was a last operator. if so take care of it */
+                                               free_node(opnode);
+                                               free_node(opnode);
+                                               all = g_list_delete_link (all, all);
+                                               operators = g_list_delete_link (operators, operators);
+                                               opnode = operators->data;
+                                               if ((g_ascii_strcasecmp (n2->exact_token,  "follow-up") == 0) ||
+                                                   (g_ascii_strcasecmp (n2->exact_token,  "completed-on") == 0)) {
+                                                       /* swap */
+                                                       char *temp = n2->exact_token;
+                                                       n2->exact_token = n1->exact_token;
+                                                       n1->exact_token = temp;
+                                                       temp = n2->exact_token;
+                                                       n2->exact_token = n3->exact_token;
+                                                       n3->exact_token = temp;
+                                                       n3->ignore_lhs = 0;
+                                                       if (g_ascii_strcasecmp (opnode->token, "not") == 0)
+                                                               temp_op = "LIKE";
+                                                       else
+                                                               temp_op = "NOT LIKE";
+                                                       prefix="";      
+                                               } else {
+                                                       /* user tags like important */
+                                                       g_free(n2->exact_token);
+                                                       n2->exact_token = n3->exact_token;
+                                                       n3->exact_token = g_strdup("");
+                                                       temp_op = "LIKE";
+                                                       n3->ignore_lhs = 0;
+                                               }
+                                               
+                                                       
+                                       }
+                                       if (n3->prefix && ((g_ascii_strcasecmp (opnode->token, ">") == 0) || (g_ascii_strcasecmp (opnode->token, ">") == 0) )) {
+                                               /* see if '=' was a last operator. if so take care of it */
+                                               free_node(opnode);
+                                               free_node(opnode);
+                                               all = g_list_delete_link (all, all);
+                                               operators = g_list_delete_link (operators, operators);
+                                               opnode = operators->data;
+                                               if ((g_ascii_strcasecmp (n2->exact_token,  "follow-up") == 0) ||
+                                                   (g_ascii_strcasecmp (n2->exact_token,  "completed-on") == 0)) {
+                                                       /* swap */
+                                                       char *temp = n2->exact_token;
+                                                       n2->exact_token = n1->exact_token;
+                                                       n1->exact_token = temp;
+                                                       temp = n2->exact_token;
+                                                       n2->exact_token = n3->exact_token;
+                                                       n3->exact_token = temp;
+                                                       n3->ignore_lhs = 0;
+                                                       if (g_ascii_strcasecmp (opnode->token, "not") == 0)
+                                                               temp_op = "LIKE";
+                                                       else
+                                                               temp_op = "NOT LIKE";
+                                                       prefix="";      
+                                               } else {
+                                                       /* user tags like important */
+                                                       g_free(n2->exact_token);
+                                                       n2->exact_token = n3->exact_token;
+                                                       n3->exact_token = g_strdup("");
+                                                       temp_op = "LIKE";
+                                                       n3->ignore_lhs = 0;
+                                               }
+                                               
+                                                       
+                                       }                                       
+                                       
+                                       /* Handle if 'not' was a last sysnode, if so take care of it */
+                                       if (n3->prefix && g_ascii_strcasecmp (opnode->token, "not") == 0) {
+                                               if (!prefix)
+                                                       prefix = "NOT ";
+                                               free_node(opnode);
+                                               free_node(opnode);
+                                               operators = g_list_delete_link (operators, operators);
+                                               all = g_list_delete_link (all, all);
+                                       } 
+                                       
+                                       /* n2 needs to be db specific */
+                                       sqstr = g_strdup_printf("%c%s%c", n3->pre_token, n1->exact_token, n3->post_token);
+                                       escstr = escape_values(sqstr);
+                                       str = g_strdup_printf("(%s %s%s %s %s)", n3->ignore_lhs ? "":n2->exact_token, prefix ? prefix : " ", temp_op, n3->exact_token, escstr);
+                                       //printf("str %s\n", str);
+
+                                       g_free (n3->exact_token);
+                                       g_free (sqstr);
+                                       g_free (escstr);
+                                       
+                                       n3->exact_token = str;
+                                       n3->prefix = 0;
+                                       n3->nodes = (pnode ? pnode->nodes : 0 ) > 0 ? pnode->nodes -1 : 0;
+                                       n3->level = dyn_lvl;
+                                       operands = g_list_prepend (operands, n3);
+                                       free_node (n2); free_node(n1);
+                                       free_node (n2); free_node(n1);
+                                       d(printf("Pushed %s\n", n3->exact_token));
+                                       all = g_list_prepend (all, n3);
+                               } else {
+                                       char prefix = 0;
+                                       char *str, *estr;
+                                       Node *opnode = operators->data;
+                                       int dyn_lvl = n1->level;
+
+                                       if (n2->prefix && g_ascii_strcasecmp (opnode->token, "not") == 0) {
+                                               prefix = '!';
+                                               dyn_lvl = opnode->level;
+                                               free_node(opnode); free_node(opnode);
+                                               operators = g_list_delete_link (operators, operators);
+                                               all = g_list_delete_link (all, all);
+                                               //g_node_dump (operators);
+                                       }
+
+                                       if (n2->execute) {
+                                               Node *popnode=NULL;
+                                               gboolean dbl = FALSE;
+                                               
+                                               //g_node_dump (operators);
+                                               if (n2->prefix) {
+                                                       if (operators->next)
+                                                               popnode = operators->next->data;
+
+                                                       if (g_ascii_strcasecmp (popnode->token, "not") == 0) {
+                                                               prefix = '!';
+                                                               dbl = TRUE;
+                                                       }
+                                               }
+                                               str = g_strdup_printf("(%s %c%s %s)", n2->exact_token, prefix ? prefix : ' ', opnode->exact_token, n1->exact_token);
+                                               
+                                               free_node(opnode); free_node(opnode);
+                                               operators = g_list_delete_link (operators, operators);
+                                               all = g_list_delete_link (all, all);
+                                               if (dbl) {
+                                                       free_node(opnode); free_node(opnode);
+                                                       operators = g_list_delete_link (operators, operators);
+                                                       all = g_list_delete_link (all, all);                                                    
+                                               }
+                                               
+                                       } else {
+                                               if (!n2->swap) {
+                                                       str = g_strdup_printf("(%s %c%s %c)", n1->exact_token, prefix ? prefix : ' ', n2->exact_token, n2->rval);
+                                               } else {
+                                                       str = g_strdup_printf("%c%c%s%c", prefix ? prefix : ' ', n2->pre_token, n1->exact_token, n2->post_token);
+                                                       estr = escape_values(str);
+                                                       g_free(str);
+                                                       str = g_strdup_printf("(%s %s %c)", n2->exact_token, estr, n2->rval ? n2->rval : ' ');
+                                                       g_free(estr);
+                                               }
+                                       }
+                                       g_free (n2->exact_token);
+                                       
+                                       n2->exact_token = str;
+                                       n2->prefix = 0;
+                                       n2->nodes = (pnode ? pnode->nodes : 0 )> 0 ? pnode->nodes -1 : 0;
+                                       n2->level = dyn_lvl;
+                                       operands = g_list_prepend (operands, n2);
+                                       d(printf("Pushed %s\n", n2->exact_token));                                      
+                                       free_node(n1);
+                                       free_node(n1);
+
+                                       all = g_list_prepend (all, n2);
+                               }
+                                       
+                       }
+               }
+       
+
+       }
+       
+
+       tmp = operands;
+       d(g_node_dump (operands));
+       while (tmp) {
+                free_node(tmp->data); 
+                tmp = tmp->next;
+       }
+       d(g_node_dump (operands));
+
+       g_list_free (operands);
+       d(printf("\n\n\n"));
+       d(g_node_dump (operators));
+       tmp = operators;
+       while (tmp) {
+                free_node(tmp->data); 
+                tmp = tmp->next;
+       }       
+       g_list_free (operators);
+       d(printf("\n\n\n"));
+       d(g_node_dump (all));
+       d(printf("\n\n\n"));
+       
+       
+       res=NULL;
+       tmp = all;
+       op=NULL; n1=NULL;
+
+       /* Time to operate on the stack. */
+       tmp = all;
+       while (all->next) {
+                n1 = tmp->data;
+                all = g_list_delete_link (all, all);
+                tmp = all;
+
+                if (n1->operator) {
+                         if (res->next) {
+                                  GList *ts=res;
+                                  Node *n = ts->data;
+                                  GString *s = g_string_new (NULL);
+                                       
+                                  g_string_append_printf (s, "(%s", n->exact_token);
+                                  ts = ts->next;
+                                  while (ts) { /* should have atleast 2 nodes */
+                                               free_node (n);
+                                               n = ts->data;
+                                               g_string_append_printf (s, " %s %s", n1->exact_token, n->exact_token);
+                                               ts = ts->next;
+                                  }
+
+                                  g_string_append_printf (s, ")");
+                                  g_free (n->exact_token);
+                                  n->exact_token = s->str;
+                                  g_string_free (s, FALSE);
+                                  all = g_list_prepend (all, n); 
+                         } else /* remove single operand nodes */
+                                  all = g_list_prepend (all, res->data);
+
+                         free_node (n1);
+                         g_list_free (res);
+                         res = NULL;
+                         tmp = all;
+                } else {
+                         res = g_list_prepend (res, n1);
+                }
+       }
+               
+       n1 = res->data;
+       sql = g_strdup (n1->exact_token);
+       free_node (n1);
+       g_list_free (res);
+       
+       tlist = all;
+       while (tlist) {
+               free_node (tlist->data);
+               tlist = tlist->next;
+       }
+       g_list_free (all);
+
+       g_scanner_destroy(scanner);
+
+       return sql;
+}
+
+#ifdef TEST_MAIN
+/*
+
+
+(and (match-all (and (not (system-flag "deleted")) (not (system-flag "junk"))))
+ (and   (or
+  
+     (match-all (not (system-flag "Attachments")))
+    
+  )
+ ))
+"
+replied INTEGER ,                (match-all (system-flag  "Answered"))
+size INTEGER ,                   (match-all (< (get-size) 100))
+dsent NUMERIC ,                  (match-all (< (get-sent-date) (- (get-current-date) 10)))
+dreceived NUMERIC ,               (match-all (< (get-received-date) (- (get-current-date) 10)))
+//mlist TEXT ,                      x-camel-mlist   (match-all (header-matches "x-camel-mlist"  "gnome.org"))
+//attachment,                      system-flag "Attachments"   (match-all (system-flag "Attachments")) 
+//followup_flag TEXT ,             (match-all (not (= (user-tag "follow-up") "")))
+//followup_completed_on TEXT ,      (match-all (not (= (user-tag "completed-on") "")))
+//followup_due_by TEXT ," //NOTREQD
+*/
+
+char * camel_db_get_column_name (const char *raw_name)
+{
+       d(g_print ("\n\aRAW name is : [%s] \n\a", raw_name));
+       if (!g_ascii_strcasecmp (raw_name, "Subject"))
+               return g_strdup ("subject");
+       else if (!g_ascii_strcasecmp (raw_name, "from"))
+               return g_strdup ("mail_from");
+       else if (!g_ascii_strcasecmp (raw_name, "Cc"))
+               return g_strdup ("mail_cc");
+       else if (!g_ascii_strcasecmp (raw_name, "To"))
+               return g_strdup ("mail_to");
+       else if (!g_ascii_strcasecmp (raw_name, "Flagged"))
+               return g_strdup ("important");
+       else if (!g_ascii_strcasecmp (raw_name, "deleted"))
+               return g_strdup ("deleted");
+       else if (!g_ascii_strcasecmp (raw_name, "junk"))
+               return g_strdup ("junk");
+       else if (!g_ascii_strcasecmp (raw_name, "Answered"))
+               return g_strdup ("replied");    
+       else if (!g_ascii_strcasecmp (raw_name, "Seen"))
+               return g_strdup ("read");
+       else if (!g_ascii_strcasecmp (raw_name, "user-tag"))
+               return g_strdup ("usertags");   
+       else if (!g_ascii_strcasecmp (raw_name, "user-flag"))
+               return g_strdup ("labels");     
+       else if (!g_ascii_strcasecmp (raw_name, "Attachments"))
+               return g_strdup ("attachment");
+       else if (!g_ascii_strcasecmp (raw_name, "x-camel-mlist"))
+               return g_strdup ("mlist");      
+       else {
+               /* Let it crash for all unknown columns for now. 
+               We need to load the messages into memory and search etc. 
+               We should extend this for camel-folder-search system flags search as well 
+               otherwise, search-for-signed-messages will not work etc.*/
+
+               return g_strdup (raw_name);
+       }
+
+}
+
+int main ()
+{
+
+       int i=0;
+       char *txt[] = {
+       "(and  (and   (match-all (header-contains \"From\"  \"org\"))   )  (match-all (not (system-flag \"junk\"))))", 
+       "(and  (and (match-all (header-contains \"From\"  \"org\"))) (and (match-all (not (system-flag \"junk\"))) (and   (or (match-all (header-contains \"Subject\"  \"test\")) (match-all (header-contains \"From\"  \"test\"))))))", 
+       "(and  (and   (match-all (header-exists \"From\"))   )  (match-all (not (system-flag \"junk\"))))", 
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (header-contains \"Subject\"  \"org\")) (match-all (header-contains \"From\"  \"org\")) (match-all (system-flag  \"Flagged\")) (match-all (system-flag  \"Seen\")) )))",
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (header-ends-with \"To\"  \"novell.com\") (header-ends-with \"Cc\"  \"novell.com\"))) (match-all (or (= (user-tag \"label\")  \"work\")  (user-flag  \"work\"))) )))", 
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (header-ends-with \"To\"  \"novell.com\") (header-ends-with \"Cc\"  \"novell.com\"))) ((= (user-tag \"label\")  \"work\") ) )))", 
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (header-ends-with \"To\"  \"novell.com\") (header-ends-with \"Cc\"  \"novell.com\"))) (user-flag  \"work\") )))", 
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (header-ends-with \"To\"  \"novell.com\") (header-ends-with \"Cc\"  \"novell.com\"))) (user-flag  (+ \"$Label\"  \"work\")) )))",
+
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (not (= (user-tag \"follow-up\") \"\"))) )))",
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (= (user-tag \"follow-up\") \"\")) )))",
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (not (= (user-tag \"completed-on\") \"\"))) )))",
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (or (= (user-tag \"label\")  \"important\") (user-flag (+ \"$Label\"  \"important\")) (user-flag  \"important\"))) ))",              
+       "(or (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")) (not (system-flag \"Attachments\")) (not (system-flag \"Answered\")))) (and   (or (match-all (= (user-tag \"completed-on\") \"\")) )))",
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (= (user-tag \"completed-on\") \"\")) (match-all (= (user-tag \"follow-up\") \"\")) )))",
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (> (get-sent-date) (- (get-current-date) 100))) )))",
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (< (get-sent-date) (+ (get-current-date) 100))) )))",
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (not (= (get-sent-date) 1216146600))) )))",
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (or (match-all (= (get-sent-date) 1216146600)) )))" ,
+       "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and   (and (match-all (header-contains \"Subject\"  \"mysubject\")) (match-all (not (header-matches \"From\"  \"mysender\"))) (match-all (= (get-sent-date) (+ (get-current-date) 1))) (match-all (= (get-received-date) (- (get-current-date) 604800))) (match-all (or (= (user-tag \"label\")  \"important\") (user-flag (+ \"$Label\"  \"important\")) (match-all (< (get-size) 7000)) (match-all (not (= (get-sent-date) 1216146600)))  (match-all (> (cast-int (user-tag \"score\")) 3))  (user-flag  \"important\"))) (match-all (system-flag  \"Deleted\")) (match-all (not (= (user-tag \"follow-up\") \"\"))) (match-all (= (user-tag \"completed-on\") \"\")) (match-all (system-flag \"Attachments\")) (match-all (header-contains \"x-camel-mlist\"  \"evo-hackers\")) )))",
+
+       };
+
+       for (i=18; i < G_N_ELEMENTS(txt); i++) {
+               char *sql;
+               printf("Q: %s\n\n", txt[i]);            
+               sql = camel_sexp_to_sql (txt[i]);
+               printf("A: %s\n\n\n", sql);
+               g_free (sql);
+       }
+
+}
+#endif
diff --git a/camel/camel-search-sql.h b/camel/camel-search-sql.h
new file mode 100644 (file)
index 0000000..845e0d1
--- /dev/null
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright (C) 2008 Novell, Inc. (www.novell.com)
+ *
+ *  Authors: Srinivsa Ragavan <sragavan@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CAMEL_SEARCH_SQL_H
+#define _CAMEL_SEARCH_SQL_H
+
+G_BEGIN_DECLS
+
+char * camel_sexp_to_sql (const char *sexp);
+
+G_END_DECLS
+
+#endif /* ! _CAMEL_SEARCH_SQL_H */