diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 70dab53..488e4c4 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 1250,1255 ****
--- 1250,1258 ----
     <indexterm>
      <primary>chr</primary>
     </indexterm>
+     <indexterm>
+     <primary>concat</primary>
+    </indexterm>
     <indexterm>
      <primary>convert</primary>
     </indexterm>
***************
*** 1266,1274 ****
--- 1269,1283 ----
      <primary>encode</primary>
     </indexterm>
     <indexterm>
+     <primary>format</primary>
+    </indexterm>
+    <indexterm>
      <primary>initcap</primary>
     </indexterm>
     <indexterm>
+     <primary>left</primary>
+    </indexterm>
+    <indexterm>
      <primary>lpad</primary>
     </indexterm>
     <indexterm>
***************
*** 1296,1301 ****
--- 1305,1316 ----
      <primary>replace</primary>
     </indexterm>
     <indexterm>
+     <primary>reverse</primary>
+    </indexterm>
+    <indexterm>
+     <primary>right</primary>
+    </indexterm>
+    <indexterm>
      <primary>rpad</primary>
     </indexterm>
     <indexterm>
***************
*** 1376,1381 ****
--- 1391,1409 ----
  
        <row>
         <entry>
+         <literal><function>concat</function>(<parameter>str</parameter> <type>"any"</type>,
+          [, <parameter>str</parameter> <type>"any"</type> [...] ])</literal>
+        </entry>
+        <entry><type>text</type></entry>
+        <entry>
+         Returns concated strings. NULLs are ignored.
+        </entry>
+        <entry><literal>concat('abcde', 2, NULL, 22)</literal></entry>
+        <entry><literal>abcde222</literal></entry>
+       </row>
+ 
+       <row>
+        <entry>
          <literal><function>convert</function>(<parameter>string</parameter> <type>bytea</type>,
          <parameter>src_encoding</parameter> <type>name</type>,
          <parameter>dest_encoding</parameter> <type>name</type>)</literal>
***************
*** 1454,1459 ****
--- 1482,1502 ----
        </row>       
  
        <row>
+        <entry>
+         <literal><function>format</function>(<parameter>formatstr</parameter> <type>text</type>
+         [, <parameter>value</parameter> <type>any</type> [...] ])</literal>
+        </entry>
+        <entry><type>text</type></entry>
+        <entry>
+         The format string can be followed by optional argument values to be inserted into 
+         the result string. Inside the format string, % is replaced by the string representation 
+         of the next optional argument's value. Write %% to emit a literal %.
+        </entry>
+        <entry><literal>format('% % xxx: %', 10, 'foo', current_date)</literal></entry>
+        <entry><literal>10 foo xxx: 2010-03-05</literal></entry>
+       </row>
+ 
+       <row>
         <entry><literal><function>initcap</function>(<parameter>string</parameter>)</literal></entry>
         <entry><type>text</type></entry>
         <entry>
***************
*** 1466,1471 ****
--- 1509,1528 ----
        </row>
  
        <row>
+        <entry>
+         <literal><function>left</function>(<parameter>str</parameter> <type>text</type>,
+         <parameter>n</parameter> <type>int</type> )</literal>
+        </entry>
+        <entry><type>text</type></entry>
+        <entry>
+         Returns first n chars of string. When n is negative, then returns chars from begin
+         to n char from right.
+        </entry>
+        <entry><literal>left('abcde', 2)</literal></entry>
+        <entry><literal>ab</literal></entry>
+       </row>
+ 
+       <row>
         <entry><literal><function>length</function>(<parameter>string</parameter>)</literal></entry>
         <entry><type>int</type></entry>
         <entry>
***************
*** 1680,1685 ****
--- 1737,1768 ----
  
        <row>
         <entry>
+         <literal><function>reverse</function>(<parameter>str</parameter>)</literal>
+        </entry>
+        <entry><type>text</type></entry>
+        <entry>
+         Returns reversed string.
+        </entry>
+        <entry><literal>reverse('abcde')</literal></entry>
+        <entry><literal>edcba</literal></entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <literal><function>right</function>(<parameter>str</parameter> <type>text</type>,
+          <parameter>n</parameter> <type>int</type> )</literal>
+        </entry>
+        <entry><type>text</type></entry>
+        <entry>
+         Returns last n chars of string. When n is negative, then returns chars from n char
+         to end of string
+        </entry>
+        <entry><literal>right('abcde', 2)</literal></entry>
+        <entry><literal>de</literal></entry>
+       </row>
+ 
+       <row>
+        <entry>
          <literal><function>rpad</function>(<parameter>string</parameter> <type>text</type>,
          <parameter>length</parameter> <type>int</type>
          <optional>, <parameter>fill</parameter> <type>text</type></optional>)</literal>
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index af28c15..e62cf6e 100644
*** a/src/backend/utils/adt/varlena.c
--- b/src/backend/utils/adt/varlena.c
*************** string_agg_finalfn(PG_FUNCTION_ARGS)
*** 3415,3417 ****
--- 3415,3635 ----
  	else
  		PG_RETURN_NULL();
  }
+ 
+ /*
+  * Replaces % symbols with the external representation of the arguments.
+  *
+  * Syntax: text_format(format text [, arg "any"] ...) RETURNS text
+  */
+ Datum
+ text_format(PG_FUNCTION_ARGS)
+ {
+ 	text		   *fmt;
+ 	size_t			len;
+ 	StringInfoData	str;
+ 	const char	   *cp,
+ 				   *start_ptr,
+ 				   *end_ptr;
+ 	int				n;
+ 	text		   *result;
+ 
+ 	/* When format string is null, returns null */
+ 	if (PG_ARGISNULL(0))
+ 		PG_RETURN_NULL();
+ 
+ 	fmt = PG_GETARG_TEXT_PP(0);
+ 	len = VARSIZE_ANY_EXHDR(fmt);
+ 	start_ptr = VARDATA_ANY(fmt);
+ 	end_ptr = start_ptr + len - 1;
+ 	initStringInfo(&str);
+ 
+ 	n = 1;
+ 	for (cp = start_ptr; cp <= end_ptr; cp++)
+ 	{
+ 		/*
+ 		 * Occurrences of a single % are replaced by the next parameter's
+ 		 * external representation. Double %'s are converted to one %.
+ 		 */
+ 		if (cp[0] != '%')
+ 		{
+ 			appendStringInfoChar(&str, cp[0]);
+ 		}
+ 		else if (cp < end_ptr && cp[1] == '%')
+ 		{
+ 			appendStringInfoChar(&str, '%');
+ 			cp++;
+ 		}
+ 		else if (n >= PG_NARGS())
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 					 errmsg("too few parameters for format function")));
+ 		}
+ 		else
+ 		{
+ 			if (PG_ARGISNULL(n))
+ 			{
+ 				/* A NULL value is shown as '<NULL>'. */
+ 				appendStringInfoString(&str, "<NULL>");
+ 			}
+ 			else
+ 			{
+ 				Datum	value;
+ 				Oid		type;
+ 				Oid		typOutput;
+ 				bool	typIsVarlena;
+ 
+ 				/* append n-th value */
+ 				value = PG_GETARG_DATUM(n);
+ 				type = get_fn_expr_argtype(fcinfo->flinfo, n);
+ 				getTypeOutputInfo(type, &typOutput, &typIsVarlena);
+ 				appendStringInfoString(&str,
+ 					OidOutputFunctionCall(typOutput, value));
+ 			}
+ 			n++;
+ 		}
+ 	}
+ 
+ 	/* check if all arguments are used */
+ 	if (n != PG_NARGS())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("too many parameters for format function")));
+ 
+ 	result = cstring_to_text_with_len(str.data, str.len);
+ 	pfree(str.data);
+ 
+ 	PG_RETURN_TEXT_P(result);
+ }
+ 
+ /*
+  * Non-variadic version of text_format only to validate format string.
+  */
+ Datum
+ text_format_nv(PG_FUNCTION_ARGS)
+ {
+ 	return text_format(fcinfo);
+ }
+ 
+ /*
+  * Concatenates arguments. NULL values are skipped.
+  */
+ Datum
+ text_concat(PG_FUNCTION_ARGS)
+ {
+ 	StringInfoData	str;
+ 	int				i;
+ 	text		   *result;
+ 
+ 	/* return NULL, if there are not any parameter */
+ 	if (PG_NARGS() == 0)
+ 		PG_RETURN_NULL();
+ 
+ 	initStringInfo(&str);
+ 
+ 	for (i = 0; i < PG_NARGS(); i++)
+ 	{
+ 		if (!PG_ARGISNULL(i))
+ 		{
+ 			Datum	value;
+ 			Oid		type;
+ 			Oid		typOutput;
+ 			bool	typIsVarlena;
+ 
+ 			/* append n-th value */
+ 			value = PG_GETARG_DATUM(i);
+ 			type = get_fn_expr_argtype(fcinfo->flinfo, i);
+ 			getTypeOutputInfo(type, &typOutput, &typIsVarlena);
+ 			appendStringInfoString(&str,
+ 				OidOutputFunctionCall(typOutput, value));
+ 		}
+ 	}
+ 
+ 	result = cstring_to_text_with_len(str.data, str.len);
+ 	pfree(str.data);
+ 
+ 	PG_RETURN_TEXT_P(result);
+ }
+ 
+ 
+ /*
+  * Returns first n characters. When n is negative, returns chars without
+  * last |n| characters.
+  */
+ Datum
+ text_left(PG_FUNCTION_ARGS)
+ {
+ 	text	   *str = PG_GETARG_TEXT_PP(0);
+ 	const char *p = VARDATA_ANY(str);
+ 	int			len = VARSIZE_ANY_EXHDR(str);
+ 	int			n = PG_GETARG_INT32(1);
+ 	int			rlen;
+ 
+ 	if (n < 0)
+ 		n = pg_mbstrlen_with_len(p, len) + n;
+ 	rlen = pg_mbcharcliplen(p, len, n);
+ 
+ 	PG_RETURN_TEXT_P(cstring_to_text_with_len(p, rlen));
+ }
+ 
+ /*
+  * Returns last n characters. When n is negative, returns string without
+  * first |n| characters.
+  */
+ Datum
+ text_right(PG_FUNCTION_ARGS)
+ {
+ 	text	   *str = PG_GETARG_TEXT_PP(0);
+ 	const char *p = VARDATA_ANY(str);
+ 	int			len = VARSIZE_ANY_EXHDR(str);
+ 	int			n = PG_GETARG_INT32(1);
+ 	int			off;
+ 
+ 	if (n < 0)
+ 		n = -n;
+ 	else
+ 		n = pg_mbstrlen_with_len(p, len) - n;
+ 	off = pg_mbcharcliplen(p, len, n);
+ 
+ 	PG_RETURN_TEXT_P(cstring_to_text_with_len(p + off, len - off));
+ }
+ 
+ /*
+  * Returns reversed string
+  */
+ Datum
+ text_reverse(PG_FUNCTION_ARGS)
+ {
+ 	text		   *str = PG_GETARG_TEXT_PP(0);
+ 	const char	   *p = VARDATA_ANY(str);
+ 	int				len = VARSIZE_ANY_EXHDR(str);
+ 	const char	   *endp = p + len;
+ 	text		   *result;
+ 	char		   *dst;
+ 
+ 	result = palloc(len + VARHDRSZ);
+ 	dst = (char*) VARDATA(result) + len;
+ 	SET_VARSIZE(result, len + VARHDRSZ);
+ 
+ 	if (pg_database_encoding_max_length() > 1)
+ 	{
+ 		/* multibyte version */
+ 		while (p < endp)
+ 		{
+ 			int		sz;
+ 
+ 			sz = pg_mblen(p);
+ 			dst -= sz;
+ 			memcpy(dst, p, sz);
+ 			p += sz;
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* single byte version */
+ 		while (p < endp)
+ 			*(--dst) = *p++;
+ 	}
+ 
+ 	PG_RETURN_TEXT_P(result);
+ }
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6036493..87c891d 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("I/O");
*** 2718,2723 ****
--- 2718,2735 ----
  DATA(insert OID = 1799 (  oidout		   PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2275 "26" _null_ _null_ _null_ _null_ oidout _null_ _null_ _null_ ));
  DESCR("I/O");
  
+ DATA(insert OID = 3094 ( concat			PGNSP PGUID 12 1 0 2276 f f f f f s 1 0 25 "2276" "{2276}" "{v}" _null_ _null_  text_concat _null_ _null_ _null_ ));
+ DESCR("concat values to text");
+ DATA(insert OID = 3095 ( left			PGNSP PGUID 12 1 0 0 f f f t f i 2 0 25 "25 23" _null_ _null_ _null_ _null_  text_left _null_ _null_ _null_ ));
+ DESCR("returns first n chars");
+ DATA(insert OID = 3096 ( right			PGNSP PGUID 12 1 0 0 f f f t f i 2 0 25 "25 23" _null_ _null_ _null_ _null_  text_right _null_ _null_ _null_ ));
+ DESCR("returns last n chars");
+ DATA(insert OID = 3097 ( reverse			PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_  text_reverse  _null_ _null_ _null_ ));
+ DESCR("returns reversed string");
+ DATA(insert OID = 3098 ( format		PGNSP PGUID 12 1 0 2276 f f f f f s 2 0 25 "25 2276" "{25,2276}" "{i,v}" _null_ _null_  text_format _null_ _null_ _null_ ));
+ DESCR("format text message");
+ DATA(insert OID = 3099 ( format		PGNSP PGUID 12 1 0 0 f f f f f s 1 0 25 "25" _null_ _null_ _null_ _null_  text_format_nv _null_ _null_ _null_ ));
+ DESCR("format text message");
  
  DATA(insert OID = 1810 (  bit_length	   PGNSP PGUID 14 1 0 0 f f f t f i 1 0 23 "17" _null_ _null_ _null_ _null_ "select pg_catalog.octet_length($1) * 8" _null_ _null_ _null_ ));
  DESCR("length in bits");
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 45123fd..5c4cd44 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum string_agg_transfn(PG_FUNCT
*** 731,736 ****
--- 731,743 ----
  extern Datum string_agg_delim_transfn(PG_FUNCTION_ARGS);
  extern Datum string_agg_finalfn(PG_FUNCTION_ARGS);
  
+ extern Datum text_format(PG_FUNCTION_ARGS);
+ extern Datum text_format_nv(PG_FUNCTION_ARGS);
+ extern Datum text_concat(PG_FUNCTION_ARGS);
+ extern Datum text_left(PG_FUNCTION_ARGS);
+ extern Datum text_right(PG_FUNCTION_ARGS);
+ extern Datum text_reverse(PG_FUNCTION_ARGS);
+ 
  /* version.c */
  extern Datum pgsql_version(PG_FUNCTION_ARGS);
  
diff --git a/src/test/regress/expected/text.out b/src/test/regress/expected/text.out
index 08d002f..f561d61 100644
*** a/src/test/regress/expected/text.out
--- b/src/test/regress/expected/text.out
*************** ERROR:  operator does not exist: integer
*** 51,53 ****
--- 51,126 ----
  LINE 1: select 3 || 4.0;
                   ^
  HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
+ select format('Hello % %', 'World', 10);
+      format     
+ ----------------
+  Hello World 10
+ (1 row)
+ 
+ select format('users: %, date: %', 10, to_date('20080809','YYYYMMDD'));
+            format            
+ -----------------------------
+  users: 10, date: 08-09-2008
+ (1 row)
+ 
+ select format('Hello');
+  format 
+ --------
+  Hello
+ (1 row)
+ 
+ -- should to fail!
+ select format('Hello %');
+ ERROR:  too few parameters for format function
+ select format('Hello',10);
+ ERROR:  too many parameters for format function
+ /*
+  * concat tests
+  */
+ select concat(1,2,3,'hello',true, false, to_date('20100309','YYYYMMDD'));
+         concat        
+ ----------------------
+  123hellotf03-09-2010
+ (1 row)
+ 
+ /*
+  * others tests
+  */
+ select reverse('abcde');
+  reverse 
+ ---------
+  edcba
+ (1 row)
+ 
+ select i, left('ahoj', i) from generate_series(-5, 5) t(i) order by i;
+  i  | left 
+ ----+------
+  -5 | 
+  -4 | 
+  -3 | a
+  -2 | ah
+  -1 | aho
+   0 | 
+   1 | a
+   2 | ah
+   3 | aho
+   4 | ahoj
+   5 | ahoj
+ (11 rows)
+ 
+ select i, right('ahoj', i) from generate_series(-5, 5) t(i) order by i;
+  i  | right 
+ ----+-------
+  -5 | 
+  -4 | 
+  -3 | j
+  -2 | oj
+  -1 | hoj
+   0 | 
+   1 | j
+   2 | oj
+   3 | hoj
+   4 | ahoj
+   5 | ahoj
+ (11 rows)
+ 
diff --git a/src/test/regress/sql/text.sql b/src/test/regress/sql/text.sql
index b739e56..e22f18b 100644
*** a/src/test/regress/sql/text.sql
--- b/src/test/regress/sql/text.sql
*************** select 'four: ' || 2+2;
*** 28,30 ****
--- 28,50 ----
  -- but not this:
  
  select 3 || 4.0;
+ 
+ select format('Hello % %', 'World', 10);
+ select format('users: %, date: %', 10, to_date('20080809','YYYYMMDD'));
+ select format('Hello');
+ 
+ -- should to fail!
+ select format('Hello %');
+ select format('Hello',10);
+ 
+ /*
+  * concat tests
+  */
+ select concat(1,2,3,'hello',true, false, to_date('20100309','YYYYMMDD'));
+ 
+ /*
+  * others tests
+  */
+ select reverse('abcde');
+ select i, left('ahoj', i) from generate_series(-5, 5) t(i) order by i;
+ select i, right('ahoj', i) from generate_series(-5, 5) t(i) order by i;
