/**
GetListForItemL has a dual nature. If aIsGroup is ETrue, a list of contact items belonging to
specified group is returned. Otherwise a list of group ids to which contact id belongs is returned.

@param aItemId contact item id
@param aIsGroup ETrue if the method will fill a group.
*/
CContactIdArray* CPplGroupsTable::GetListForItemL(TContactItemId aItemId, TBool aIsGroup)
	{
	/*
	// Check if group membership information was not requested or if the item
	// is not derived from CContactItemPlusGroup.
	if (!(aType == KUidContactGroup	  || aType == KUidContactCard ||
		  aType == KUidContactOwnCard || aType == KUidContactICCEntry) )
		{
		return NULL; 
		}
	*/	

	// build the RSqlStatement
	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
	TInt idIndex;

	// build the CCntSqlStatement statement
	const TInt KWhereParamIndex(KFirstIndex); // only one parameter in the query
	if (aIsGroup)
		{
		// group -> select members
		stmnt.PrepareL(iDatabase, iSelectMembersStmnt->SqlStringL() );
		User::LeaveIfError(stmnt.BindInt(KWhereParamIndex, aItemId ) );
		idIndex = stmnt.ColumnIndex(KGroupContactGroupMemberId() );
		}
	else
		{
		// member -> select groups
		stmnt.PrepareL(iDatabase, iSelectGroupsStmnt->SqlStringL() );
		User::LeaveIfError(stmnt.BindInt(KWhereParamIndex, aItemId ) );
		idIndex = stmnt.ColumnIndex(KGroupContactGroupId() );
		}
	User::LeaveIfError(idIndex);
	// fetch the list of any matching ids
	CContactIdArray* items = CContactIdArray::NewLC();
	TInt err(KErrNone);
	while ((err = stmnt.Next() ) == KSqlAtRow)
		{
		items->AddL(stmnt.ColumnInt(idIndex) );
		}

	// leave if we didn't complete going through the results properly
	if(err != KSqlAtEnd)
		{
		User::Leave(err);
		}
	
	CleanupStack::Pop(items);
	CleanupStack::PopAndDestroy(&stmnt);
	return items;
	}
/**
Searches the contacts database to find any contact items with an exact match on the address supplied.

@param aCommAddr A descriptor containing the address to be found in the database.
@param aAddrType The type of addresses that is being sought.
@return An array of contact IDs which match the supplied address.
*/
CContactIdArray* CPplCommAddrTable::MatchNonPhoneAddrL(const TDesC& aCommAddr, TCommAddrType aAddrType)
	{

	// build statement
	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
	stmnt.PrepareL(iDatabase, iMatchSelectStmnt->SqlStringL() );

	const TInt KValueParamIndex(KFirstParam);					// first parameter in query...
	const TInt KTypeParamIndex(KValueParamIndex + 1);	// ...and the second.
	User::LeaveIfError(stmnt.BindText(KValueParamIndex, aCommAddr) );
	User::LeaveIfError(stmnt.BindInt(KTypeParamIndex, aAddrType) );

	// fetch the list of any matching contact ids
	CContactIdArray* idArray = CContactIdArray::NewLC();
	TInt err(KErrNone);
	const TInt KContactIdIdx(iMatchSelectStmnt->ParameterIndex(KCommAddrContactId() ) );
	while ((err = stmnt.Next() ) == KSqlAtRow)
		{
		idArray->AddL(stmnt.ColumnInt(KContactIdIdx) );
		}

	// leave if we didn't complete going through the results properly
	if(err != KSqlAtEnd)
		{
		User::Leave(err);
		}

	CleanupStack::Pop(idArray);
	CleanupStack::PopAndDestroy(&stmnt);

	return idArray;
	}
/**
Deletes all the communication addresses for a particular contact item. Should be used when
deleting a contact item from the database altogether.

@param aItem The contact item whose communcation addresses are to be deleted.
*/
void CPplCommAddrTable::DeleteL(const CContactItem& aItem, TBool& aLowDiskErrorOccurred)
	{
	const TUid KType = aItem.Type();
    if (KType != KUidContactCard && KType != KUidContactOwnCard && KType != KUidContactICCEntry && KType != KUidContactGroup)
		{
		return;
		}

	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
	stmnt.PrepareL(iDatabase, iAllForItemDeleteStmnt->SqlStringL() );
	const TInt KContactIdParamIndex(KFirstIndex); // first and only parameter in query
	User::LeaveIfError(stmnt.BindInt(KContactIdParamIndex, aItem.Id() ) );
	TInt err = stmnt.Exec();
	CleanupStack::PopAndDestroy(&stmnt);

	if (err == KErrDiskFull)
		{
		aLowDiskErrorOccurred = ETrue;
		}
	else
		{
		User::LeaveIfError(err);
		}
	}
TBool CPredictiveSearchSynchronizer::CheckIfPredSearchTableExistsL(
	const TDesC& aTableName) const
	{
	PRINT1(_L("CPredictiveSearchSynchronizer::CheckIfPredSearchTableExistsL table='%S'"),
		   &aTableName);

	_LIT(KSelectFirstTableFormat,
		 "SELECT name FROM sqlite_master WHERE type='table' AND name='%S';");

	TInt bufSize = KSelectFirstTableFormat().Length() +
				   aTableName.Length();
	HBufC* select = HBufC::NewLC(bufSize);
	select->Des().AppendFormat(KSelectFirstTableFormat, &aTableName);
	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
    stmnt.PrepareL(iDatabase, *select);

	TBool tableExists = (stmnt.Next() == KSqlAtRow);

	CleanupStack::PopAndDestroy(&stmnt);
	CleanupStack::PopAndDestroy(select);

	PRINT1(_L("CPredictiveSearchSynchronizer::CheckIfPredSearchTablesExistL return %d"),
		   tableExists);
	return tableExists;
	}
TBool CPredictiveSearchSynchronizer::ReadMailAddressesL(CContactItem& aContact)
	{
	PRINT(_L("CPredictiveSearchSynchronizer::ReadMailAddressesL"));

	// SELECT value FROM comm_addr
	//	 WHERE contact_id = [contact id value] AND type = [type value];
    _LIT(KSelectMailAddrFormat, "SELECT %S FROM %S WHERE %S = %d AND %S = %d;");
	const TInt KContactIdLength = 10;
	const TInt KCommAddrTypeLength = 2; // CPplCommAddrTable::EEmailAddress is enum
	TInt bufSize = KSelectMailAddrFormat().Length() +
				   KCommAddrValue().Length() +
				   KSqlContactCommAddrTableName().Length() +
				   KCommAddrContactId().Length() +
				   KContactIdLength +
				   KCommAddrType().Length() +
				   KCommAddrTypeLength;
	HBufC* sqlStatement = HBufC::NewLC(bufSize);
	sqlStatement->Des().AppendFormat(KSelectMailAddrFormat,
		&KCommAddrValue,
		&KSqlContactCommAddrTableName,
		&KCommAddrContactId,
		aContact.Id(),
		&KCommAddrType,
		CPplCommAddrTable::EEmailAddress);

	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
	PRINT1(_L("prepare SQL statement:%S"), sqlStatement);
    stmnt.PrepareL(iDatabase, *sqlStatement);

	const TInt KValueIndex = 0;
	TBool foundMailAddress(EFalse);
	TInt err(KErrNone);
    while ((err = stmnt.Next()) == KSqlAtRow)
        {
		TPtrC value;
		if (stmnt.ColumnText(KValueIndex, value) == KErrNone)
			{
			PRINT2(_L("  id=%d, found mail address=%S"), aContact.Id(), &value);
			CContactItemField* field =
				CContactItemField::NewLC(KStorageTypeText, KUidContactFieldEMail);
			CContactTextField* textfield = field->TextStorage();
			textfield->SetTextL(value);
			aContact.AddFieldL(*field); // Takes ownership
			CleanupStack::Pop(field);
			foundMailAddress = ETrue;
			}
        }

    if (err != KSqlAtEnd)
        {
		PRINT1(_L("CPredictiveSearchSynchronizer::ReadMailAddressesL SQL err=%d"), err);
        User::Leave(err);
        }
    CleanupStack::PopAndDestroy(&stmnt);
	CleanupStack::PopAndDestroy(sqlStatement);
	PRINT1(_L("CPredictiveSearchSynchronizer::ReadMailAddressesL return %d"), foundMailAddress);
	return foundMailAddress;
	}
/**
Create view contact object based on given sql statement.

@return CViewContact object or NULL the contact is not found.
*/
CViewContact* CCntPplViewSession::CreateViewItemL(RSqlStatement& aSqlStmt, const CCntSqlStatement& aCntSqlStmt, const TContactViewPreferences& aViewPrefs)
	{
	if (! iContactsFile.IsOpened())
		{
		User::Leave(KErrInUse);
		}
	
	CViewContact* viewContact = InitialiseViewItemL(aSqlStmt, aCntSqlStmt, aViewPrefs);
	if(!viewContact)
		{
		//Couldn't find the contact.
		return NULL;
		}
	
	CleanupStack::PushL(viewContact);
	if(viewContact->ContactType() == CViewContact::EContactItem)
		{
		FillViewItemL(*viewContact, aSqlStmt, aViewPrefs);
		}
	
	TUid typeUid = GetContactFieldMatchUid(*viewContact, aViewPrefs);	
	if(typeUid != KUidContactFieldNone)
		{
		RSqlStatement contactSqlStmt;
		CleanupClosePushL(contactSqlStmt);
		
		/* we have to reload the row from SQL database to cache in text header and text blob */ 	
		contactSqlStmt.PrepareL(iContactsFile.NamedDatabase(),  iSqlSmtSelectAllFieldsById.SqlStringL());
		User::LeaveIfError(contactSqlStmt.BindInt(KFirstIndex, viewContact->Id())); //Bind item id into the condition.	
		
		TInt err = contactSqlStmt.Next();
		if(err == KSqlAtEnd)
			{
			//Should never be here, we found it in InitialiseViewItemL 
			//but couldn't find the same contact in same database.
			CleanupStack::PopAndDestroy(viewContact);
			return NULL;
			}			
			
		User::LeaveIfError(err);
		
		/* set first field with possible content for group or unsorted contact */	
		CContactDatabase::TTextFieldMinimal buf;
		TextFieldL(contactSqlStmt, iSqlSmtSelectAllFieldsById, iContactProperties.SystemTemplateL(), typeUid, buf);
		viewContact->SetFirstFieldForBlankContactL(buf);
		CleanupStack::PopAndDestroy(&contactSqlStmt);
		} //if(typeUid != 0)
	
	CleanupStack::Pop(viewContact);
	return viewContact;
	}
/**
Deletes individual communication addresses from the database. In other words, deletes at
the sub-contact item level rather than deleting everything with a specific contact item ID.
*/
void CPplCommAddrTable::DeleteSingleCommAddrL(TInt aCommAddrId, TBool& aLowDiskErrorOccurred)
	{
	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
	stmnt.PrepareL(iDatabase, iSingleDeleteStmnt->SqlStringL() );
	const TInt KCommAddrIdParamIndex(KFirstIndex); // first and only parameter in the query
	User::LeaveIfError(stmnt.BindInt(KCommAddrIdParamIndex, aCommAddrId ) );
	TInt err = stmnt.Exec();
	CleanupStack::PopAndDestroy(&stmnt);

	if (err == KErrDiskFull)
		{
		aLowDiskErrorOccurred = ETrue;
		}
	else
		{
		User::LeaveIfError(err);
		}
	}
/**
Deletes information about group for the passed contact item id

@param aItemId contact item id
@param aLowDiskErrorOccurred out parameter; will be set to ETrue if there was a deletion in 
		low disk condition
*/
void CPplGroupsTable::DeleteItemL(TContactItemId aItemId, TBool& aLowDiskErrorOccurred)
	{
	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
	stmnt.PrepareL(iDatabase, iDeleteStmnt->SqlStringL() );

	const TInt KGroupIdIndex(KFirstIndex); 			// first parameter in query...	
	const TInt KMemberIdIndex(KGroupIdIndex + 1); 	// ...and the second parameter
	User::LeaveIfError(stmnt.BindInt(KGroupIdIndex, aItemId) );
	User::LeaveIfError(stmnt.BindInt(KMemberIdIndex, aItemId) );
	TInt err = stmnt.Exec();
	CleanupStack::PopAndDestroy(&stmnt);

	if (err == KErrDiskFull)
		{
		aLowDiskErrorOccurred = ETrue;
		}
	else
		{
		User::LeaveIfError(err);
		}
	}
/**
Returns an array of contact item IDs for all the contact items which may contain
the specified telephone number in a telephone, fax or SMS type field.

This is improved version of MatchPhoneNumberL method.
The number is compared starting from the right side of the number and 
the method returns an array of candidate matches.  
Punctuation (e.g. spaces) and other alphabetic characters are ignored
when comparing. Leading zeros are removed. Digits are compared up to 
the lenght of shorter number.

@param aNumber Phone number string.
@return Array of contact IDs which are candidate matches.
*/
CContactIdArray* CPplCommAddrTable::BestMatchingPhoneNumberL(const TDesC& aNumber)
    {
    const TInt KUpperMaxLength = KMaxPhoneMatchLength - KLowerSevenDigits;

    CContactIdArray* phoneMatchArray = CContactIdArray::NewLC();

    TMatch phoneDigits = CreatePaddedPhoneDigitsL(aNumber, KLowerSevenDigits, KUpperMaxLength);

    if (phoneDigits.iNumLowerDigits + phoneDigits.iNumUpperDigits > 0)
        {
        // build statement
        RSqlStatement stmnt;
        CleanupClosePushL(stmnt);
        stmnt.PrepareL(iDatabase, iMatchSelectStmnt->SqlStringL());

        const TInt KValueParamIndex(KFirstParam); // first parameter in query...
        const TInt KTypeParamIndex(KValueParamIndex + 1); // ...and the second.

        User::LeaveIfError(stmnt.BindInt(KValueParamIndex,
                phoneDigits.iLowerSevenDigits));
        User::LeaveIfError(stmnt.BindInt(KTypeParamIndex, EPhoneNumber));

        // fetch the list of any matching contact ids
        TInt err(KErrNone);
        const TInt KContactIdIdx(iMatchSelectStmnt->ParameterIndex( KCommAddrContactId()));
        const TInt KExtraValueIdx(iMatchSelectStmnt->ParameterIndex(KCommAddrExtraValue()));
        while ((err = stmnt.Next()) == KSqlAtRow)
            {
            // Check the upper digits...
            TInt32 number = phoneDigits.iUpperDigits;
            TPtrC extValString = stmnt.ColumnTextL(KExtraValueIdx);
            TInt32 storedUpperDigits;
            User::LeaveIfError(TLex(extValString).Val(storedUpperDigits));
            TInt32 stored = storedUpperDigits;

            TBool nonZeroInStoredFound = EFalse;
            TBool nonZeroInNumberFound = EFalse;
            while ((number != 0) && (stored != 0))
                {
                nonZeroInNumberFound |= (number % 10 != 0);
                nonZeroInStoredFound |= (stored % 10 != 0);
                if (nonZeroInStoredFound && nonZeroInNumberFound)
                    {
                    break;
                    }
                number /= 10;
                stored /= 10;
                }

            if ( (phoneDigits.iUpperDigits == 0) || (storedUpperDigits == 0) ||
                 (number == stored) )
                {
                phoneMatchArray->AddL(stmnt.ColumnInt(KContactIdIdx));
                }
            }

        // leave if we didn't complete going through the results properly
        if (err != KSqlAtEnd)
            {
            User::Leave(err);
            }
        CleanupStack::PopAndDestroy(&stmnt);
        }

    CleanupStack::Pop(phoneMatchArray);
    return phoneMatchArray;
    }
/**
Returns an array of contact item IDs for all the contact items which may contain
the specified telephone number in a telephone, fax or SMS type field.

The comparison method used is not exact.  The number is compared starting from
the right side of the number and the method returns an array of candidate
matches.  Punctuation (e.g. spaces) and other alphabetic characters are ignored
when comparing.

Additionally, if the Contacts Model Phone Parser (CNTPHONE.DLL) is available,
then any DTMF digits are also excluded from the comparision.

Note that due to the way numbers are stored in the database, it is recommended
that at least 7 match digits are specified even when matching a number
containing fewer digits.  Failure to follow this guideline may (depending on the
database contents) mean that the function will not return the expected Contact
IDs.

@param aNumber Phone number string.
@param aMatchLengthFromRight Number of digits from the right of the phone number
to use.  Up to 15 digits can be specified, and it is recommended that at least 7
match digits are specified.
@param aDatabase The database.

@return Array of contact IDs which are candidate matches.
*/
CContactIdArray* CPplCommAddrTable::MatchPhoneNumberL(const TDesC& aNumber, const TInt aMatchLengthFromRight)
	{
	CContactIdArray* phoneMatchArray = CContactIdArray::NewLC();

	TInt numLowerDigits = aMatchLengthFromRight;
	TInt numUpperDigits = 0;

	if(numLowerDigits > KLowerSevenDigits)
		{
		// New style matching.
		numLowerDigits = KLowerSevenDigits;
		numUpperDigits = aMatchLengthFromRight - KLowerSevenDigits;
		}

	TMatch phoneDigits = CreatePaddedPhoneDigitsL(aNumber, numLowerDigits, numUpperDigits);

	if (phoneDigits.iNumLowerDigits + phoneDigits.iNumUpperDigits > 0)
		{
		// build statement
		RSqlStatement stmnt;
		CleanupClosePushL(stmnt);
		stmnt.PrepareL(iDatabase, iMatchSelectStmnt->SqlStringL() );

		const TInt KValueParamIndex(KFirstParam);			// first parameter in query...
		const TInt KTypeParamIndex(KValueParamIndex + 1);	// ...and the second.

    	User::LeaveIfError(stmnt.BindInt(KValueParamIndex, phoneDigits.iLowerSevenDigits ));
    	User::LeaveIfError(stmnt.BindInt(KTypeParamIndex, EPhoneNumber ));

		// fetch the list of any matching contact ids
		TInt err(KErrNone);
		const TInt KContactIdIdx(iMatchSelectStmnt->ParameterIndex(KCommAddrContactId() ) );
		const TInt KExtraValueIdx(iMatchSelectStmnt->ParameterIndex(KCommAddrExtraValue() ) );
		while ((err = stmnt.Next() ) == KSqlAtRow)
			{
			if (aMatchLengthFromRight <= KLowerSevenDigits)
				{
				// Matching 7 or less digits...we've already matched.
				phoneMatchArray->AddL(stmnt.ColumnInt(KContactIdIdx) );
				}
			else
				{
				// Check the upper digits...
				TInt32 storedUpperDigits(0);
				TPtrC extValString = stmnt.ColumnTextL(KExtraValueIdx);
				User::LeaveIfError(TLex(extValString).Val(storedUpperDigits) );

				const TInt KDigitsToRemove = KMaxPhoneMatchLength - KLowerSevenDigits - phoneDigits.iNumUpperDigits;
				for(TInt i = 0; i < KDigitsToRemove; ++i)
					{
					// repeatedly divide by 10 to lop off the appropriate number of digits from the right
					storedUpperDigits /= 10;
					}

				storedUpperDigits = TMatch::PadOutPhoneMatchNumber(storedUpperDigits, KDigitsToRemove);

				if (phoneDigits.iUpperDigits == storedUpperDigits)
					{
					phoneMatchArray->AddL(stmnt.ColumnInt(KContactIdIdx) );
					}
				}
			}

		// leave if we didn't complete going through the results properly
		if(err != KSqlAtEnd)
			{
			User::Leave(err);
			}
		CleanupStack::PopAndDestroy(&stmnt);
		}

	CleanupStack::Pop(phoneMatchArray);
	return phoneMatchArray;
	}
/**
Removes comm addresses from the 3 lists that are already in the database and have been updated.
It takes the 3 lists in as parameters and modifies them accordingly. It also populates the list
of comm address ids that are free to be recycled during updating.
*/
void CPplCommAddrTable::RemoveNonUpdatedAddrsL(RArray<TMatch>& aNewPhones, RArray<TPtrC>& aNewEmails, RArray<TPtrC>& aNewSips,
					  RArray<TInt>& aFreeCommAddrIds, const TInt aItemId,CPplCommAddrTable::TCommAddrExtraInfoType 
                      aExtraInfoType )
	{
	// build the RSqlStatement
	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
	stmnt.PrepareL(iDatabase, iWholeSelectStmnt->SqlStringL() );
	const TInt KContactIdParamIndex(KFirstIndex); // first and only parameter in the query
	User::LeaveIfError(stmnt.BindInt(KContactIdParamIndex, aItemId) ) ;

	// fetch the results from the query and compare them with the new comm_addrs we have
	TInt err(KErrNone);
	while ((err = stmnt.Next() ) == KSqlAtRow)
		{
		const TInt KType(stmnt.ColumnInt(iWholeSelectStmnt->ParameterIndex(KCommAddrType() ) ) );
		if (KType == EPhoneNumber)
			{
			TMatch phoneNumber;
			TPtrC valString    = stmnt.ColumnTextL(iWholeSelectStmnt->ParameterIndex(KCommAddrValue() ) );
			TPtrC extValString = stmnt.ColumnTextL(iWholeSelectStmnt->ParameterIndex(KCommAddrExtraValue() ) );
			TInt extTypeInfoString = stmnt.ColumnInt(iWholeSelectStmnt->ParameterIndex(KCommAddrExtraTypeInfo() ) );
			User::LeaveIfError(TLex(valString).Val(phoneNumber.iLowerSevenDigits) );
			User::LeaveIfError(TLex(extValString).Val(phoneNumber.iUpperDigits) );

			TInt matchIndex(aNewPhones.Find(phoneNumber, TIdentityRelation<TMatch>(&TMatch::Equals) ) );
			// remove any phone numbers from the new list if we already
			// have them in the db and they haven't changed...
			if (matchIndex != KErrNotFound  && (extTypeInfoString == aExtraInfoType))
				{
				aNewPhones.Remove(matchIndex);
				}
			// ...and add any spare ids to the recycle list
			else
				{
				aFreeCommAddrIds.AppendL(
					stmnt.ColumnInt(iWholeSelectStmnt->ParameterIndex(KCommAddrId() ) ) );
				}
			}
		else // is Email or SIP
			{
			TPtrC valString = stmnt.ColumnTextL(iWholeSelectStmnt->ParameterIndex(KCommAddrValue() ) );
			TInt matchIndex(0);

			// remove any email and sip addresses from the new list if
			// we already have them in the db and they haven't changed...
			if (KType == EEmailAddress)
				{
				matchIndex = aNewEmails.Find(valString);
				if (matchIndex != KErrNotFound)
					{
					aNewEmails.Remove(matchIndex);
					}
				}
			else // SIP
				{
				matchIndex = aNewSips.Find(valString);
				if (matchIndex != KErrNotFound)
					{
					aNewSips.Remove(matchIndex);
					}
				}

			// ...and add any spare ids to the recycle list
			if (matchIndex == KErrNotFound)
				{
				aFreeCommAddrIds.AppendL(
					stmnt.ColumnInt(iWholeSelectStmnt->ParameterIndex(KCommAddrId() ) ) );
				}
			}
		}
	// leave if we didn't complete going through the results properly
	if(err != KSqlAtEnd)
		{
		User::Leave(err);
		}
	CleanupStack::PopAndDestroy(&stmnt);
	}
void CPredictiveSearchSynchronizer::CreatePredSearchTablesL(TBool aAllTables)
	{
	PRINT1(_L("CPredictiveSearchSynchronizer::CreatePredSearchTablesL all=%d"), aAllTables);

	if (aAllTables)
		{
		i12keyTable.CreateTableL();
		iSettingsTable.CreateTableL();
		iSettingsTable.StoreCurrentLanguageL();
		}
	iQwertyTable.CreateTableL();

	_LIT(KSelectAllContactsFormat, "SELECT %S,%S,%S FROM %S;");
	TInt bufSize = KSelectAllContactsFormat().Length() +
				   KContactId().Length() +
				   KContactFirstName().Length() +
				   KContactLastName().Length() +
				   KSqlContactTableName().Length();
	HBufC* sqlStatement = HBufC::NewLC(bufSize);
	sqlStatement->Des().AppendFormat(KSelectAllContactsFormat,
		&KContactId,
		&KContactFirstName,
		&KContactLastName,
		&KSqlContactTableName);

	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
	PRINT1(_L("CreatePredSearchTablesL prepare SQL statement:%S"), sqlStatement);
    stmnt.PrepareL(iDatabase, *sqlStatement);

	const TInt KContactIdIndex = 0;
	const TInt KFirstNameIndex = 1;
	const TInt KLastNameIndex = 2;
	TInt err(KErrNone);
    while ((err = stmnt.Next()) == KSqlAtRow)
        {
		PRINT(_L("CreatePredSearchTablesL create CContactItem"));

		TInt id = KUidContactCardValue;
		TUid uid;
		uid.iUid = id;
		CContactItem* contact = CContactItem::NewLC(uid);
		contact->SetId(stmnt.ColumnInt(KContactIdIndex));

		// If first name exists, write it to contact item
		TPtrC firstName;
		if (stmnt.ColumnText(KFirstNameIndex, firstName) == KErrNone)
			{
			CContactItemField* field =
				CContactItemField::NewLC(KStorageTypeText, KUidContactFieldGivenName);
			CContactTextField* textfield = field->TextStorage();
			textfield->SetTextL(firstName);
			contact->AddFieldL(*field); // Takes ownership
			CleanupStack::Pop(field);
			}

		TPtrC lastName;
		if (stmnt.ColumnText(KLastNameIndex, lastName) == KErrNone)
			{
			CContactItemField* field =
				CContactItemField::NewLC(KStorageTypeText, KUidContactFieldFamilyName);
			CContactTextField* textfield = field->TextStorage();
			textfield->SetTextL(lastName);
			contact->AddFieldL(*field); // Takes ownership
			CleanupStack::Pop(field);
			}
		PRINT(_L("CreatePredSearchTablesL create entry to tables"));
		if (aAllTables)
			{
			i12keyTable.CreateInDbL(*contact);
			}
		if (ReadMailAddressesL(*contact))
			{
			iQwertyTable.CreateInDbL(*contact);
			}

		CleanupStack::PopAndDestroy(contact);
        }

    // Leave if we didn't complete going through the results properly
    if (err != KSqlAtEnd)
        {
		PRINT1(_L("CreatePredSearchTablesL SQL err=%d"), err);
        User::Leave(err);
        }
    CleanupStack::PopAndDestroy(&stmnt);
	CleanupStack::PopAndDestroy(sqlStatement);

	PRINT(_L("CPredictiveSearchSynchronizer::CreatePredSearchTablesL ends"));
	}
/**
Persist the items belonging to curent group into group table

@param aGroup referece to a contact group
*/
void CPplGroupsTable::WriteGroupMembersL(const CContactItem& aGroup)
	{
	if (aGroup.Type() != KUidContactGroup)
		{
		return;
		}

	const TContactItemId KGroupId(aGroup.Id() );

	// make sure we clear out any previous, out-of-date data
	TBool lowDiskErr(EFalse);
	DeleteItemL(KGroupId, lowDiskErr);
	if (lowDiskErr)
		{
		User::Leave(KErrDiskFull);
		}

	// build the RSqlStatement
	RSqlStatement stmnt;
	CleanupClosePushL(stmnt);
	stmnt.PrepareL(iDatabase, iInsertStmnt->SqlStringL() );
	const TInt KGroupIdIndex(KFirstIndex); 			// first parameter in query...	
	const TInt KMemberIdIndex(KGroupIdIndex + 1); 	// ...and the second parameter
	
	// copy and sort the member id array so we can see if there are duplicates
	const CContactIdArray* contactIdArray = static_cast<const CContactGroup&>(aGroup).ItemsContained();  //does not take the ownership
	
	const TInt arrayCount = contactIdArray->Count();
	CArrayFixFlat<TContactItemId>* sortedList = new(ELeave) CArrayFixFlat<TContactItemId>(KArrayGranularity);	
	CleanupStack::PushL(sortedList);
    for(TInt loop = 0;loop < arrayCount; ++loop)
    	{
    	sortedList->AppendL((*contactIdArray)[loop]);		
    	}
	TKeyArrayFix key(0,ECmpTInt);
	sortedList->Sort(key);

	// insert the group-member relationships
	const TInt KCountStmntParamIndex(KFirstIndex); // first and only parameter in query
	const TInt listLen(sortedList->Count() );
	TInt lastId(0);
	for (TInt i = 0; i < listLen; ++i)
		{
		TInt itemId((*sortedList)[i]);
		
		//check if a contact item with itemId id really exists in contact database
		RSqlStatement countStmnt;
		CleanupClosePushL(countStmnt);
		countStmnt.PrepareL(iDatabase, iCountContactsStmnt->SqlStringL() );
		User::LeaveIfError(countStmnt.BindInt(KCountStmntParamIndex, itemId) );
		TInt count = 0;
		TInt err = KErrNone;
		if((err = countStmnt.Next() ) == KSqlAtRow)
			{
			count = countStmnt.ColumnInt(iCountContactsStmnt->ParameterIndex(KSqlCount) );
			}
		else
			{
			User::LeaveIfError(err);	
			}	

		if(count == 0) 
			{
			User::Leave(KErrNotFound);	
			}
		CleanupStack::PopAndDestroy(&countStmnt);
			
		// only insert this if we haven't already seen it
		if (itemId != lastId || i == 0)
			{
			User::LeaveIfError(stmnt.BindInt(KGroupIdIndex, KGroupId) );
			User::LeaveIfError(stmnt.BindInt(KMemberIdIndex, itemId) );
			User::LeaveIfError(stmnt.Exec() );
			User::LeaveIfError(stmnt.Reset() );
			}
		lastId = itemId;
		}

	CleanupStack::PopAndDestroy(2, &stmnt); // and sortedList
	}