Commit 4b44b849 authored by unknown's avatar unknown

Fixed problem with range optimization over overlapping ranges (#2448)


mysql-test/r/ctype_tis620.result:
  Cleanup test
mysql-test/r/range.result:
  Test problem with range optimization over overlapping ranges (#2448)
mysql-test/t/ctype_tis620.test:
  Cleanup test
mysql-test/t/range.test:
  Test problem with range optimization over overlapping ranges (#2448)
sql/mysqld.cc:
  Remove debug statement
strings/ctype-tis620.c:
  est problem with range optimization over overlapping ranges (#2448)
parent 7ba7ce6e
DROP TABLE IF EXISTS t620; drop table if exists t1;
CREATE TABLE t620 ( CREATE TABLE t1 (
recid int(11) NOT NULL auto_increment, recid int(11) NOT NULL auto_increment,
dyninfo text, dyninfo text,
PRIMARY KEY (recid) PRIMARY KEY (recid)
) TYPE=MyISAM; ) ENGINE=MyISAM;
INSERT INTO t620 VALUES (1,'color=\"STB,NPG\"\r\nengine=\"J30A13\"\r\nframe=\"MRHCG1640YP4\"\r\ngrade=\"V6\"\r\nmodel=\"ACCORD\"\r\nmodelcode=\"CG164YEN\"\r\ntype=\"VT6\"\r\n'); INSERT INTO t1 VALUES (1,'color=\"STB,NPG\"\r\nengine=\"J30A13\"\r\nframe=\"MRHCG1640YP4\"\r\ngrade=\"V6\"\r\nmodel=\"ACCORD\"\r\nmodelcode=\"CG164YEN\"\r\ntype=\"VT6\"\r\n');
INSERT INTO t620 VALUES (2,'color=\"HTM,NPG,DEG,RGS\"\r\nengine=\"F23A5YP1\"\r\nframe=\"MRHCF8640YP3\"\r\ngrade=\"EXi AT\"\r\nmodel=\"ACCORD\"\r\nmodelcode=\"CF864YE\"\r\ntype=\"EXA\"\r\n'); INSERT INTO t1 VALUES (2,'color=\"HTM,NPG,DEG,RGS\"\r\nengine=\"F23A5YP1\"\r\nframe=\"MRHCF8640YP3\"\r\ngrade=\"EXi AT\"\r\nmodel=\"ACCORD\"\r\nmodelcode=\"CF864YE\"\r\ntype=\"EXA\"\r\n');
SELECT DISTINCT SELECT DISTINCT
(IF( LOCATE( 'year=\"', dyninfo ) = 1, (IF( LOCATE( 'year=\"', dyninfo ) = 1,
SUBSTRING( dyninfo, 6+1, LOCATE('\"\r',dyninfo) - 6 -1), SUBSTRING( dyninfo, 6+1, LOCATE('\"\r',dyninfo) - 6 -1),
IF( LOCATE( '\nyear=\"', dyninfo ), IF( LOCATE( '\nyear=\"', dyninfo ),
SUBSTRING( dyninfo, LOCATE( '\nyear=\"', dyninfo ) + 7, SUBSTRING( dyninfo, LOCATE( '\nyear=\"', dyninfo ) + 7,
LOCATE( '\"\r', SUBSTRING( dyninfo, LOCATE( '\nyear=\"', dyninfo ) +7 )) - 1), '' ))) AS year LOCATE( '\"\r', SUBSTRING( dyninfo, LOCATE( '\nyear=\"', dyninfo ) +7 )) - 1), '' ))) AS year
FROM t620 FROM t1
HAVING year != '' ORDER BY year; HAVING year != '' ORDER BY year;
year year
DROP TABLE t1;
...@@ -300,3 +300,16 @@ SELECT COUNT(*) FROM t1 WHERE (c=0 and b=1) or (c=0 and a=1); ...@@ -300,3 +300,16 @@ SELECT COUNT(*) FROM t1 WHERE (c=0 and b=1) or (c=0 and a=1);
COUNT(*) COUNT(*)
6 6
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 ( a int not null, b int not null, INDEX ab(a,b) );
INSERT INTO t1 VALUES (47,1), (70,1), (15,1), (15, 4);
SELECT * FROM t1
WHERE
(
( b =1 AND a BETWEEN 14 AND 21 ) OR
( b =2 AND a BETWEEN 16 AND 18 ) OR
( b =3 AND a BETWEEN 15 AND 19 ) OR
(a BETWEEN 19 AND 47)
);
a b
15 1
47 1
DROP TABLE IF EXISTS t620; --disable_warnings
CREATE TABLE t620 ( drop table if exists t1;
--enable_warnings
CREATE TABLE t1 (
recid int(11) NOT NULL auto_increment, recid int(11) NOT NULL auto_increment,
dyninfo text, dyninfo text,
PRIMARY KEY (recid) PRIMARY KEY (recid)
) TYPE=MyISAM; ) ENGINE=MyISAM;
INSERT INTO t620 VALUES (1,'color=\"STB,NPG\"\r\nengine=\"J30A13\"\r\nframe=\"MRHCG1640YP4\"\r\ngrade=\"V6\"\r\nmodel=\"ACCORD\"\r\nmodelcode=\"CG164YEN\"\r\ntype=\"VT6\"\r\n'); INSERT INTO t1 VALUES (1,'color=\"STB,NPG\"\r\nengine=\"J30A13\"\r\nframe=\"MRHCG1640YP4\"\r\ngrade=\"V6\"\r\nmodel=\"ACCORD\"\r\nmodelcode=\"CG164YEN\"\r\ntype=\"VT6\"\r\n');
INSERT INTO t620 VALUES (2,'color=\"HTM,NPG,DEG,RGS\"\r\nengine=\"F23A5YP1\"\r\nframe=\"MRHCF8640YP3\"\r\ngrade=\"EXi AT\"\r\nmodel=\"ACCORD\"\r\nmodelcode=\"CF864YE\"\r\ntype=\"EXA\"\r\n'); INSERT INTO t1 VALUES (2,'color=\"HTM,NPG,DEG,RGS\"\r\nengine=\"F23A5YP1\"\r\nframe=\"MRHCF8640YP3\"\r\ngrade=\"EXi AT\"\r\nmodel=\"ACCORD\"\r\nmodelcode=\"CF864YE\"\r\ntype=\"EXA\"\r\n');
SELECT DISTINCT SELECT DISTINCT
(IF( LOCATE( 'year=\"', dyninfo ) = 1, (IF( LOCATE( 'year=\"', dyninfo ) = 1,
...@@ -14,5 +17,7 @@ SELECT DISTINCT ...@@ -14,5 +17,7 @@ SELECT DISTINCT
IF( LOCATE( '\nyear=\"', dyninfo ), IF( LOCATE( '\nyear=\"', dyninfo ),
SUBSTRING( dyninfo, LOCATE( '\nyear=\"', dyninfo ) + 7, SUBSTRING( dyninfo, LOCATE( '\nyear=\"', dyninfo ) + 7,
LOCATE( '\"\r', SUBSTRING( dyninfo, LOCATE( '\nyear=\"', dyninfo ) +7 )) - 1), '' ))) AS year LOCATE( '\"\r', SUBSTRING( dyninfo, LOCATE( '\nyear=\"', dyninfo ) +7 )) - 1), '' ))) AS year
FROM t620 FROM t1
HAVING year != '' ORDER BY year; HAVING year != '' ORDER BY year;
DROP TABLE t1;
...@@ -238,7 +238,7 @@ SELECT * FROM t1 WHERE a IN(1,2) AND b=5; ...@@ -238,7 +238,7 @@ SELECT * FROM t1 WHERE a IN(1,2) AND b=5;
DROP TABLE t1; DROP TABLE t1;
# #
# Test error with # Test problem with range optimzer and sub ranges
# #
CREATE TABLE t1 (a int, b int, c int, INDEX (c,a,b)); CREATE TABLE t1 (a int, b int, c int, INDEX (c,a,b));
...@@ -249,3 +249,17 @@ SELECT COUNT(*) FROM t1 WHERE (c=0 and a=1) or (c=0 and b=1); ...@@ -249,3 +249,17 @@ SELECT COUNT(*) FROM t1 WHERE (c=0 and a=1) or (c=0 and b=1);
SELECT COUNT(*) FROM t1 WHERE (c=0 and b=1) or (c=0 and a=1); SELECT COUNT(*) FROM t1 WHERE (c=0 and b=1) or (c=0 and a=1);
DROP TABLE t1; DROP TABLE t1;
#
# Test problem with range optimization over overlapping ranges (#2448)
#
CREATE TABLE t1 ( a int not null, b int not null, INDEX ab(a,b) );
INSERT INTO t1 VALUES (47,1), (70,1), (15,1), (15, 4);
SELECT * FROM t1
WHERE
(
( b =1 AND a BETWEEN 14 AND 21 ) OR
( b =2 AND a BETWEEN 16 AND 18 ) OR
( b =3 AND a BETWEEN 15 AND 19 ) OR
(a BETWEEN 19 AND 47)
);
...@@ -4747,7 +4747,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), ...@@ -4747,7 +4747,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
berkeley_env_flags|= DB_TXN_NOSYNC; berkeley_env_flags|= DB_TXN_NOSYNC;
else else
berkeley_env_flags&= ~DB_TXN_NOSYNC; berkeley_env_flags&= ~DB_TXN_NOSYNC;
printf("berkeley_env_flags: %d, arg '%s'\n", berkeley_env_flags, argument);
break; break;
case OPT_BDB_NO_RECOVER: case OPT_BDB_NO_RECOVER:
berkeley_init_flags&= ~(DB_RECOVER); berkeley_init_flags&= ~(DB_RECOVER);
......
...@@ -1710,6 +1710,8 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) ...@@ -1710,6 +1710,8 @@ key_or(SEL_ARG *key1,SEL_ARG *key2)
return 0; // OOM return 0; // OOM
tmp->copy_max_to_min(&key); tmp->copy_max_to_min(&key);
tmp->increment_use_count(key1->use_count+1); tmp->increment_use_count(key1->use_count+1);
/* Increment key count as it may be used for next loop */
key.increment_use_count(1);
new_arg->next_key_part=key_or(tmp->next_key_part,key.next_key_part); new_arg->next_key_part=key_or(tmp->next_key_part,key.next_key_part);
key1=key1->insert(new_arg); key1=key1->insert(new_arg);
break; break;
...@@ -2708,15 +2710,18 @@ int QUICK_SELECT_DESC::get_next() ...@@ -2708,15 +2710,18 @@ int QUICK_SELECT_DESC::get_next()
} }
else else
{ {
/* Heikki changed Sept 11, 2002: since InnoDB does not store the cursor /*
position if READ_KEY_EXACT is used to a primary key with all Heikki changed Sept 11, 2002: since InnoDB does not store the cursor
key columns specified, we must use below HA_READ_KEY_OR_NEXT, position if READ_KEY_EXACT is used to a primary key with all
so that InnoDB stores the cursor position and is able to move key columns specified, we must use below HA_READ_KEY_OR_NEXT,
the cursor one step backward after the search. */ so that InnoDB stores the cursor position and is able to move
the cursor one step backward after the search. */
DBUG_ASSERT(range->flag & NEAR_MAX || range_reads_after_key(range)); DBUG_ASSERT(range->flag & NEAR_MAX || range_reads_after_key(range));
/* Note: even if max_key is only a prefix, HA_READ_AFTER_KEY will /*
* do the right thing - go past all keys which match the prefix */ Note: even if max_key is only a prefix, HA_READ_AFTER_KEY will
do the right thing - go past all keys which match the prefix
*/
result=file->index_read(record, (byte*) range->max_key, result=file->index_read(record, (byte*) range->max_key,
range->max_length, range->max_length,
((range->flag & NEAR_MAX) ? ((range->flag & NEAR_MAX) ?
......
...@@ -449,225 +449,160 @@ uchar NEAR sort_order_tis620[]= ...@@ -449,225 +449,160 @@ uchar NEAR sort_order_tis620[]=
(uchar) '\370',(uchar) '\371',(uchar) '\372',(uchar) '\373',(uchar) '\374',(uchar) '\375',(uchar) '\376',(uchar) '\377', (uchar) '\370',(uchar) '\371',(uchar) '\372',(uchar) '\373',(uchar) '\374',(uchar) '\375',(uchar) '\376',(uchar) '\377',
}; };
/* Convert thai string to "Standard C String Function" sortable string
Arg: const source string and length of converted string
Ret: Sortable string
*/
static void _thai2sortable(uchar *tstr)
{
uchar *p ;
int len,tlen ;
uchar c,l2bias ;
tlen= len = strlen (tstr) ;
l2bias = 256 - 8 ;
for (p=tstr; tlen > 0; p++,tlen--)
{
c = *p ;
if (isthai(c))
{
int *t_ctype0 = t_ctype[c] ;
if (isconsnt(c))
l2bias -= 8 ;
if (isldvowel(c) && isconsnt(p[1]))
{
/*
simply swap between leading-vowel and consonant
*/
*p = p[1];
p[1]= c ;
tlen-- ;
p++;
continue ;
}
// if found level 2 char (L2_GARAN,L2_TONE*,L2_TYKHU) move to last
if (t_ctype0[1]>= L2_GARAN)
{
// l2bias use to control position weight of l2char
// example (*=l2char) XX*X must come before X*XX
strcpy (p,p+1) ;
tstr[len-1] = l2bias + t_ctype0[1]- L2_GARAN +1 ;
p-- ;
continue ;
}
}
else
{
l2bias -= 8 ;
*p = to_lower_tis620[c];
}
/*
this routine skip non-printable char
but not necessary, leave it like raw ascii 8 bits
*/
/*
t_ctype0 = t_ctype[p[0]];
if ((t_ctype0[0]|t_ctype0[1]|t_ctype0[2])==IGNORE)
{
strcpy(p,p+1);
p-- ;
}
*/
}
}
/* /*
NOTE: isn't it faster to alloc buffer in calling function? Convert thai string to "Standard C String Function" sortable string
Arg: const source string and length of converted string
Ret: Sortable string
*/ */
/*
Sathit's NOTE: we don't use this function anymore static void _thai2sortable(uchar *tstr)
static uchar* thai2sortable(const uchar * tstr,int len)
{ {
*/ uchar *p;
/* We use only 3 levels (neglect capitalization). */ int len, tlen;
/* uchar l2bias;
const uchar* p= tstr;
uchar *outBuf; tlen= len= strlen (tstr);
uchar *pRight1, *pRight2, *pRight3; l2bias= 256 - 8;
uchar *pLeft1, *pLeft2, *pLeft3; for (p= tstr; tlen > 0; p++, tlen--)
uint bufSize;
uint RightSize;
len= (int) strnlen((char*) tstr,len);
bufSize= (uint) buffsize((char*) tstr);
RightSize= sizeof(uchar) * (len + 1);
if (!(outBuf= pLeft1= pRight1=
(uchar *)malloc(sizeof(uchar) * bufSize + RightSize*2)))
return (uchar*) tstr;
pLeft2= pRight2= pRight1 + sizeof(uchar) * bufSize;
pLeft3= pRight3= pRight2 + RightSize;
while (--len > 0)
{ {
int *t_ctype0= t_ctype[p[0]]; uchar c= *p;
if (isldvowel(*p) && isconsnt(p[1]))
if (isthai(c))
{ {
int *t_ctype1= t_ctype[p[1]]; int *t_ctype0= t_ctype[c];
*pRight1++= t_ctype1[0];
*pRight2++= t_ctype1[1]; if (isconsnt(c))
*pRight3++= t_ctype1[2]; l2bias -= 8;
*pRight1++= t_ctype0[0]; if (isldvowel(c) && isconsnt(p[1]))
*pRight2++= t_ctype0[1]; {
*pRight3++= t_ctype0[2]; /* simply swap between leading-vowel and consonant */
p+= 2; *p= p[1];
len--; p[1]= c;
tlen--;
p++;
continue;
}
/* if found level 2 char (L2_GARAN,L2_TONE*,L2_TYKHU) move to last */
if (t_ctype0[1] >= L2_GARAN)
{
/*
l2bias use to control position weight of l2char
example (*=l2char) XX*X must come before X*XX
*/
strmov(p,p+1);
tstr[len-1]= l2bias + t_ctype0[1]- L2_GARAN +1;
p--;
continue;
}
} }
else else
{ {
*pRight1= t_ctype0[0]; l2bias-= 8;
if(*pRight1 != IGNORE) *p= to_lower_tis620[c];
pRight1++;
*pRight2= t_ctype0[1];
if (*pRight2 != IGNORE)
pRight2++;
*pRight3= t_ctype0[2];
if(*pRight3 != IGNORE)
pRight3++;
p++;
} }
} }
if (!len)
{
int *t_ctype0= t_ctype[p[0]];
*pRight1= t_ctype0[0];
if (*pRight1 != IGNORE)
pRight1++;
*pRight2= t_ctype0[1];
if (*pRight2 != IGNORE)
pRight2++;
*pRight3= t_ctype0[2];
if (*pRight3 != IGNORE)
pRight3++;
}
*pRight1++= L2_BLANK;
*pRight2++= L3_BLANK;
*pRight3++= '\0';
memcpy(pRight1, pLeft2, pRight2 - pLeft2);
pRight1+= pRight2 - pLeft2;
memcpy(pRight1, pLeft3, pRight3 - pLeft3);
return outBuf;
} }
*/
/* strncoll() replacement, compare 2 string, both are conveted to sortable string
Arg: 2 Strings and it compare length /*
Ret: strcmp result strncoll() replacement, compare 2 string, both are converted to sortable
string
Arg: 2 Strings and it compare length
Ret: strcmp result
*/ */
int my_strnncoll_tis620(const uchar * s1, int len1, const uchar * s2, int len2) int my_strnncoll_tis620(const uchar * s1, int len1, const uchar * s2, int len2)
{ {
uchar buf[80] ; uchar buf[80] ;
uchar *tc1, *tc2; uchar *tc1, *tc2;
int i; int i;
len1= (int) strnlen((char*) s1,len1); len1= (int) strnlen((char*) s1,len1);
len2= (int) strnlen((char*) s2,len2); len2= (int) strnlen((char*) s2,len2);
if ((len1 + len2 +2) > (int) sizeof(buf)) tc1= buf;
tc1 = (uchar *)malloc(len1+len2) ; if ((len1 + len2 +2) > (int) sizeof(buf))
else tc1= (uchar*) malloc(len1+len2);
tc1 = buf ; tc2= tc1 + len1+1;
tc2 = tc1 + len1+1 ; memcpy((char*) tc1, (char*) s1, len1);
strncpy((char *)tc1,(char *)s1,len1) ; tc1[len1]= 0; /* if length(s1)> len1, need to put 'end of string' */
tc1[len1] = 0; // if s1's length > len1, need to put 'end of string' memcpy((char *)tc2, (char *)s2, len2);
strncpy((char *)tc2,(char *)s2,len2) ; tc2[len2]= 0; /* put end of string */
tc2[len2] = 0; // put end of string _thai2sortable(tc1);
_thai2sortable(tc1); _thai2sortable(tc2);
_thai2sortable(tc2); i= strcmp((char*)tc1, (char*)tc2);
i= strcmp((char*)tc1, (char*)tc2); if (tc1 != buf)
if (tc1 != buf ) free(tc1);
free(tc1); return i;
return i;
} }
/* strnxfrm replacment, convert Thai string to sortable string
Arg: Destination buffer, source string, dest length and source length /*
Ret: Conveted string size strnxfrm replacment, convert Thai string to sortable string
Arg: Destination buffer, source string, dest length and source length
Ret: Conveted string size
*/ */
int my_strnxfrm_tis620(uchar * dest, const uchar * src, int len, int srclen) int my_strnxfrm_tis620(uchar * dest, const uchar * src, int len, int srclen)
{ {
if (len > srclen) if (len > srclen)
len = srclen ; len= srclen ;
strncpy (dest,src,len) ; strnmov(dest, src, len) ;
dest[len] = 0; // if src's length > len, need to put 'end of string' dest[len]= 0; /* if length(src) > len, need to put 'end of string' */
_thai2sortable(dest); _thai2sortable(dest);
return strlen(dest); return strlen(dest);
} }
/* strcoll replacment, compare 2 strings
Arg: 2 strings /*
Ret: strcmp result strcoll replacment, compare 2 strings
Arg: 2 strings
Ret: strcmp result
*/ */
int my_strcoll_tis620(const uchar * s1, const uchar * s2) int my_strcoll_tis620(const uchar * s1, const uchar * s2)
{ {
return my_strnncoll_tis620(s1, strlen((char *)s1),s2,strlen((char *)s2)); return my_strnncoll_tis620(s1, strlen((char *)s1),s2,strlen((char *)s2));
} }
/* strxfrm replacment, convert Thai string to sortable string
Arg: Destination buffer, String and dest buffer size /*
Ret: Converting string size strxfrm replacment, convert Thai string to sortable string
Arg: Destination buffer, String and dest buffer size
Ret: Converting string size
*/ */
int my_strxfrm_tis620(uchar * dest, const uchar * src, int len) int my_strxfrm_tis620(uchar * dest, const uchar * src, int len)
{ {
return my_strnxfrm_tis620(dest,src,len,strlen((char *)src)); return my_strnxfrm_tis620(dest,src,len,strlen((char *)src));
} }
/* Convert SQL like string to C string
Arg: String, its length, escape character, resource length, minimal string and maximum string /*
Ret: Alway 0 Convert SQL LIKE string to C string
Arg: String, its length, escape character, resource length,
minimal string and maximum string
Ret: Always 0
*/
/*
We just copy this function from opt_range.cc. No need to convert to
thai2sortable string. min_str and max_str will be use for comparison and
converted there.
*/ */
/* We just copy this function from opt_range.cc. No need to convert to
thai2sortable string. min_str and max_str will be use for comparison and
converted there. */
#define max_sort_chr ((char) 255) #define max_sort_chr ((char) 255)
#define wild_one '_' #define wild_one '_'
#define wild_many '%' #define wild_many '%'
my_bool my_like_range_tis620(const char *ptr, uint ptr_length, pchar escape, my_bool my_like_range_tis620(const char *ptr, uint ptr_length, pchar escape,
uint res_length, char *min_str, char *max_str, uint res_length, char *min_str, char *max_str,
uint *min_length, uint *max_length) uint *min_length, uint *max_length)
{ {
const char *end=ptr+ptr_length; const char *end=ptr+ptr_length;
char *min_org=min_str; char *min_org=min_str;
...@@ -692,7 +627,7 @@ my_bool my_like_range_tis620(const char *ptr, uint ptr_length, pchar escape, ...@@ -692,7 +627,7 @@ my_bool my_like_range_tis620(const char *ptr, uint ptr_length, pchar escape,
*min_length= (uint) (min_str - min_org); *min_length= (uint) (min_str - min_org);
*max_length=res_length; *max_length=res_length;
do { do {
*min_str++ = ' '; /* Because if key compression */ *min_str++ = ' '; /* Because of key compression */
*max_str++ = max_sort_chr; *max_str++ = max_sort_chr;
} while (min_str != min_end); } while (min_str != min_end);
return 0; return 0;
...@@ -702,18 +637,21 @@ my_bool my_like_range_tis620(const char *ptr, uint ptr_length, pchar escape, ...@@ -702,18 +637,21 @@ my_bool my_like_range_tis620(const char *ptr, uint ptr_length, pchar escape,
*min_length= *max_length = (uint) (min_str - min_org); *min_length= *max_length = (uint) (min_str - min_org);
while (min_str != min_end) while (min_str != min_end)
*min_str++ = *max_str++ = ' '; /* Because if key compression */ *min_str++= *max_str++ = ' '; /* Because of key compression */
return 0; return 0;
} }
/* Thai normalization for input sub system
Arg: Buffer, 's length, String, 'length /*
Ret: Void Thai normalization for input sub system
Arg: Buffer, 's length, String, 'length
*/ */
void ThNormalize(uchar* ptr, uint field_length, const uchar* from, uint length) void ThNormalize(uchar* ptr, uint field_length, const uchar* from, uint length)
{ {
const uchar* fr= from; const uchar *fr= from;
uchar* p= ptr; uchar *p= ptr;
uint i; uint i;
if (length > field_length) if (length > field_length)
...@@ -721,24 +659,21 @@ void ThNormalize(uchar* ptr, uint field_length, const uchar* from, uint length) ...@@ -721,24 +659,21 @@ void ThNormalize(uchar* ptr, uint field_length, const uchar* from, uint length)
for (i=0;i<length;i++,p++,fr++) for (i=0;i<length;i++,p++,fr++)
{ {
*p = *fr ; *p= *fr ;
/* Sathit's NOTE: it's better idea not to do any normalize /* Sathit's NOTE: it's better idea not to do any normalize */
*/ if (istone(*fr) || isdiacrt1(*fr))
if (istone(*fr) || isdiacrt1(*fr)) {
{ if (i > 0 && (islwrvowel(fr[-1]) || isuprvowel(fr[-1])))
if (i > 0 && (islwrvowel(fr[-1]) || isuprvowel(fr[-1]))) continue;
continue ; if(islwrvowel(fr[1]) || isuprvowel(fr[1]))
if(islwrvowel(fr[1]) || isuprvowel(fr[1])) {
{ *p= fr[1];
*p= fr[1]; p[1]= *fr;
p[1]= *fr; fr++;
fr++; p++;
p++; i++;
i++ ; }
}
} }
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment