evolution-data-server r9175 - trunk/camel
- From: sragavan svn gnome org
- To: svn-commits-list gnome org
- Subject: evolution-data-server r9175 - trunk/camel
- Date: Thu, 24 Jul 2008 05:54:42 +0000 (UTC)
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]