evolution-data-server r9175 - trunk/camel



Author: sragavan
Date: Thu Jul 24 05:54:42 2008
New Revision: 9175
URL: http://svn.gnome.org/viewvc/evolution-data-server?rev=9175&view=rev

Log:
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:


Added:
   trunk/camel/camel-search-sql.c
   trunk/camel/camel-search-sql.h
Modified:
   trunk/camel/ChangeLog
   trunk/camel/camel-folder-search.c
   trunk/camel/camel-folder-summary.c

Modified: trunk/camel/camel-folder-search.c
==============================================================================
--- trunk/camel/camel-folder-search.c	(original)
+++ trunk/camel/camel-folder-search.c	Thu Jul 24 05:54:42 2008
@@ -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 @@
 	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 @@
 		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 @@
 
 	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 */

Modified: trunk/camel/camel-folder-summary.c
==============================================================================
--- trunk/camel/camel-folder-summary.c	(original)
+++ trunk/camel/camel-folder-summary.c	Thu Jul 24 05:54:42 2008
@@ -2917,12 +2917,12 @@
 	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;

Added: trunk/camel/camel-search-sql.c
==============================================================================
--- (empty file)
+++ trunk/camel/camel-search-sql.c	Thu Jul 24 05:54:42 2008
@@ -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

Added: trunk/camel/camel-search-sql.h
==============================================================================
--- (empty file)
+++ trunk/camel/camel-search-sql.h	Thu Jul 24 05:54:42 2008
@@ -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 */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]