persistentstorage/dbms/pcdbms/usql/UQ_LIKE.CPP
changeset 0 08ec8eefde2f
equal deleted inserted replaced
-1:000000000000 0:08ec8eefde2f
       
     1 // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // SQL Like predicate node
       
    15 // 
       
    16 //
       
    17 
       
    18 #include "UQ_STD.H"
       
    19 
       
    20 inline TUint8* TextCopy(TUint8* aDest,const TUint8* aSrc,TInt aLen)
       
    21 	{return Mem::Copy(aDest,aSrc,aLen);}
       
    22 inline TUint16* TextCopy(TUint16* aDest,const TUint16* aSrc,TInt aLen)
       
    23 	{return (TUint16*)Mem::Copy(aDest,aSrc,aLen<<1);}
       
    24 
       
    25 // template Class HMatcherPattern
       
    26 
       
    27 template <class T,class D>
       
    28 inline HMatcherPattern<T,D>* HMatcherPattern<T,D>::Realloc(HMatcherPattern<T,D>* aThis,TInt aSize)
       
    29 	{return STATIC_CAST(TThis*,User::ReAlloc(aThis,_FOFF(TThis,iPattern[aSize])));}
       
    30 
       
    31 /**
       
    32 Creates a HMatcherPattern that converts the pattern into more manageable pieces
       
    33 based on a delimeter that is the wildcard * (asterisk).
       
    34 
       
    35 Characters between * (asterisks) must not number greater than KMaxSegmentLength.
       
    36 If only one * exists then the length is taken from the start and end of pattern.
       
    37 If these segments contain ? (question mark) wildcard than they must not number
       
    38 greater than KMaxSegmentLength-2.
       
    39 
       
    40 The user is responsible for the returned pointer's memory.
       
    41 
       
    42 @param			aPattern The search pattern to match.
       
    43 @param			aEscape ESCAPE clause 
       
    44 @leave			KErrNoMemory if no more memory is available.
       
    45 @leave			KErrArgument if the search pattern segment length is greater
       
    46 				than KMaxSegmentLength,
       
    47 @return			The newly constructed pattern pointer.
       
    48 @see			KMaxSegmentLength
       
    49 */
       
    50 template <class T,class D>
       
    51 HMatcherPattern<T,D>* HMatcherPattern<T,D>::NewL(const D& aPattern, TBool aEscape)
       
    52 	{
       
    53 	TThis* self=0;
       
    54 	TInt r=Construct(self,aPattern,aEscape);
       
    55 	if (r!=KErrNone)
       
    56 		{
       
    57 		User::Free(self);
       
    58 		__LEAVE(r);
       
    59 		}
       
    60 	return self;
       
    61 	}
       
    62 
       
    63 template <class T,class D>
       
    64 TInt HMatcherPattern<T,D>::MatchL(const D& aDes,const TTextOps& aTextOp) const
       
    65 //
       
    66 // Test the pattern against the supplied descriptor
       
    67 //
       
    68 	{
       
    69 	TDesMatcher<T,D> matcher(aDes);
       
    70 	return matcher.MatchL(*this,aTextOp);
       
    71 	}
       
    72 
       
    73 template <class T,class D>
       
    74 TInt HMatcherPattern<T,D>::MatchL(MStreamBuf& aBuf,TInt aLength,const TTextOps& aTextOp) const
       
    75 //
       
    76 // Test the pattern against the supplied stream
       
    77 //
       
    78 	{
       
    79 	TStreamMatcher<T,D> matcher(aBuf,aLength);
       
    80 	return matcher.MatchL(*this,aTextOp);
       
    81 	}
       
    82 
       
    83 /**
       
    84 Breaks up a given search pattern into manageable segments.
       
    85 
       
    86 The pattern is broken into segments. These segments are organised
       
    87 from the segment delimeter that is the wildcard * (asterisk).
       
    88 So in effect a segment may contain other wildcards (i.e. ?) or
       
    89 the entire pattern (no embedded asterisks) as these at least MUST match.
       
    90 
       
    91 The cell is created and updated with the segments type and length, and also
       
    92 the segment itself.
       
    93 
       
    94 Not including asterisks, the segment must be no longer than length KMaxSegmentLength.
       
    95 However, if the segment has a ? (question mark) wildcard within it then the 
       
    96 segment must be no longer than length KMaxSegmentLength-2.
       
    97 
       
    98 This is due to the way the Find and Match functions of TDes work. Match understands
       
    99 wildcards whilst Find does not.
       
   100 
       
   101 The match algorithm depends on KMaxSegmentLength being smaller than the
       
   102 text read buffer, and for efficiency should not be more than half the size
       
   103 of the read buffer. Also KMaxSegmentLength must currently fit into 8 bits,
       
   104 as it is embedded into a single character in an 8-bit text matching buffer. 
       
   105 [So increasing KMaxSegmentLength is not a simple exercise.]
       
   106 
       
   107 The search is repeated for each segment within the pattern. Increasing the cell
       
   108 size and inserting each segment and associated segment data.
       
   109 
       
   110 On reaching the end of the pattern this data is passed back to the user.
       
   111 The user is responsible for this returned pointers memory.
       
   112 
       
   113 @param			aCell On return points to a cell matching this pattern.
       
   114 @param			aPattern The search pattern to match.
       
   115 @param			aEscape ESCAPE clause 
       
   116 @return			KErrNoMemory if no memory is available to create or
       
   117 				increase the size of a cell,
       
   118 				KErrArgument if the search pattern segment length is greater
       
   119 				than KMaxSegmentLength,
       
   120 				KErrNone if the match was successful.
       
   121 @see			KMaxSegmentLength
       
   122 */
       
   123 template <class T,class D>
       
   124 TInt HMatcherPattern<T,D>::Construct(HMatcherPattern<T,D>*& aCell,const D& aPattern,TBool aEscape)
       
   125 	{
       
   126 	const T* pM=aPattern.Ptr();
       
   127 	const T* const eM=pM+aPattern.Length();
       
   128 	TInt size=(eM-pM)+KPatternGranularity;
       
   129 	TThis* self=Realloc(0,size);
       
   130 	if (!self)
       
   131 		return KErrNoMemory;
       
   132 	aCell=self;
       
   133 	T* seg=&self->iPattern[0];
       
   134 	const T* end=&self->iPattern[size];
       
   135 	TInt term;	// terminating code
       
   136 	
       
   137 	if (eM==pM)
       
   138 		term=ENull;
       
   139 	else
       
   140 		{
       
   141 		term=EStop;
       
   142 		do
       
   143 			{
       
   144 			//Following code is for the implementation of limited-ESCAPE-clause 
       
   145 			if(aEscape)
       
   146 				{
       
   147 				const T* here=pM;
       
   148 				TInt len = aPattern.Length();
       
   149 				if (len>KMaxSegmentLength)
       
   150 					return KErrArgument;
       
   151 				*seg++=T(EEscape );
       
   152 				*seg++=T(len);
       
   153 				seg=TextCopy(seg,here,len);
       
   154 				break;
       
   155 				}
       
   156 			T m=*pM;
       
   157 			if (m==KMatchAny)
       
   158 				{
       
   159 				term=ESuccess;
       
   160 				do
       
   161 					{
       
   162 					if (++pM==eM)
       
   163 						break;
       
   164 					m=*pM;
       
   165 					} while (m==KMatchAny);
       
   166 				if (pM==eM)
       
   167 					break;
       
   168 				}
       
   169 			const T* here=pM;
       
   170 			TInt type;
       
   171 			if (m==KMatchOne)
       
   172 				{
       
   173 				while (++pM<eM && *pM==KMatchOne) {;}
       
   174 				type=ESkip;
       
   175 				}
       
   176 			else
       
   177 				{
       
   178 				type=0;
       
   179 				while (++pM<eM)
       
   180 					{
       
   181 					m=*pM;
       
   182 					if (m==KMatchAny)
       
   183 						break;
       
   184 					if (m==KMatchOne)
       
   185 						type|=EWild;
       
   186 					}
       
   187 				if (term==EStop)
       
   188 					type|=EBeginning;
       
   189 				else if (pM==eM)
       
   190 					type|=EEnd;
       
   191 				else
       
   192 					{
       
   193 					type|=EMiddle;
       
   194 					if (type&EWild)
       
   195 						{		// include '*'s in segment for matching
       
   196 						--here;
       
   197 						++pM;
       
   198 						}
       
   199 					}
       
   200 				}
       
   201 			*seg++=T(type);
       
   202 			TInt len=pM-here;
       
   203 			if (len>KMaxSegmentLength)
       
   204 				return KErrArgument;
       
   205 			*seg++=T(len);
       
   206 			if (type==ESkip)
       
   207 				len=0;
       
   208 			if (seg+4+len>end)
       
   209 				{
       
   210 				TInt newsize=end-&self->iPattern[0]+KPatternGranularity;
       
   211 				self=Realloc(self,newsize);
       
   212 				if (!self)
       
   213 					return KErrNoMemory;
       
   214 				seg+=&self->iPattern[0]-&aCell->iPattern[0];
       
   215 				end=&self->iPattern[newsize];
       
   216 				aCell=self;
       
   217 				}
       
   218 			if (type==(EMiddle|EWild))
       
   219 				{
       
   220 				*seg++=T(KMatchAny);
       
   221 				++here;
       
   222 				--len;
       
   223 				}
       
   224 			seg=TextCopy(seg,here,len);
       
   225 			} while (pM<eM);
       
   226 		}
       
   227 	*seg++=T(term);
       
   228 	aCell=Realloc(self,seg-&self->iPattern[0]);
       
   229 	return KErrNone;
       
   230 	}
       
   231 	
       
   232 // class TMatcher
       
   233 
       
   234 template <class T,class D>
       
   235 TBool TMatcher<T,D>::MatchL(const HMatcherPattern<T,D>& aPattern,const TTextOps& aTextOp)
       
   236 	{
       
   237 	const T* pM=&aPattern.iPattern[0];
       
   238 	const T* eB;
       
   239 	const T* pB=UnderflowL(eB);
       
   240 	for (;;)
       
   241 		{
       
   242 		TInt segment=*pM;
       
   243 		switch (segment&EMask)
       
   244 			{
       
   245 		case ENull:
       
   246 			return pB==eB;
       
   247 		case ESuccess:
       
   248 			return ETrue;
       
   249 		case EStop:
       
   250 			if (pB==eB)
       
   251 				pB=UnderflowL(eB);
       
   252 			return pB==eB;
       
   253 		case ESkip:
       
   254 			{
       
   255 			TInt len=*++pM;
       
   256 			TInt skip=len+pB-eB;
       
   257 			if (skip>0)
       
   258 				{
       
   259 				pB=UnderflowL(eB)+skip;
       
   260 				if (pB>eB)
       
   261 					return EFalse;
       
   262 				}
       
   263 			else
       
   264 				pB+=len;
       
   265 			++pM;
       
   266 			}
       
   267 			break;
       
   268 		//Following code is for the implementation of limited-ESCAPE-clause 
       
   269 		case EEscape:
       
   270 			{
       
   271 			TInt len=*++pM;
       
   272 			++pM;
       
   273 			TInt bLen=eB-pB;
       
   274 			if (bLen<len)
       
   275 				return EFalse;
       
   276 			if (aTextOp.Find(pB,bLen,pM,len)<0)
       
   277 				return EFalse;
       
   278 			pM+=len;
       
   279 			pB+=bLen;
       
   280 			}
       
   281 			break;
       
   282 		case EBeginning:
       
   283 			{
       
   284 			TInt len=*++pM;
       
   285 			++pM;
       
   286 			if (eB-pB<len)
       
   287 				return EFalse;
       
   288 			if (segment&EWild)
       
   289 				{
       
   290 				if (aTextOp.Match(pB,len,pM,len)<0)
       
   291 					return EFalse;
       
   292 				}
       
   293 			else
       
   294 				{
       
   295 				if (aTextOp.Compare(pB,len,pM,len)!=0)
       
   296 					return EFalse;
       
   297 				}
       
   298 			pM+=len;
       
   299 			pB+=len;
       
   300 			}
       
   301 			break;
       
   302 		case EMiddle:
       
   303 			{
       
   304 			TInt len=*++pM;
       
   305 			++pM;
       
   306 			TInt match=len;
       
   307 			if (segment&EWild)
       
   308 				match-=2;	// the number of characters to match
       
   309 			TInt left=eB-pB;
       
   310 			if (left<match)
       
   311 				pB=UnderflowL(eB,left);
       
   312 			for (;;)
       
   313 				{
       
   314 				TInt bLen=eB-pB;
       
   315 				if (bLen<match)
       
   316 					return EFalse;
       
   317 				TInt pos=segment&EWild ? aTextOp.Match(pB,bLen,pM,len) : aTextOp.Find(pB,bLen,pM,len);
       
   318 				if (pos>=0)
       
   319 					{	// found it
       
   320 					pB+=pos+match;
       
   321 					break;
       
   322 					}
       
   323 				// not found, next chunk of data please.
       
   324 				pB=UnderflowL(eB,match-1);
       
   325 				}
       
   326 			pM+=len;
       
   327 			}
       
   328 			break;
       
   329 		case EEnd:	// match the last segment
       
   330 			{
       
   331 			TInt len=*++pM;
       
   332 			++pM;
       
   333 			TInt left=eB-pB;
       
   334 			if (left<len)
       
   335 				{
       
   336 				pB=UnderflowL(eB,left);
       
   337 				if (eB-pB<len)
       
   338 					return EFalse;
       
   339 				}
       
   340 			while (eB-pB>len)
       
   341 				pB=UnderflowL(eB,len);
       
   342 			if (segment&EWild)
       
   343 				{
       
   344 				if (aTextOp.Match(pB,len,pM,len)<0)
       
   345 					return EFalse;
       
   346 				}
       
   347 			else
       
   348 				{
       
   349 				if (aTextOp.Compare(pB,len,pM,len)!=0)
       
   350 					return EFalse;
       
   351 				}
       
   352 			}
       
   353 			return ETrue;
       
   354 			}
       
   355 		}
       
   356 	}
       
   357 
       
   358 // Class TDesMatcher
       
   359 
       
   360 template <class T,class D>
       
   361 inline TDesMatcher<T,D>::TDesMatcher(const D& aDes)
       
   362 	{iEnd=(iPtr=aDes.Ptr())+aDes.Length();}
       
   363 
       
   364 template <class T,class D>
       
   365 const T* TDesMatcher<T,D>::UnderflowL(const T*& aEnd,TInt aRetain)
       
   366 	{
       
   367 	const T* ptr=iPtr-aRetain;
       
   368 	aEnd=iPtr=iEnd;
       
   369 	return ptr;
       
   370 	}
       
   371 
       
   372 // Class TStreamMatcher
       
   373 
       
   374 template <class T,class D>
       
   375 inline TStreamMatcher<T,D>::TStreamMatcher(MStreamBuf& aStreamBuf,TInt aLength)
       
   376 	:iStream(&aStreamBuf),iRemain(aLength),iEnd(&iBuffer[EBufSize])
       
   377 	{}
       
   378 
       
   379 template <class T,class D>
       
   380 const T* TStreamMatcher<T,D>::UnderflowL(const T*& aEnd,TInt aRetain)
       
   381 	{
       
   382 	if (iRemain==0)
       
   383 		{	// no more stream data, don't move etc.
       
   384 		aEnd=iEnd;
       
   385 		return iEnd-aRetain;
       
   386 		}
       
   387 	else
       
   388 		{
       
   389 		T* rp=&iBuffer[0];
       
   390 		if (aRetain)
       
   391 			rp=TextCopy(rp,iEnd-aRetain,aRetain);
       
   392 		TInt read=Min(EBufSize-aRetain,iRemain);
       
   393 		iStream.ReadL(rp,read);
       
   394 		iRemain-=read;
       
   395 		aEnd=iEnd=rp+read; 
       
   396 		return iBuffer;
       
   397 		}
       
   398 	}
       
   399 
       
   400 // Class RSqlLiteral
       
   401 
       
   402 void RSqlLiteral::ToPattern8L(TBool aEscape)
       
   403 //
       
   404 // Convert a Buf8 to an 8-bit pattern
       
   405 //
       
   406 	{
       
   407 	__ASSERT(iType==EBuf8);
       
   408 	HMatcherPattern8* pattern=HMatcherPattern8::NewL(Text8(),aEscape);
       
   409 	User::Free(iVal.iAlloc);
       
   410 	iVal.iAlloc=pattern;
       
   411 	iType=EPattern8;
       
   412 	}
       
   413 
       
   414 void RSqlLiteral::ToPattern16L(TBool aEscape)
       
   415 //
       
   416 // Convert a Buf8 to an 8-bit pattern
       
   417 //
       
   418 	{
       
   419 	__ASSERT(iType==EBuf16);
       
   420 	HMatcherPattern16* pattern=HMatcherPattern16::NewL(Text16(),aEscape);
       
   421 	User::Free(iVal.iAlloc);
       
   422 	iVal.iAlloc=pattern;
       
   423 	iType=EPattern16;
       
   424 	}
       
   425 
       
   426 
       
   427 // Class CSqlLikePredicate
       
   428 
       
   429 LOCAL_C TInt MatchLength(const TText* aPtr,const TText* aEnd)
       
   430 	{
       
   431 	TInt match=0;
       
   432 	while (aPtr<aEnd)
       
   433 		{
       
   434 		TText c=*aPtr++;
       
   435 		if (c!=KMatchAny)
       
   436 			{
       
   437 			++match;
       
   438 			if (c=='\'')
       
   439 				++aPtr;
       
   440 			}
       
   441 		}
       
   442 	return match;
       
   443 	}
       
   444 //Following code is for the implementation of limited-ESCAPE-clause 
       
   445 LOCAL_C TInt MatchLengthEscape(const TText* aPtr,const TText* aEnd)
       
   446 	{
       
   447 	TInt match=0;
       
   448 	while (aPtr<aEnd)
       
   449 		{
       
   450 		TText c=*aPtr++;
       
   451 			{
       
   452 			++match;
       
   453 			if (c=='\'')
       
   454 				++aPtr;
       
   455 			}
       
   456 		}
       
   457 	return match;
       
   458 	}
       
   459 
       
   460 
       
   461 CSqlLikePredicate::CSqlLikePredicate(TType aType,const TDesC& aColumn,const RSqlLiteral& aPattern)
       
   462 	: CSqlLiteralNode(aType,aColumn,aPattern)
       
   463 	{
       
   464 	__ASSERT(aType==ELike||aType==ENotLike);
       
   465 	}
       
   466 
       
   467 void CSqlLikePredicate::BindL(const RDbTableRow& aSource)
       
   468 //
       
   469 // Compile the Pattern for LongText columns and evaluate the match length
       
   470 //
       
   471 	{
       
   472 	TInt matchsize;
       
   473 	if(iIsEscape)//Following code is for the implementation of limited-ESCAPE-clause 
       
   474 		{
       
   475 		matchsize = MatchLengthEscape(Value().Ptr(),Value().End());				
       
   476 		}
       
   477 	else
       
   478 		{
       
   479 		matchsize = MatchLength(Value().Ptr(),Value().End());	
       
   480 		}
       
   481 	CSqlLiteralNode::BindL(aSource);
       
   482 	switch (ColType())
       
   483 		{
       
   484 	default:	// type mismatch: should be caught by Literal::Bind
       
   485 		__ASSERT(0);
       
   486 	case EDbColText8:
       
   487 		break;
       
   488 	case EDbColLongText8:
       
   489 		Value().ToPattern8L(iIsEscape);
       
   490 		break;
       
   491 	case EDbColLongText16:
       
   492 		Value().ToPattern16L(iIsEscape);
       
   493 		matchsize<<=1;
       
   494 		break;
       
   495 	case EDbColText16:
       
   496 		matchsize<<=1;
       
   497 		break;
       
   498 		}
       
   499 	iMatchSize=matchsize;
       
   500 	}
       
   501 
       
   502 TBool CSqlLikePredicate::EvaluateL(const TTextOps& aTextOp) const
       
   503 	{
       
   504 	__ASSERT(IsBound());
       
   505 	TDbColType type=ColType();
       
   506 	TDbColumnC column(Column());
       
   507 	TInt match;
       
   508 	if (TDbCol::IsLong(type))
       
   509 		{
       
   510 		const TDbBlob& blob=column.Blob();
       
   511 		if (blob.Size()<iMatchSize)
       
   512 			match=EFalse;
       
   513 		else if (blob.IsInline())
       
   514 			match=type==EDbColLongText8 ? 
       
   515 				Value().Pattern8().MatchL(blob.PtrC8(),aTextOp) : 
       
   516 				Value().Pattern16().MatchL(blob.PtrC16(),aTextOp);
       
   517 		else
       
   518 			{
       
   519 			MStreamBuf& buf=StreamLC(blob);
       
   520 			match=type==EDbColLongText8 ? 
       
   521 				Value().Pattern8().MatchL(buf,blob.Size(),aTextOp) : 
       
   522 				Value().Pattern16().MatchL(buf,blob.Size()>>1,aTextOp);
       
   523 			CleanupStack::PopAndDestroy();
       
   524 			}
       
   525 		}
       
   526 	else if (column.Size()<iMatchSize)
       
   527 		match=EFalse;
       
   528 	else
       
   529 		{
       
   530 			if(iIsEscape) //Following code is for the implementation of limited-ESCAPE-clause 
       
   531 			{
       
   532 			match=type==EDbColText8 ?
       
   533 				aTextOp.Find(column.PtrC8(),Value().Text8()) :
       
   534 				aTextOp.Find(column.PtrC16(),Value().Text16());
       
   535 			}
       
   536 			else
       
   537 			{
       
   538 			match=type==EDbColText8 ?
       
   539 				aTextOp.Match(column.PtrC8(),Value().Text8()) :
       
   540 				aTextOp.Match(column.PtrC16(),Value().Text16());
       
   541 				
       
   542 			}
       
   543 		
       
   544 		match=match>=0;
       
   545 		}
       
   546 	return NodeType()==ELike ? match : !match;
       
   547 	}