phonebookengines/contactsmodel/cntplsql/src/cntsqlsearch.cpp
changeset 46 efe85016a067
equal deleted inserted replaced
40:b46a585f6909 46:efe85016a067
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the examples of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 #include <QStringList>
       
    42 
       
    43 #include "cntsqlsearch.h"
       
    44 #include "cqwertykeymap.h"
       
    45 #include "c12keykeymap.h"
       
    46 #include <QHash>
       
    47 #include <QLocale>
       
    48 
       
    49 const char KLimitLength = 15;
       
    50 const int KTwoTokens = 2;
       
    51 const int KOneToken = 1;
       
    52 const char KLowerLimitPadding = '0';
       
    53 const char KUpperLimitPadding = 'F';
       
    54 const int KMinimumSearchPatternLength = 1;
       
    55 const int KHexadecimalBase = 16;
       
    56 
       
    57 
       
    58 #define ORDER_BY_FIRSTNAME_LASTNAME " ORDER BY first_name, last_name ASC;"
       
    59 #define SELECT_CONTACT_ID			"SELECT contact_id FROM "
       
    60 
       
    61 // TODO: Since the column names are repeated several times, replace them with
       
    62 // shorter names like w, x, y & z. Also replace contact_id by id etc.
       
    63 
       
    64 // Predictive search table's columns
       
    65 const QString KColumn1 = "nbr";
       
    66 const QString KColumn2 = "nbr2";
       
    67 const QString KColumn3 = "nbr3";
       
    68 const QString KColumn4 = "nbr4";
       
    69 
       
    70 // Predictive search qwert table's columns
       
    71 const QString KQm1 = "n";
       
    72 const QString KQm2 = "n2";
       
    73 const QString KQm3 = "n3";
       
    74 const QString KQm4 = "n4";
       
    75 const QString KQm5 = "n5";
       
    76 const QString KQm6 = "n6";
       
    77 const QString KQm7 = "n7";
       
    78 
       
    79 // Special handling for characters that originate from * and # keys
       
    80 const QChar KStarChar('*');
       
    81 const QChar KPlusChar('+');
       
    82 const QChar KPChar('p');
       
    83 const QChar KWChar('w');
       
    84 const QChar KHashChar('#');
       
    85 
       
    86 //Predictive search table
       
    87 const QString QwertyTableName = "qm";
       
    88 
       
    89 CntSqlSearch::CntSqlSearch()
       
    90 	{
       
    91     QT_TRAP_THROWING(mQertyKeyMap = CQwertyKeyMap::NewL());
       
    92     QT_TRAP_THROWING(mkeyKeyMap = C12keyKeyMap::NewL());
       
    93 	}
       
    94 
       
    95 CntSqlSearch::~CntSqlSearch()
       
    96     {
       
    97     delete mQertyKeyMap;
       
    98     delete mkeyKeyMap;
       
    99     }
       
   100 // Basic cases:
       
   101 // 1: "0", "5"
       
   102 // Just one digit. Select all contact ids from the table. No need to compare
       
   103 // values.
       
   104 //
       
   105 // 2: "123", "01", "10", "010", "00"
       
   106 // No zeros which have non-zeros in their both sides
       
   107 // One or zero tokens, when pattern is split using '0'.
       
   108 //
       
   109 // 3: "101", "1001"
       
   110 // Same digit of both sides of the zero
       
   111 // Two tokens, each with length of 1 and tokens are the same.
       
   112 // The queries of case 3 could also be handled with case 4 query, but that
       
   113 // would yield a longer SQL statement.
       
   114 //
       
   115 // 4: "102", "1002"
       
   116 // One or more zeros in the middle, just one digit on either side of the zero(s)
       
   117 // and those digits are not the same.
       
   118 // Two tokens, each with length of 1 and tokens are different.
       
   119 //
       
   120 // 5: "1023", "0102", "1010", "00100200", "10203", "01020304050"
       
   121 // Two tokens, at least one of them has length > 1.
       
   122 // If tokens are identical, handle as case 3, otherwise handle as case 4.
       
   123 // ("10203" -> tokens "1" and "203"
       
   124 //  "0010023004560" -> tokens "001" and "23004560")
       
   125 //
       
   126 // 6: "10", "1000"
       
   127 // One token, ends with zero.
       
   128 // In this case, query should look-up first toke and number ("10", "1000").
       
   129 
       
   130 QString CntSqlSearch::CreatePredictiveSearch(const QString &pattern)
       
   131 	{
       
   132 	int len = pattern.length();
       
   133 	QString newPattern;
       
   134 	if (isQwerty(pattern))
       
   135 	    {
       
   136         return CreateQwertyQuery(pattern);
       
   137 	    }
       
   138 	else
       
   139 	    {
       
   140         newPattern = ChangeStringPadings(pattern);
       
   141         // For best performance, handle 1 digit case first
       
   142         if (len == KMinimumSearchPatternLength)
       
   143             {
       
   144             // Case 1
       
   145             return SELECT_CONTACT_ID + SelectTable(newPattern) + ORDER_BY_FIRSTNAME_LASTNAME;
       
   146             }
       
   147         if (len <= KLimitLength && len > KMinimumSearchPatternLength)
       
   148             {
       
   149             return CreateQuery(newPattern);
       
   150             }
       
   151         return QString(""); // Invalid pattern
       
   152         }
       
   153 	}
       
   154 QString CntSqlSearch::selectQweryTable(const QString &pattern) const
       
   155     {
       
   156     QString tableNumber; 
       
   157     if(pattern.length() > 0)
       
   158         {
       
   159         return QwertyTableName + tableNumber.setNum(mQertyKeyMap->MapKeyNameToValue(pattern[0]));
       
   160         }
       
   161     else
       
   162         {
       
   163         return QString("");
       
   164         }
       
   165     }
       
   166 QString CntSqlSearch::SelectTable(const QString &pattern) const
       
   167 	{
       
   168         QString predictivesearch;
       
   169         QStringList tokens = GetTokens(pattern);
       
   170         bool ok;
       
   171         if (pattern.length() == 0)
       
   172                 {
       
   173                 return "";
       
   174                 }
       
   175         QString firstNumber(pattern.at(0));
       
   176         uint hex = firstNumber.toUInt(&ok, 16);
       
   177         if (!ok)
       
   178             {
       
   179             // TODO: handle error (=invalid characters in pattern)
       
   180             }
       
   181         switch (hex)
       
   182             {
       
   183             case 0:
       
   184                 {
       
   185                 predictivesearch = QString("predictivesearch0");
       
   186                 }
       
   187             break;
       
   188             case 1:
       
   189                 {
       
   190                 predictivesearch = QString("predictivesearch1");
       
   191                 }
       
   192             break;
       
   193             case 2:
       
   194                 {
       
   195                 predictivesearch = QString("predictivesearch2");
       
   196                 }
       
   197             break;
       
   198             case 3:
       
   199                 {
       
   200                 predictivesearch = QString("predictivesearch3");
       
   201                 }
       
   202             break;
       
   203             case 4:
       
   204                 {
       
   205                 predictivesearch = QString("predictivesearch4");
       
   206                 }
       
   207             break;
       
   208             case 5:
       
   209                 {
       
   210                 predictivesearch = QString("predictivesearch5");
       
   211                 }
       
   212             break;
       
   213             case 6:
       
   214                 {
       
   215                 predictivesearch = QString("predictivesearch6");
       
   216                 }
       
   217             break;
       
   218             case 7:
       
   219                 {
       
   220                 predictivesearch = QString("predictivesearch7");
       
   221                 }
       
   222             break;
       
   223             case 8:
       
   224                 {
       
   225                 predictivesearch = QString("predictivesearch8");
       
   226                 }
       
   227             break;
       
   228             case 9:
       
   229                 {
       
   230                 predictivesearch = QString("predictivesearch9");
       
   231                 }
       
   232              break;
       
   233             case 10:
       
   234                 {
       
   235                 predictivesearch = QString("predictivesearch10");
       
   236                 }
       
   237             break;
       
   238             case 11:
       
   239                 {
       
   240                 predictivesearch = QString("predictivesearch11");
       
   241                 }
       
   242             break;
       
   243                     default: // error
       
   244                             predictivesearch = "";
       
   245                             break;
       
   246             }
       
   247             return predictivesearch;
       
   248 	}
       
   249 
       
   250 // Even if there are over 2 tokens, make 2 tokens.
       
   251 // If there are two or more tokens, include the leading zeros in the first
       
   252 // token the and trailing zeros in the second token.
       
   253 // E.g. "0010230" results tokens "001" and "230" and
       
   254 // "001230045067800900" tokens "00123" and "45067800900".
       
   255 QStringList CntSqlSearch::GetTokens(const QString& pattern) const
       
   256     {
       
   257     const QChar KZero('0');
       
   258     QStringList tokens = pattern.split(KZero, QString::SkipEmptyParts);
       
   259     if (tokens.count() < KTwoTokens)
       
   260         {
       
   261         return tokens;
       
   262         }
       
   263 
       
   264     QStringList twoTokens;
       
   265     int i(0);
       
   266     while (pattern[i] == KZero) // Skip leading zeros
       
   267         {
       
   268         ++i;
       
   269         }
       
   270     while (pattern[i] != KZero) // Skip non-zeros to find where first token ends
       
   271         {
       
   272         ++i;
       
   273         }
       
   274     twoTokens.append(pattern.left(i));
       
   275     
       
   276     while (pattern[i] == KZero) // Skip zeros to find where second token begins
       
   277         {
       
   278         ++i;
       
   279         }
       
   280     twoTokens.append(pattern.mid(i));
       
   281     return twoTokens;
       
   282     }
       
   283 
       
   284 QString CntSqlSearch::CreateQwertyQuery(const QString& pattern) const
       
   285     {
       
   286     QStringList qwertyString; 
       
   287     qwertyString = qwertyTokens(pattern);
       
   288     if(qwertyString.count() == 1 )
       
   289         {
       
   290         if(qwertyString.at(0).length() == 1)
       
   291             {
       
   292             return SELECT_CONTACT_ID + selectQweryTable(qwertyString.at(0)) + ORDER_BY_FIRSTNAME_LASTNAME;
       
   293             }
       
   294         else if (qwertyString.at(0).length() > 1)
       
   295             {
       
   296             return ExactMatchSearchQwerty(qwertyString.at(0)) + ORDER_BY_FIRSTNAME_LASTNAME;
       
   297             }
       
   298         else
       
   299             {
       
   300             //Empty string
       
   301             return QString(""); 
       
   302             }
       
   303         }
       
   304     else
       
   305         {
       
   306         return SearchTokensFromOneTable(pattern, qwertyString, CntSqlSearch::QwertyEmail);
       
   307         }
       
   308     }
       
   309 // pattern length is between KMinimumSearchPatternLength...KLimitLength
       
   310 QString CntSqlSearch::CreateQuery(const QString& pattern) const
       
   311 	{
       
   312 	QStringList tokens = GetTokens(pattern);
       
   313 	if (tokens.count() < KTwoTokens)
       
   314         {
       
   315 	    if (TestPattern(pattern, CntSqlSearch::ZerosEndOfFirstToken))
       
   316             {
       
   317             return TwoDifferentTokensSearch(pattern, tokens);  // Case 6
       
   318             }
       
   319         else
       
   320             {
       
   321             return ExactMatchSearch(pattern) + Order(tokens); // Case 2
       
   322             }
       
   323         }
       
   324 	else
       
   325         {
       
   326         if (tokens.at(0) == tokens.at(1))
       
   327             {
       
   328             return IdenticalTokensSearch(pattern, tokens); // Case 3
       
   329             }
       
   330         else
       
   331             {
       
   332             return IntersectionSearch(pattern, tokens); // Case 4
       
   333             }
       
   334         }
       
   335 	}
       
   336 
       
   337 QString CntSqlSearch::ExactMatchSearch(const QString& pattern) const
       
   338 	{
       
   339 	return QString(SELECT_CONTACT_ID + SelectTable(pattern) +
       
   340 		           " WHERE " + ExactMatch(pattern)); 
       
   341 	}
       
   342 
       
   343 QString CntSqlSearch::ExactMatchSearchQwerty(const QString& pattern) const
       
   344     {
       
   345     return QString(SELECT_CONTACT_ID + selectQweryTable(pattern) +
       
   346                    " WHERE " + ExactMatchQwerty(pattern)); 
       
   347     }
       
   348 
       
   349 // It has been checked that tokens are different, but they might begin with
       
   350 // the same digit.
       
   351 QString CntSqlSearch::IntersectionSearch(const QString& pattern,
       
   352 									     const QStringList& tokens) const
       
   353 	{
       
   354 #if defined(SEARCH_FROM_ONE_TABLE)
       
   355 	return SearchTokensFromOneTable(pattern, tokens);
       
   356 #else
       
   357 /* Query for pattern = "205":
       
   358 SELECT predictivesearch2.contact_id FROM predictivesearch2 WHERE EXISTS
       
   359 (SELECT contact_id FROM predictivesearch5 WHERE predictivesearch2.contact_id = predictivesearch5.contact_id)
       
   360 OR
       
   361 (SELECT contact_id FROM predictivesearch2 
       
   362 WHERE(predictivesearch2.nbr>145522562959409152 AND predictivesearch2.nbr<145804037936119807) OR
       
   363 (predictivesearch2.nbr2>145522562959409152 AND predictivesearch2.nbr2<145804037936119807) OR
       
   364 (predictivesearch2.nbr3>145522562959409152 AND predictivesearch2.nbr3<145804037936119807) OR
       
   365 (predictivesearch2.nbr4>145522562959409152 AND predictivesearch2.nbr4<145804037936119807))
       
   366 ORDER BY predictivesearch2.first_name, predictivesearch2.last_name ASC;
       
   367 
       
   368 This query works if both tokens have just one digit (e.g. "102", but not "1023")
       
   369 */	
       
   370 	if (tokens.at(0).length() == KMinimumSearchPatternLength &&
       
   371 		tokens.at(1).length() == KMinimumSearchPatternLength) 
       
   372 		{
       
   373 		// Case 4
       
   374 		QString firstTable = SelectTable(tokens.at(0));
       
   375 		QString secondTable = SelectTable(tokens.at(1));
       
   376 		QString query =
       
   377 			"SELECT " + firstTable + ".contact_id FROM " + firstTable +
       
   378 			" WHERE EXISTS (" + SELECT_CONTACT_ID + secondTable + " WHERE " +
       
   379 			firstTable + ".contact_id = " + secondTable + ".contact_id) OR (" +
       
   380 			SELECT_CONTACT_ID + firstTable + " WHERE " + ExactMatch(pattern, firstTable) + ")";
       
   381 		return query  + Order(tokens);
       
   382 		}
       
   383 	if (tokens.at(0).at(0) == tokens.at(1).at(0) ||
       
   384 	    tokens.at(0).length() > 1 && tokens.at(1).length() > 1)
       
   385 		{
       
   386 		// Tokens begin with same digit or both tokens are longer than one digit.
       
   387 		// Must search from one table.
       
   388 		return SearchTokensFromOneTable(pattern, tokens);
       
   389 		}
       
   390 	return CreateJoinTableSearch(pattern, tokens); // Case 5
       
   391 #endif
       
   392 	}
       
   393 
       
   394 // Find the exact match, or a column whose value is within
       
   395 // lower..upper(exclusive) and another column whose value is within
       
   396 // lower2..upper2(exclusive).
       
   397 // In this case the limits are different, so there are 12 combinations the two
       
   398 // values can exist in four columns:
       
   399 // 
       
   400 // (column = X  AND column2 = Y) OR
       
   401 // (column = X  AND column3 = Y) OR
       
   402 // (column = X  AND column4 = Y) OR
       
   403 // (column2 = X AND column3 = Y) OR
       
   404 // (column2 = X AND column4 = Y) OR
       
   405 // (column3 = X AND column4 = Y) OR
       
   406 // (column = Y  AND column2 = X) OR
       
   407 // (column = Y  AND column3 = X) OR
       
   408 // (column = Y  AND column4 = X) OR
       
   409 // (column2 = Y AND column3 = X) OR
       
   410 // (column2 = Y AND column4 = X) OR
       
   411 // (column3 = Y AND column4 = X)
       
   412 //
       
   413 //
       
   414 // Qwert case
       
   415 // Where X means: (value > lower-limit AND value < upper-limit)
       
   416 // and Y means: (value > lower-limit-2 AND value < upper-limit-2)
       
   417 QString CntSqlSearch::SearchTokensFromOneTable(const QString& pattern,
       
   418 											   const QStringList& tokens,
       
   419 											   QueryType queryType) const
       
   420     {
       
   421     QString token;
       
   422     QString lower;
       
   423     QString upper;
       
   424     QString lower2;
       
   425     QString upper2;
       
   426     int err;
       
   427     
       
   428     if(queryType == CntSqlSearch::TwelveTable)
       
   429         {
       
   430         err = mkeyKeyMap->GetNumericLimits(tokens.at(0), lower, upper);
       
   431         if(err)
       
   432             {
       
   433             return QString("");
       
   434             }
       
   435         err = mkeyKeyMap->GetNumericLimits(tokens.at(1), lower2, upper2);
       
   436         if(err)
       
   437             {
       
   438             return QString("");
       
   439             }
       
   440         }
       
   441     else
       
   442         {
       
   443         err = mQertyKeyMap->GetNumericLimits(tokens.at(0), lower, upper);
       
   444         err = mQertyKeyMap->GetNumericLimits(tokens.at(1), lower2, upper2);
       
   445         if(err)
       
   446             {
       
   447             return QString("");
       
   448             }
       
   449         }
       
   450     QString query; 
       
   451     if (queryType == CntSqlSearch::TwelveTable)
       
   452 		{
       
   453         query = SELECT_CONTACT_ID + SelectTable(pattern) + " WHERE NOT(NOT" +
       
   454 	    ExactMatch(pattern) + " AND NOT" +
       
   455 		CompareTwoColumns(lower, upper, lower2, upper2) + " AND NOT" +
       
   456         CompareTwoColumns(lower2, upper2, lower, upper) + ")";
       
   457 		}
       
   458     else
       
   459         {
       
   460     
       
   461         query = SELECT_CONTACT_ID + selectQweryTable(tokens.at(0)) + " WHERE NOT(NOT" +
       
   462         ExactMatchQwerty(pattern) + " AND NOT" +
       
   463         CompareTwoQwertyColumns(lower, upper, lower2, upper2) + " AND NOT" +
       
   464         CompareTwoQwertyColumns(lower2, upper2, lower, upper) + ")";
       
   465         }
       
   466 	query += Order(tokens, queryType);
       
   467 	return query;
       
   468 	}
       
   469 
       
   470 // Either an exact match is required, or tokens must be found, but not in the
       
   471 // same column.
       
   472 // Since tokens are identical, they have same limits, and one call to
       
   473 // CompareTwoColumns() is enough.
       
   474 QString CntSqlSearch::IdenticalTokensSearch(const QString& pattern,
       
   475 										    const QStringList& tokens) const
       
   476 	{
       
   477 	QString token = tokens.at(0);
       
   478 	QString lower;
       
   479 	QString upper;
       
   480 	
       
   481 	TInt err = mkeyKeyMap->GetNumericLimits(token, lower, upper);
       
   482 	    
       
   483 #if defined(USE_DEMORGAN)
       
   484 	QString query(SELECT_CONTACT_ID + SelectTable(pattern) + " WHERE NOT(NOT" +
       
   485 	    ExactMatch(pattern) +
       
   486         " AND NOT" + CompareTwoColumns(lower, upper, lower, upper) + ")");
       
   487 #else
       
   488 	QString query(SELECT_CONTACT_ID + SelectTable(pattern) + " WHERE (" +
       
   489 	    ExactMatch(pattern) +  // exact match (e.g. "101")
       
   490         ") OR " + CompareTwoColumns(lower, upper, lower, upper));
       
   491 #endif
       
   492 	query += Order(tokens);
       
   493 	return query;
       
   494 	}
       
   495 
       
   496 
       
   497 QString CntSqlSearch::TwoDifferentTokensSearch(const QString& pattern, const QStringList& tokens) const
       
   498         {
       
   499         QString token = tokens.at(0);
       
   500         QString sortPatern = pattern;
       
   501         sortPatern.truncate(pattern.length()-1);
       
   502 #if defined(USE_DEMORGAN)
       
   503         QString query(SELECT_CONTACT_ID + SelectTable(pattern) + " WHERE NOT(NOT" +
       
   504             ExactMatch(sortPatern) +
       
   505         " AND NOT" + ExactMatch(pattern) + ")");
       
   506 #else
       
   507         QString query(SELECT_CONTACT_ID + SelectTable(pattern) + " WHERE (" +
       
   508             ExactMatch(sortPatern) +  // exact match (e.g. "2")
       
   509         ") OR " + ExactMatch(pattern)); // exact match (e.g. "20")
       
   510 #endif
       
   511         query += Order(tokens);
       
   512         return query;
       
   513         }
       
   514 
       
   515 
       
   516 // Put individual AND / OR operations in such order that in most cases there is
       
   517 // no need to evaluate all arguments of the AND / OR.
       
   518 // In case of AND, put the less likely condition on the left side of AND.
       
   519 // In case of OR, put the more likely condition on the left side of OR.
       
   520 // Since 2nd column is more likely to be empty, compare it before 1st column.
       
   521 // Since 1st & 2nd columns are more likely to contain a match, compare them
       
   522 // before other column combinations (1st & 3rd, 2nd & 3rd etc)
       
   523 QString CntSqlSearch::CompareTwoColumns(const QString& lower,
       
   524 										const QString& upper,
       
   525 										const QString& lower2,
       
   526 										const QString& upper2) const
       
   527 	{
       
   528 #if defined(USE_DEMORGAN)
       
   529 	// Using De Morgan's law to replace ORs with ANDs:
       
   530 	// A || B || C || D || E || F --> !(!A && !B && !C && !D && !E && !F)
       
   531 	//
       
   532 	// As A (match found in columns 1 and 2) is more likely true than other
       
   533 	// combinations, then !A is more likely false than other combinations, so
       
   534 	// it is put first in the AND statement.	
       
   535 	QString query =
       
   536 		"(NOT(NOT(" +
       
   537 		// 2nd & 1st column (='A')
       
   538 		KColumn2 + ">" + lower2 + " AND " + KColumn2 + "<" + upper2 + " AND " +
       
   539 		KColumn1 + ">" + lower + " AND " + KColumn1 + "<" + upper +
       
   540 		") AND NOT(" +
       
   541 		// 3nd & 1st column (='B')
       
   542 		KColumn3 + ">" + lower2 + " AND " + KColumn3 + "<" + upper2 + " AND " +
       
   543 		KColumn1 + ">" + lower + " AND " + KColumn1 + "<" + upper +
       
   544 		") AND NOT(" +
       
   545 		// 3rd & 2nd column (='C')
       
   546 		KColumn3 + ">" + lower2 + " AND " + KColumn3 + "<" + upper2 + " AND " +
       
   547 		KColumn2 + ">" + lower + " AND " + KColumn2 + "<" + upper +
       
   548 		") AND NOT(" +
       
   549 		// 4th & 1st column (='D')
       
   550 		KColumn4 + ">" + lower2 + " AND " + KColumn4 + "<" + upper2 + " AND " +
       
   551 		KColumn1 + ">" + lower + " AND " + KColumn1 + "<" + upper +
       
   552 		") AND NOT(" +
       
   553 		// 4th & 2nd column (='E')
       
   554 		KColumn4 + ">" + lower2 + " AND " + KColumn4 + "<" + upper2 + " AND " +
       
   555 		KColumn2 + ">" + lower + " AND " + KColumn2 + "<" + upper +
       
   556 		") AND NOT(" +
       
   557 		// 4th & 3rd column (='F')
       
   558 		KColumn4 + ">" + lower2 + " AND " + KColumn4 + "<" + upper2 + " AND " +
       
   559 		KColumn3 + ">" + lower + " AND " + KColumn3 + "<" + upper + ")))";
       
   560 #else
       
   561 	QString query =
       
   562 		"(" +
       
   563 		// 2nd & 1st column
       
   564 		KColumn2 + ">" + lower2 + " AND " + KColumn2 + "<" + upper2 + " AND " +
       
   565 		KColumn1 + ">" + lower + " AND " + KColumn1 + "<" + upper +
       
   566 		") OR (" +
       
   567 		// 3nd & 1st column
       
   568 		KColumn3 + ">" + lower2 + " AND " + KColumn3 + "<" + upper2 + " AND " +
       
   569 		KColumn1 + ">" + lower + " AND " + KColumn1 + "<" + upper +
       
   570 		") OR (" +
       
   571 		// 3rd & 2nd column
       
   572 		KColumn3 + ">" + lower2 + " AND " + KColumn3 + "<" + upper2 + " AND " +
       
   573 		KColumn2 + ">" + lower + " AND " + KColumn2 + "<" + upper +
       
   574 		") OR (" +
       
   575 		// 4th & 1st column
       
   576 		KColumn4 + ">" + lower2 + " AND " + KColumn4 + "<" + upper2 + " AND " +
       
   577 		KColumn1 + ">" + lower + " AND " + KColumn1 + "<" + upper +
       
   578 		") OR (" +
       
   579 		// 4th & 2nd column
       
   580 		KColumn4 + ">" + lower2 + " AND " + KColumn4 + "<" + upper2 + " AND " +
       
   581 		KColumn2 + ">" + lower + " AND " + KColumn2 + "<" + upper +
       
   582 		") OR (" +
       
   583 		// 4th & 3rd column
       
   584 		KColumn4 + ">" + lower2 + " AND " + KColumn4 + "<" + upper2 + " AND " +
       
   585 		KColumn3 + ">" + lower + " AND " + KColumn3 + "<" + upper + ")";
       
   586 #endif
       
   587 	return query;
       
   588 	}
       
   589 
       
   590 QString CntSqlSearch::CompareTwoQwertyColumns(const QString& lower,
       
   591                                         const QString& upper,
       
   592                                         const QString& lower2,
       
   593                                         const QString& upper2) const
       
   594     {
       
   595     // Using De Morgan's law to replace ORs with ANDs:
       
   596     // A || B || C || D || E || F --> !(!A && !B && !C && !D && !E && !F)
       
   597     //
       
   598     // As A (match found in columns 1 and 2) is more likely true than other
       
   599     // combinations, then !A is more likely false than other combinations, so
       
   600     // it is put first in the AND statement.    
       
   601     QString query =
       
   602         "(NOT(NOT(" +
       
   603         // 2nd & 1st column (='A')
       
   604         KQm2 + ">" + lower2 + " AND " + KQm2 + "<" + upper2 + " AND " +
       
   605         KQm1 + ">" + lower + " AND " + KQm1 + "<" + upper +
       
   606         ") AND NOT(" +
       
   607         // 3nd & 1st column (='B')
       
   608         KQm3 + ">" + lower2 + " AND " + KQm3 + "<" + upper2 + " AND " +
       
   609         KQm1 + ">" + lower + " AND " + KQm1 + "<" + upper +
       
   610         ") AND NOT(" +
       
   611         // 3rd & 2nd column (='C')
       
   612         KQm3 + ">" + lower2 + " AND " + KQm3 + "<" + upper2 + " AND " +
       
   613         KQm2 + ">" + lower + " AND " + KQm2 + "<" + upper +
       
   614         ") AND NOT(" +
       
   615         // 4th & 1st column (='D')
       
   616         KQm4 + ">" + lower2 + " AND " + KQm4 + "<" + upper2 + " AND " +
       
   617         KQm1 + ">" + lower + " AND " + KQm1 + "<" + upper +
       
   618         ") AND NOT(" +
       
   619         // 4th & 2nd column (='E')
       
   620         KQm4 + ">" + lower2 + " AND " + KQm4 + "<" + upper2 + " AND " +
       
   621         KQm2 + ">" + lower + " AND " + KQm2 + "<" + upper +
       
   622         ") AND NOT(" +
       
   623         // 4th & 3rd column (='F')
       
   624         KQm4 + ">" + lower2 + " AND " + KQm4 + "<" + upper2 + " AND " +
       
   625         KQm3 + ">" + lower + " AND " + KQm3 + "<" + upper + ")))";
       
   626         // 5th & 1rd column (='G')
       
   627         KQm5 + ">" + lower2 + " AND " + KQm5 + "<" + upper2 + " AND " +
       
   628         KQm1 + ">" + lower + " AND " + KQm1 + "<" + upper + ")))";
       
   629         // 5th & 2rd column (='H')
       
   630         KQm5 + ">" + lower2 + " AND " + KQm5 + "<" + upper2 + " AND " +
       
   631         KQm2 + ">" + lower + " AND " + KQm2 + "<" + upper + ")))";
       
   632         // 5th & 3rd column (='I')
       
   633         KQm5 + ">" + lower2 + " AND " + KQm5 + "<" + upper2 + " AND " +
       
   634         KQm3 + ">" + lower + " AND " + KQm3 + "<" + upper + ")))";
       
   635         // 5th & 4rd column (='I')
       
   636         KQm5 + ">" + lower2 + " AND " + KQm5 + "<" + upper2 + " AND " +
       
   637         KQm4 + ">" + lower + " AND " + KQm4 + "<" + upper + ")))";
       
   638         // 6th & 1rd column (='J')
       
   639         KQm6 + ">" + lower2 + " AND " + KQm6 + "<" + upper2 + " AND " +
       
   640         KQm1 + ">" + lower + " AND " + KQm1 + "<" + upper + ")))";
       
   641         // 6th & 2rd column (='K')
       
   642         KQm6 + ">" + lower2 + " AND " + KQm6 + "<" + upper2 + " AND " +
       
   643         KQm2 + ">" + lower + " AND " + KQm2 + "<" + upper + ")))";
       
   644         // 6th & 3rd column (='L')
       
   645         KQm6 + ">" + lower2 + " AND " + KQm6 + "<" + upper2 + " AND " +
       
   646         KQm3 + ">" + lower + " AND " + KQm3 + "<" + upper + ")))";
       
   647         // 6th & 43rd column (='M')
       
   648         KQm6 + ">" + lower2 + " AND " + KQm6 + "<" + upper2 + " AND " +
       
   649         KQm4 + ">" + lower + " AND " + KQm4 + "<" + upper + ")))";
       
   650         // 6th & 5rd column (='M')
       
   651         KQm6 + ">" + lower2 + " AND " + KQm6 + "<" + upper2 + " AND " +
       
   652         KQm5 + ">" + lower + " AND " + KQm5 + "<" + upper + ")))";
       
   653         // 7th & 1rd column (='N')
       
   654         KQm7 + ">" + lower2 + " AND " + KQm7 + "<" + upper2 + " AND " +
       
   655         KQm1 + ">" + lower + " AND " + KQm1 + "<" + upper + ")))";
       
   656         // 7th & 1rd column (='O')
       
   657         KQm7 + ">" + lower2 + " AND " + KQm7 + "<" + upper2 + " AND " +
       
   658         KQm2 + ">" + lower + " AND " + KQm2 + "<" + upper + ")))";
       
   659         // 7th & 3rd column (='P')
       
   660         KQm7 + ">" + lower2 + " AND " + KQm7 + "<" + upper2 + " AND " +
       
   661         KQm3 + ">" + lower + " AND " + KQm3 + "<" + upper + ")))";
       
   662         // 7th & 4rd column (='Q')
       
   663         KQm7 + ">" + lower2 + " AND " + KQm7 + "<" + upper2 + " AND " +
       
   664         KQm4 + ">" + lower + " AND " + KQm4 + "<" + upper + ")))";
       
   665         // 7th & 5rd column (='Q')
       
   666         KQm7 + ">" + lower2 + " AND " + KQm7 + "<" + upper2 + " AND " +
       
   667         KQm5 + ">" + lower + " AND " + KQm5 + "<" + upper + ")))";
       
   668         // 7th & 5rd column (='R')
       
   669         KQm7 + ">" + lower2 + " AND " + KQm7 + "<" + upper2 + " AND " +
       
   670         KQm6 + ">" + lower + " AND " + KQm6 + "<" + upper + ")))";
       
   671        
       
   672     return query;
       
   673     }
       
   674 QString CntSqlSearch::ExactMatch(const QString& pattern, QString table) const
       
   675     {
       
   676     QString lower;
       
   677     QString upper;
       
   678     
       
   679     int err = mkeyKeyMap->GetNumericLimits(pattern, lower, upper);
       
   680     if(err)
       
   681         {
       
   682         return QString("");
       
   683         }
       
   684     
       
   685     if (table.length() > 0)
       
   686         {
       
   687         table += ".";
       
   688         }
       
   689 #if defined(USE_DEMORGAN)
       
   690 	// Using De Morgan's law to replace ORs with ANDs:
       
   691 	// column1 || column2 || column3 || column4
       
   692 	// ->
       
   693 	// (NOT(NOT(column1) AND NOT(column2) AND NOT(column3) AND NOT(column4))
       
   694 	//
       
   695 	// Which means:
       
   696 	// (NOT(NOT(N>lower  && < N<upper)  AND NOT(N2>lower && < N2<upper) AND
       
   697 	//      NOT(N3>lower && < N3<upper) AND NOT(N4>lower && < N4<upper))
       
   698 	//
       
   699 	// As KColumn1 is most likely to contain a match, "NOT(KColumn1)" is more
       
   700 	// likely to be false than "NOT(KColumn2)" etc. So put KColumn1 first in the
       
   701 	// AND statement.
       
   702 	return QString("(NOT(NOT(" +
       
   703         table + KColumn1 + ">" + lower + " AND " + table + KColumn1 + "<" + upper + ") AND NOT(" +
       
   704         table + KColumn2 + ">" + lower + " AND " + table + KColumn2 + "<" + upper + ") AND NOT(" +
       
   705         table + KColumn3 + ">" + lower + " AND " + table + KColumn3 + "<" + upper + ") AND NOT(" +
       
   706         table + KColumn4 + ">" + lower + " AND " + table + KColumn4 + "<" + upper + ")))");
       
   707 #else
       
   708 	// Since first column has always some value, and 2nd column is more likely to
       
   709 	// have a value than 3rd column etc. Put columns in that order:
       
   710 	// (column 1 comparison) OR (column 2 comparison) OR (column 3 comparison) OR
       
   711 	// (column 4 comparison)
       
   712 	// If 1st column results true, there is no need to compare other columns etc.
       
   713     return QString("(" +
       
   714         table + KColumn1 + ">" + lower + " AND " + table + KColumn1 + "<" + upper + ") OR (" +
       
   715         table + KColumn2 + ">" + lower + " AND " + table + KColumn2 + "<" + upper + ") OR (" +
       
   716         table + KColumn3 + ">" + lower + " AND " + table + KColumn3 + "<" + upper + ") OR (" +
       
   717         table + KColumn4 + ">" + lower + " AND " + table + KColumn4 + "<" + upper + ")");
       
   718 #endif
       
   719     }
       
   720 
       
   721 QString CntSqlSearch::ExactMatchQwerty(const QString& pattern, QString table) const
       
   722     {
       
   723     QString lower;
       
   724     QString upper;
       
   725     TInt err = mQertyKeyMap->GetNumericLimits(pattern, lower, upper);
       
   726     
       
   727     if (table.length() > 0)
       
   728         {
       
   729         table += ".";
       
   730         }
       
   731 #if defined(USE_DEMORGAN)
       
   732     // Using De Morgan's law to replace ORs with ANDs:
       
   733     // column1 || column2 || column3 || column4
       
   734     // ->
       
   735     // (NOT(NOT(column1) AND NOT(column2) AND NOT(column3) AND NOT(column4))
       
   736     //
       
   737     // Which means:
       
   738     // (NOT(NOT(N>lower  && < N<upper)  AND NOT(N2>lower && < N2<upper) AND
       
   739     //      NOT(N3>lower && < N3<upper) AND NOT(N4>lower && < N4<upper))
       
   740     //
       
   741     // As KColumn1 is most likely to contain a match, "NOT(KColumn1)" is more
       
   742     // likely to be false than "NOT(KColumn2)" etc. So put KColumn1 first in the
       
   743     // AND statement.
       
   744     return QString("(NOT(NOT(" +
       
   745         table + KQm1 + ">" + lower + " AND " + table + KQm1 + "<" + upper + ") AND NOT(" +
       
   746         table + KQm2 + ">" + lower + " AND " + table + KQm2 + "<" + upper + ") AND NOT(" +
       
   747         table + KQm3 + ">" + lower + " AND " + table + KQm3 + "<" + upper + ") AND NOT(" +
       
   748         table + KQm4 + ">" + lower + " AND " + table + KQm4 + "<" + upper + ") AND NOT(" +
       
   749         table + KQm5 + ">" + lower + " AND " + table + KQm5 + "<" + upper + ") AND NOT(" +
       
   750         table + KQm6 + ">" + lower + " AND " + table + KQm6 + "<" + upper + ") AND NOT(" +
       
   751         table + KQm7 + ">" + lower + " AND " + table + KQm7 + "<" + upper + ")))");
       
   752 #else
       
   753     // Since first column has always some value, and 2nd column is more likely to
       
   754     // have a value than 3rd column etc. Put columns in that order:
       
   755     // (column 1 comparison) OR (column 2 comparison) OR (column 3 comparison) OR
       
   756     // (column 4 comparison)
       
   757     // If 1st column results true, there is no need to compare other columns etc.
       
   758     return QString("(" +
       
   759         table + KQm1 + ">" + lower + " AND " + table + KQm1 + "<" + upper + ") OR (" +
       
   760         table + KQm2 + ">" + lower + " AND " + table + KQm2 + "<" + upper + ") OR (" +
       
   761         table + KQm3 + ">" + lower + " AND " + table + KQm3 + "<" + upper + ") OR (" +
       
   762         table + KQm4 + ">" + lower + " AND " + table + KQm4 + "<" + upper + ") OR (" +
       
   763         table + KQm5 + ">" + lower + " AND " + table + KQm5 + "<" + upper + ") OR (" +
       
   764         table + KQm6 + ">" + lower + " AND " + table + KQm6 + "<" + upper + ") OR (" +
       
   765         table + KQm7 + ">" + lower + " AND " + table + KQm7 + "<" + upper + ")");
       
   766 #endif
       
   767     }
       
   768 
       
   769 // TODO: if SEARCH_FROM_ONE_TABLE case is slower than the one that searches from
       
   770 // two tables, then this function is needed for cases where pattern is:
       
   771 // "1023", "12300450"
       
   772 //
       
   773 /* This query makes sub-query into table 5 and searches for a number that begins
       
   774 with 56606. but it does not support cases where both tokens are longer than one digit.
       
   775 
       
   776 SELECT predictivesearch5.contact_id FROM predictivesearch5 WHERE EXISTS (SELECT contact_id FROM predictivesearch5 
       
   777 WHERE (predictivesearch5.nbr>389005014883893248 AND predictivesearch5.nbr<389006114395521023) OR
       
   778 (predictivesearch5.nbr2>389005014883893248 AND predictivesearch5.nbr2<389006114395521023) OR
       
   779 (predictivesearch5.nbr3>389005014883893248 AND predictivesearch5.nbr3<389006114395521023) OR
       
   780 (predictivesearch5.nbr4>389005014883893248 AND predictivesearch5.nbr4<389006114395521023))
       
   781 OR
       
   782 (SELECT predictivesearch5.contact_id FROM predictivesearch5 JOIN predictivesearch6 ON
       
   783 predictivesearch5.contact_id = predictivesearch6.contact_id
       
   784 WHERE(predictivesearch5.nbr>388998417814126592 AND predictivesearch5.nbr<389279892790837247) OR
       
   785 (predictivesearch5.nbr2>388998417814126592 AND predictivesearch5.nbr2<389279892790837247) OR
       
   786 (predictivesearch5.nbr3>388998417814126592 AND predictivesearch5.nbr3<389279892790837247) OR
       
   787 (predictivesearch5.nbr4>388998417814126592 AND predictivesearch5.nbr4<389279892790837247))
       
   788 ORDER BY predictivesearch5.first_name, predictivesearch5.last_name ASC;
       
   789 
       
   790 SELECT contact_id
       
   791 FROM
       
   792 (
       
   793 SELECT predictivesearch5.contact_id, predictivesearch5.first_name, predictivesearch5.last_name  FROM predictivesearch5
       
   794 WHERE (predictivesearch5.nbr>387415121070129152 AND predictivesearch5.nbr<387432713256173567) OR
       
   795 (predictivesearch5.nbr2>387415121070129152 AND predictivesearch5.nbr2<387432713256173567) OR
       
   796 (predictivesearch5.nbr3>387415121070129152 AND predictivesearch5.nbr3<387432713256173567) OR
       
   797 (predictivesearch5.nbr4>387415121070129152 AND predictivesearch5.nbr4<387432713256173567)
       
   798 UNION
       
   799 SELECT predictivesearch5.contact_id, predictivesearch5.first_name, predictivesearch5.last_name  FROM predictivesearch5 JOIN predictivesearch6 ON predictivesearch5.contact_id = predictivesearch6.contact_id
       
   800 WHERE((predictivesearch5.nbr>387309567953862656 AND predictivesearch5.nbr<391813167581233151) OR (predictivesearch5.nbr2>387309567953862656 AND predictivesearch5.nbr2<391813167581233151) OR (predictivesearch5.nbr3>387309567953862656 AND predictivesearch5.nbr3<391813167581233151) OR (predictivesearch5.nbr4>387309567953862656 AND predictivesearch5.nbr4<391813167581233151)
       
   801 AND
       
   802 (predictivesearch6.nbr>387309567953862656 AND predictivesearch6.nbr<391813167581233151) OR (predictivesearch6.nbr2>387309567953862656 AND predictivesearch6.nbr2<391813167581233151) OR (predictivesearch6.nbr3>387309567953862656 AND predictivesearch6.nbr3<391813167581233151) OR (predictivesearch6.nbr4>387309567953862656 AND predictivesearch6.nbr4<391813167581233151))
       
   803 ) AS PR
       
   804 ORDER BY PR.first_name, PR.last_name ASC;
       
   805 
       
   806 Here is a De Morgan version
       
   807 
       
   808 SELECT contact_id
       
   809 FROM
       
   810 (
       
   811 SELECT predictivesearch5.contact_id, predictivesearch5.first_name, predictivesearch5.last_name  FROM predictivesearch5
       
   812 WHERE NOT((NOT (predictivesearch5.nbr >= 387415121070129152 AND predictivesearch5.nbr <= 387432713256173567)) AND (NOT (predictivesearch5.nbr2 >= 387415121070129152 AND predictivesearch5.nbr2 <= 387432713256173567)) AND (NOT (predictivesearch5.nbr3 >= 387415121070129152 AND predictivesearch5.nbr3 <= 387432713256173567)) AND (NOT (predictivesearch5.nbr4 >= 387415121070129152 AND predictivesearch5.nbr4 <= 387432713256173567)))
       
   813 UNION
       
   814 SELECT predictivesearch5.contact_id, predictivesearch5.first_name, predictivesearch5.last_name  FROM predictivesearch5 JOIN predictivesearch6 ON predictivesearch5.contact_id = predictivesearch6.contact_id
       
   815 WHERE NOT((NOT (predictivesearch5.nbr >= 387309567953862656 AND predictivesearch5.nbr <= 391813167581233151)) AND (NOT (predictivesearch5.nbr2 >= 387309567953862656 AND predictivesearch5.nbr2 <= 391813167581233151)) AND (NOT (predictivesearch5.nbr3 >= 387309567953862656 AND predictivesearch5.nbr3 <= 391813167581233151)) AND (NOT (predictivesearch5.nbr4 >= 387309567953862656 AND predictivesearch5.nbr4 <= 391813167581233151)))
       
   816 AND
       
   817 NOT((NOT (predictivesearch6.nbr >= 387309567953862656 AND predictivesearch6.nbr <= 391813167581233151)) AND (NOT (predictivesearch6.nbr2 >= 387309567953862656 AND predictivesearch6.nbr2 <= 391813167581233151)) AND (NOT (predictivesearch6.nbr3 >= 387309567953862656 AND predictivesearch6.nbr3 <= 391813167581233151)) AND (NOT (predictivesearch6.nbr4 >= 387309567953862656 AND predictivesearch6.nbr4 <= 391813167581233151)))
       
   818 ) AS PR
       
   819 ORDER BY PR.first_name, PR.last_name ASC;
       
   820 
       
   821 */
       
   822 QString CntSqlSearch::CreateJoinTableSearch(QString pattern, QStringList numbers) const
       
   823 	{
       
   824 	// It has been checked earlier that tables are not same
       
   825 	QString firstTable = SelectTable(numbers.at(0));
       
   826 	QString secondTable = SelectTable(numbers.at(1));
       
   827 
       
   828     QString queryString = QString("SELECT contact_id FROM (SELECT "
       
   829                                   + firstTable + ".contact_id, " + firstTable + ".first_name, " + firstTable + ".last_name FROM " + firstTable +
       
   830                                     " WHERE " + ExactMatch(pattern, firstTable) +
       
   831                                     " UNION SELECT " + firstTable + ".contact_id, " + firstTable + ".first_name, " + firstTable + ".last_name FROM " + firstTable + " JOIN " + secondTable + " ON " + firstTable + ".contact_id = " + secondTable +  ".contact_id WHERE" +
       
   832                                 ExactMatchColumns(numbers) + ") AS PR ORDER BY PR.first_name, PR.last_name ASC;");
       
   833 	return queryString;
       
   834 	}
       
   835 
       
   836 QString CntSqlSearch::ExactMatchColumns(QStringList numbers) const
       
   837     {
       
   838     const int KFirstColumn = 0;
       
   839     const int KSecondColumn = 1;
       
   840     QString firstColumn = numbers.at(KFirstColumn);
       
   841     QString secondColumn = numbers.at(KSecondColumn);
       
   842 
       
   843     if( firstColumn.count() >  1 && secondColumn.count() > 1)
       
   844         {
       
   845         return "(" + ExactMatch(numbers.at(KFirstColumn), SelectTable(numbers.at(KFirstColumn)))
       
   846             + " AND " +
       
   847             ExactMatch(numbers.at(KSecondColumn), SelectTable(numbers.at(KSecondColumn))) + ")";
       
   848         }
       
   849     else if(firstColumn.count() > 1)
       
   850         {
       
   851         return ExactMatch(numbers.at(KFirstColumn), SelectTable(numbers.at(KFirstColumn)));
       
   852         }
       
   853     else
       
   854         {
       
   855         return ExactMatch(numbers.at(KSecondColumn), SelectTable(numbers.at(KSecondColumn)));
       
   856         }
       
   857     }
       
   858 
       
   859 QString CntSqlSearch::Order(QStringList tokens, QueryType queryType) const
       
   860 	{
       
   861     QString table;
       
   862 	if (tokens.count() > 1 )
       
   863 		{
       
   864         if(queryType == CntSqlSearch::QwertyEmail)
       
   865             {
       
   866             table = selectQweryTable(tokens.at(0));
       
   867             }
       
   868         else
       
   869             {
       
   870             table = SelectTable(tokens.at(0));
       
   871             }
       
   872         return QString(" ORDER BY " + table + ".first_name, " + table + ".last_name ASC;");
       
   873 		}
       
   874 	return QString(ORDER_BY_FIRSTNAME_LASTNAME);
       
   875 	}
       
   876 
       
   877 QString CntSqlSearch::ChangeStringPadings( const QString &pattern ) const
       
   878     { 
       
   879     QString newPattern = pattern;
       
   880     if (QLocale::system().language() == QLocale::Thai)
       
   881         {
       
   882         newPattern.remove(KStarChar, Qt::CaseInsensitive);
       
   883         newPattern.remove(KPlusChar, Qt::CaseInsensitive);
       
   884         newPattern.remove(KPChar, Qt::CaseInsensitive);
       
   885         newPattern.remove(KWChar, Qt::CaseInsensitive);
       
   886         newPattern.remove(KHashChar, Qt::CaseInsensitive);
       
   887         }
       
   888     else
       
   889         { 
       
   890         newPattern.replace(KStarChar, 'A');
       
   891         newPattern.replace(KPlusChar, 'A');
       
   892         newPattern.replace(KPChar, 'A');
       
   893         newPattern.replace(KWChar, 'A');
       
   894         newPattern.replace(KHashChar, 'B');
       
   895         }
       
   896     return newPattern;
       
   897     }
       
   898 
       
   899 bool CntSqlSearch::TestPattern( const QString &pattern, SearchMethod searchMethod ) const
       
   900     {
       
   901     QStringList tokens = GetTokens(pattern);
       
   902     if (!tokens.isEmpty() && !pattern.isEmpty())
       
   903         {
       
   904         if (CntSqlSearch::ZerosEndOfFirstToken == searchMethod)
       
   905             {
       
   906             if( tokens.count() == KOneToken && !tokens.at(0).contains("0")
       
   907                 && !pattern.startsWith('0') && pattern.count('0') == 1
       
   908                 && pattern.endsWith('0'))
       
   909                 {
       
   910                 return true;
       
   911                 }
       
   912             }
       
   913         if (CntSqlSearch::ZeroIsFirstNumber == searchMethod )
       
   914             {
       
   915             if(pattern.startsWith('0') && pattern.count() > 1 
       
   916                 && pattern.at(1) != '0')
       
   917                 {
       
   918                 return true;
       
   919                 }
       
   920             }
       
   921         }
       
   922     return false;
       
   923     }
       
   924 
       
   925 QString CntSqlSearch::Pad( const QString &pattern, char padChar ) const
       
   926     {
       
   927     int padCount = KLimitLength - pattern.length();    
       
   928     QString result;
       
   929     if ( padCount < 0 )
       
   930         {
       
   931         result = pattern.left(KLimitLength);
       
   932         }
       
   933     else
       
   934         {
       
   935         result = pattern;
       
   936         for( int i = 0; i < padCount ;i++ )
       
   937             {
       
   938             result.append(padChar);
       
   939             }
       
   940         }
       
   941     bool ok;
       
   942     // Use signed int to prevent underflow when replaced is "00...00"
       
   943     qint64 value = result.toLongLong(&ok, KHexadecimalBase); 
       
   944     if (!ok)
       
   945     	{
       
   946     	// TODO: handle error (=invalid characters in pattern)
       
   947     	}
       
   948 
       
   949 	// In order to write queries using '>' and '<' instead of '>=' and '<=',
       
   950 	// expand the limit by one.
       
   951 	if (padChar == KUpperLimitPadding)
       
   952 		{
       
   953 		++value;
       
   954 		}
       
   955 	else
       
   956 		{
       
   957 		--value;
       
   958 		}
       
   959 
       
   960     return QString::number(value, 10);
       
   961     }
       
   962 
       
   963 bool CntSqlSearch::isQwerty(const QString &pattern)
       
   964     {
       
   965     QChar rs(30);
       
   966     int rs_index = pattern.indexOf(rs);  
       
   967     int qwerty_index = pattern.indexOf("vqwerty");
       
   968     if(rs_index >= 0 && qwerty_index >= 0 )
       
   969         {
       
   970         if(rs_index + 1 == qwerty_index)
       
   971             {
       
   972             return true;
       
   973             }
       
   974         return false;
       
   975         }
       
   976     return false;
       
   977     }
       
   978 QStringList CntSqlSearch::qwertyTokens(const QString &pattern) const
       
   979     {
       
   980     QString decodePatern;
       
   981     QString keymapsString = mQertyKeyMap->GetMappedString(pattern);
       
   982     int index = pattern.indexOf(30);
       
   983     if(index > 0 )
       
   984         {
       
   985         QString decodePatern = keymapsString.left(index);
       
   986         return decodePatern.split(32, QString::SkipEmptyParts);
       
   987         }
       
   988     else
       
   989         {
       
   990         return QStringList("");
       
   991         }
       
   992     }
       
   993 QString CntSqlSearch::UpperLimit( const QString &pattern ) const
       
   994     {
       
   995     return Pad( pattern, KUpperLimitPadding );
       
   996     }
       
   997 
       
   998 QString CntSqlSearch::LowerLimit( const QString &pattern ) const
       
   999     {
       
  1000     return Pad( pattern, KLowerLimitPadding );
       
  1001     }