diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
new file mode 100644
index 061acd1..f263103
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
*************** do { \
*** 202,207 ****
--- 202,232 ----
  	matches = completion_matches(text, complete_from_query); \
  } while (0)
  
+ #define COMPLETE_WITH_ARG(function) \
+ do { \
+ 	char   *_completion_schema; \
+ 	char   *_completion_function; \
+ \
+ 	_completion_schema = strtokx(function, " \t\n\r", ".", "\"", 0, \
+ 								 false, false, pset.encoding); \
+ 	(void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
+ 				   false, false, pset.encoding); \
+ 	_completion_function = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
+ 								   false, false, pset.encoding); \
+ 	if (_completion_function == NULL) \
+ 	{ \
+ 		completion_charp = Query_for_list_of_arguments; \
+ 		completion_info_charp = function; \
+ 	} \
+ 	else \
+ 	{ \
+ 		completion_charp = Query_for_list_of_arguments_with_schema; \
+ 		completion_info_charp = _completion_function; \
+ 		completion_info_charp2 = _completion_schema; \
+ 	} \
+ 	matches = completion_matches(text, complete_from_query); \
+ } while (0)
+ 
  /*
   * Assembly instructions for schema queries
   */
*************** static const SchemaQuery Query_for_list_
*** 598,607 ****
  "   FROM pg_catalog.pg_am "\
  "  WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'"
  
  #define Query_for_list_of_arguments \
! " SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
! "   FROM pg_catalog.pg_proc "\
! "  WHERE proname='%s'"
  
  #define Query_for_list_of_extensions \
  " SELECT pg_catalog.quote_ident(extname) "\
--- 623,647 ----
  "   FROM pg_catalog.pg_am "\
  "  WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'"
  
+ /* the silly-looking length condition is just to eat up the current word */
  #define Query_for_list_of_arguments \
! "SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
! "  FROM pg_catalog.pg_proc "\
! " WHERE (%d = pg_catalog.length('%s'))"\
! "   AND (pg_catalog.quote_ident(proname)='%s'"\
! "        OR '\"' || proname || '\"'='%s') "\
! "   AND (pg_catalog.pg_function_is_visible(pg_proc.oid))"
! 
! /* the silly-looking length condition is just to eat up the current word */
! #define Query_for_list_of_arguments_with_schema \
! "SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
! "  FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "\
! " WHERE (%d = pg_catalog.length('%s'))"\
! "   AND n.oid = p.pronamespace "\
! "   AND (pg_catalog.quote_ident(proname)='%s' "\
! "        OR '\"' || proname || '\"' ='%s') "\
! "   AND (pg_catalog.quote_ident(nspname)='%s' "\
! "        OR '\"' || nspname || '\"' ='%s') "
  
  #define Query_for_list_of_extensions \
  " SELECT pg_catalog.quote_ident(extname) "\
*************** psql_completion(char *text, int start, i
*** 863,875 ****
  			COMPLETE_WITH_LIST(list_ALTERAGG);
  		}
  		else
! 		{
! 			char	   *tmp_buf = malloc(strlen(Query_for_list_of_arguments) + strlen(prev2_wd));
! 
! 			sprintf(tmp_buf, Query_for_list_of_arguments, prev2_wd);
! 			COMPLETE_WITH_QUERY(tmp_buf);
! 			free(tmp_buf);
! 		}
  	}
  
  	/* ALTER SCHEMA <name> */
--- 903,909 ----
  			COMPLETE_WITH_LIST(list_ALTERAGG);
  		}
  		else
! 			COMPLETE_WITH_ARG(prev2_wd);
  	}
  
  	/* ALTER SCHEMA <name> */
*************** psql_completion(char *text, int start, i
*** 2186,2198 ****
  			 (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
  			  pg_strcasecmp(prev3_wd, "FUNCTION") == 0) &&
  			 pg_strcasecmp(prev_wd, "(") == 0)
! 	{
! 		char	   *tmp_buf = malloc(strlen(Query_for_list_of_arguments) + strlen(prev2_wd));
! 
! 		sprintf(tmp_buf, Query_for_list_of_arguments, prev2_wd);
! 		COMPLETE_WITH_QUERY(tmp_buf);
! 		free(tmp_buf);
! 	}
  	/* DROP OWNED BY */
  	else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
  			 pg_strcasecmp(prev_wd, "OWNED") == 0)
--- 2220,2226 ----
  			 (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
  			  pg_strcasecmp(prev3_wd, "FUNCTION") == 0) &&
  			 pg_strcasecmp(prev_wd, "(") == 0)
! 		COMPLETE_WITH_ARG(prev2_wd);
  	/* DROP OWNED BY */
  	else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
  			 pg_strcasecmp(prev_wd, "OWNED") == 0)
