patch

text/plain

Filename: patch
Type: text/plain
Part: 0
Message: Re: Are we losing momentum?
Index: command.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/psql/command.c,v
retrieving revision 1.95
diff -u -r1.95 command.c
--- command.c	2003/04/04 20:40:45	1.95
+++ command.c	2003/04/16 15:37:53
@@ -66,7 +66,386 @@
 static bool do_connect(const char *new_dbname, const char *new_user);
 static bool do_shell(const char *command);
 
+static char *eat_token(char *cp, char *token, size_t token_len);
+static char *eat_whitespace(char *cp);
+static size_t token_len(char *cp);
+
+#define _(x) gettext((x))
+
+/* These macros are a tad evil but saves a gagillion keystrokes and
+ * should only be used in HandleMySQLShowCmds(). */
+#define MYSQL_SHOW_PARSE_OPT_LIKE(cmd, cmd_len) do { \
+  cp = eat_whitespace(&tkn1[tkn1_len]); \
+  cp_pos = 0; \
+  if (cp == NULL) \
+    goto help; \
+  strncpy(my_line, cmd, cmd_len); \
+  tkn2 = eat_token(cp, "LIKE", 4); \
+  tkn2_len = token_len(tkn2); \
+  if (tkn2 != NULL) { \
+    sprintf(&my_line[cmd_len], " %.*s*", tkn2_len, tkn2); \
+    options_string = &my_line[cmd_len]; \
+  } \
+  if (!QUIET()) \
+    fprintf(stderr, "\n\tTIP: In %s, \"%s\" is natively written as \"\\%s%.*s%.*s%.*s\"\n\n", getprogname(), line, cmd, (tkn2_len ? 1 : 0), " ", tkn2_len, tkn2, (tkn2_len ? 1 : 0), "*"); \
+} while(0)
+
+#define MYSQL_SHOW_COPY_ARG(cmd, cmd_len) do { \
+  strncpy(my_line, cmd, cmd_len); \
+  if (!QUIET()) { \
+    fprintf(stderr, "\n\tTIP: In %s, \"%s\" is natively written as \"\\%s\"\n\n", getprogname(), line, cmd); \
+  } \
+} while(0)
+
 /*----------
+ * HandleMySQLShowCmds:
+ *
+ * Handles all the different "SHOW" commands while in MySQL
+ * compatibility mode, ordinarily called by MainLoop().
+ *
+ * This function is a ripoff of HandleSlashCmds() and acts as a
+ * translation layer between the user input and exec_command().
+ *
+ * 'query_buf' contains the query-so-far, which may be modified by
+ * execution of the backslash command (for example, \r clears it)
+ * query_buf can be NULL if there is no query so far.
+ *
+ * Returns a status code indicating what action is desired, see command.h.
+ *----------
+ */
+
+backslashResult
+HandleMySQLShowCmds(const char *line,
+				PQExpBuffer query_buf,
+				const char **end_of_cmd,
+				volatile int *paren_level)
+{
+	backslashResult status = CMD_SKIP_LINE;
+	bool		run_cmd;
+	char	   *cp, *my_line, *tkn1, *tkn2;
+	char	   *options_string = NULL;
+	size_t		cp_pos, tkn1_len, tkn2_len;
+	const char *continue_parse = NULL;	/* tell the mainloop where the
+										 * backslash command ended */
+
+#ifdef USE_ASSERT_CHECKING
+	assert(line);
+#endif
+
+	my_line = xstrdup(line);
+	run_cmd = true;
+	cp_pos = strlen(line);
+	continue_parse = &line[cp_pos];
+	if (end_of_cmd != NULL)
+		*end_of_cmd = &line[cp_pos];
+
+	/*
+	 * Find the thing that we're showing
+	 */
+	cp = tkn1 = eat_token(my_line, "SHOW", 4);
+	tkn1_len = token_len(tkn1);
+	if (cp == NULL) {
+		status = CMD_UNKNOWN;
+		goto error;
+	}
+
+	if (cp[0] == '\0') {
+		help:
+		MYSQL_SHOW_COPY_ARG("?", 2);
+	} else if (strncasecmp(tkn1, "AGGREGATES", tkn1_len) == 0) {
+		/* SHOW AGGREGATES [LIKE pattern] */
+		/* \da [PATTERN]  list aggregate functions */
+		MYSQL_SHOW_PARSE_OPT_LIKE("da", 3);
+	} else if (strncasecmp(tkn1, "CASTS", tkn1_len) == 0) {
+		/* SHOW CASTS */
+		/* \dC  list casts */
+		MYSQL_SHOW_COPY_ARG("dC", 3);
+	} else if (strncasecmp(tkn1, "CATALOGS", tkn1_len) == 0) {
+		/* SHOW CATALOGS [LIKE pattern] */
+		/* \dS  lists system catalogs */
+		MYSQL_SHOW_PARSE_OPT_LIKE("dS", 3);
+	} else if (strncasecmp(tkn1, "COLUMNS", tkn1_len) == 0) {
+		/* SHOW COLUMNS FROM tbl_name */
+		/* \dt [PATTERN]  list columns in a table */
+		cp = eat_whitespace(&tkn1[tkn1_len]);
+		cp_pos = 0;
+		if (cp == NULL)
+			goto help;
+
+		/* Skip past the token word FROM and return the next token */
+		cp = tkn2 = eat_token(cp, "FROM", 4);
+		tkn2_len = token_len(tkn2);
+		if (tkn2 == NULL || tkn2[0] == '\0')
+			goto help;
+
+		/* Abuse my_line since my_line is always shorter than the string passed in */
+		strncpy(my_line, "d", 2);
+		sprintf(&my_line[2], "%.*s", tkn2_len, tkn2);
+		options_string = &my_line[2];
+		if (!QUIET())
+			fprintf(stderr, "\n\tTIP: In %s, \"%s\" is natively written as \"\\%s%.*s%.*s\"\n\n", getprogname(), line, "d", (tkn2_len ? 1 : 0), " ", tkn2_len, tkn2);
+	} else if (strncasecmp(tkn1, "COMMENTS", tkn1_len) == 0) {
+		/* SHOW COMMENTS [LIKE pattern] */
+		/* \dd [PATTERN]  show comment for object */
+		MYSQL_SHOW_PARSE_OPT_LIKE("dd", 3);
+	} else if (strncasecmp(tkn1, "CONVERSIONS", tkn1_len) == 0) {
+		/* SHOW CONVERSIONS [LIKE pattern] */
+		/* \dc [PATTERN]  list conversions */
+		MYSQL_SHOW_COPY_ARG("dc", 3);
+	} else if (strncasecmp(tkn1, "DATABASES", tkn1_len) == 0) {
+		/* \l  list all databases */
+		MYSQL_SHOW_COPY_ARG("l", 2);
+	} else if (strncasecmp(tkn1, "DOMAINS", tkn1_len) == 0) {
+		/* SHOW DOMAINS [LIKE pattern] */
+		/* \dD [PATTERN]  list domains */
+		MYSQL_SHOW_PARSE_OPT_LIKE("dD", 3);
+	} else if (strncasecmp(tkn1, "FUNCTIONS", tkn1_len) == 0) {
+		/* SHOW FUNCTIONS [LIKE pattern] */
+		/* \df [PATTERN]  list functions */
+		MYSQL_SHOW_PARSE_OPT_LIKE("df", 3);
+	} else if (strncasecmp(tkn1, "HELP", tkn1_len) == 0) {
+		/* SHOW HELP [ON (function_name|ALL) ]*/
+		/* \h [NAME]  help on syntax of SQL commands, * for all commands */
+		strncpy(my_line, "h", 2);
+		cp = eat_whitespace(&tkn1[tkn1_len]);
+		cp_pos = 0;
+		if (cp == NULL)
+			goto help;
+
+		/* Skip past the token word FROM and return the next token */
+		cp = tkn2 = eat_token(cp, "ON", 2);
+		tkn2_len = token_len(tkn2);
+		if (tkn2 == NULL || tkn2[0] == '\0') {
+			strncpy(my_line, "h", 2);
+		} else {
+			strncpy(my_line, "h", 2);
+			if (strncasecmp(tkn2, "ALL", 3) == 0) {
+				strncpy(&my_line[2], " *", 3);
+				tkn2 = &my_line[3];
+			} else
+				sprintf(&my_line[2], " %.*s", tkn2_len, tkn2);
+
+			options_string = &my_line[3];
+		}
+		if (!QUIET())
+			fprintf(stderr, "\n\tTIP: In %s, \"%s\" is natively written as \"\\%s%.*s%.*s\"\n\n", getprogname(), line, "h", (tkn2_len ? 1 : 0), " ", tkn2_len, tkn2);
+	} else if (strncasecmp(tkn1, "INDEXES", tkn1_len) == 0 ||
+		   strncasecmp(tkn1, "INDEX", tkn1_len) == 0) {
+		/* SHOW INDEX (FROM table_name|LIKE pattern) */
+		/* FROM = query with arg comparing to table names.  LIKE = query with arg comparing to index names */
+		PQExpBufferData buf;
+		PGresult	 *res;
+		printQueryOpt myopt = pset.popt;
+
+		cp = eat_whitespace(&tkn1[tkn1_len]);
+		cp_pos = 0;
+		if (cp == NULL)
+			goto help;
+
+		if (strncasecmp(cp, "FROM", 4) == 0) {
+			cp = tkn2 = eat_token(cp, "FROM", 4);
+
+			tkn2_len = token_len(tkn2);
+			if (tkn2 == NULL || tkn2[0] == '\0')
+				goto help;
+
+			initPQExpBuffer(&buf);
+			/* Ned to populate the buffer */
+			printfPQExpBuffer(&buf,
+						"SELECT n.nspname AS \"Schema\", c2.relname AS \"Table\", c.relname AS \"Name\",\n"
+						"  u.usename AS \"Owner\", i.indisclustered AS \"Clustered\", i.indisunique AS \"Unique\",\n"
+						"  i.indisprimary AS \"Primary\"\n"
+						" FROM pg_catalog.pg_class c\n"
+				  		"  JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid\n"
+			  			"  JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid\n"
+						"  LEFT JOIN pg_catalog.pg_user u ON u.usesysid= c.relowner\n"
+						"  LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
+						" WHERE c.relkind IN ('i','') AND pg_catalog.pg_table_is_visible(c.oid)\n"
+						"  AND c2.relname ~ '^%.*s' ORDER BY c2.relname, c.relname",
+						tkn2_len, tkn2);
+
+		} else if (strncasecmp(cp, "LIKE", 4) == 0) {
+			cp = tkn2 = eat_token(cp, "LIKE", 4);
+
+			tkn2_len = token_len(tkn2);
+			if (tkn2 == NULL || tkn2[0] == '\0')
+				goto help;
+
+			initPQExpBuffer(&buf);
+
+			/* Ned to populate the buffer */
+			printfPQExpBuffer(&buf,
+						"SELECT n.nspname AS \"Schema\", c2.relname AS \"Table\", c.relname AS \"Name\",\n"
+						"  u.usename AS \"Owner\", i.indisclustered AS \"Clustered\", i.indisunique AS \"Unique\",\n"
+						"  i.indisprimary AS \"Primary\"\n"
+						" FROM pg_catalog.pg_class c\n"
+						"  JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid\n"
+						"  JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid\n"
+						"  LEFT JOIN pg_catalog.pg_user u ON u.usesysid= c.relowner\n"
+						"  LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
+						" WHERE c.relkind IN ('i','') AND pg_catalog.pg_table_is_visible(c.oid)\n"
+						"  AND c.relname ~ '^%.*s' ORDER BY c.relname",
+						tkn2_len, tkn2);
+		} else
+			goto help;
+
+		res = PSQLexec(buf.data, false);
+		termPQExpBuffer(&buf);
+		if (!res)
+			goto error;
+
+		if (PQntuples(res) == 0 && !QUIET())
+				fprintf(pset.queryFout, _("No matching indexes found.\n"));
+		else
+			{
+				myopt.nullPrint = NULL;
+				myopt.title = _("List of indexes");
+
+				printQuery(res, &myopt, pset.queryFout);
+			}
+
+		PQclear(res);
+		run_cmd = false;
+		options_string = &my_line[2];
+	} else if (strncasecmp(tkn1, "LARGEOBJECTS", tkn1_len) == 0) {
+		/* SHOW LARGEOBJECTS */
+		/* \dl  list large objects */
+		MYSQL_SHOW_COPY_ARG("dl", 3);
+	} else if (strncasecmp(tkn1, "OPERATORS", tkn1_len) == 0) {
+		/* SHOW OPERATORS [LIKE name] */
+		/* \do [NAME]  list operators */
+		MYSQL_SHOW_PARSE_OPT_LIKE("do", 3);
+	} else if (strncasecmp(tkn1, "PRIVILEGES", tkn1_len) == 0) {
+		/* SHOW PRIVILEGES (ON name|LIKE pattern) */
+		/* \dp [PATTERN]  list table access privileges */
+		MYSQL_SHOW_PARSE_OPT_LIKE("dp", 3);
+	} else if (strncasecmp(tkn1, "PROCESSLIST", tkn1_len) == 0) {
+		/* SHOW PROCESSLIST */
+		/* SELECT * FROM pg_catalog.pg_stat_activity ; */
+		PQExpBufferData buf;
+		PGresult	 *res;
+		printQueryOpt myopt = pset.popt;
+
+		initPQExpBuffer(&buf);
+		printfPQExpBuffer(&buf,
+					"SELECT a.datname AS \"Database\", a.procpid AS \"PID\", a.usename AS \"Username\",\n"
+					" a.current_query AS \"Current Query\" FROM pg_catalog.pg_stat_activity a");
+
+		res = PSQLexec(buf.data, false);
+		termPQExpBuffer(&buf);
+		if (!res)
+			goto error;
+
+		if (PQntuples(res) == 0 && !QUIET())
+				fprintf(pset.queryFout, _("No stats available.\n"));
+		else
+			{
+				myopt.nullPrint = NULL;
+				myopt.title = _("List of processes");
+
+				printQuery(res, &myopt, pset.queryFout);
+			}
+
+		PQclear(res);
+		run_cmd = false;
+		options_string = &my_line[2];
+	} else if (strncasecmp(tkn1, "SCHEMAS", tkn1_len) == 0) {
+		/* SHOW SCHEMAS [LIKE pattern] */
+		/* \dn [PATTERN]  list schemas */
+		MYSQL_SHOW_PARSE_OPT_LIKE("dn", 3);
+	} else if (strncasecmp(tkn1, "SEQUENCES", tkn1_len) == 0) {
+		/* SHOW SEQUENCES [LIKE pattern] */
+		/* ds [PATTERN]	 list sequences */
+		MYSQL_SHOW_PARSE_OPT_LIKE("ds", 3);
+	} else if (strncasecmp(tkn1, "STATUS", tkn1_len) == 0) {
+		/* SHOW STATUS */
+		/* SELECT * FROM pg_catalog.pg_stat_database ; */
+		PQExpBufferData buf;
+		PGresult	 *res;
+		printQueryOpt myopt = pset.popt;
+
+		initPQExpBuffer(&buf);
+		printfPQExpBuffer(&buf,
+					"SELECT d.datname AS \"Database\", d.numbackends AS \"Num Backends\",\n"
+					"  d.xact_commit AS \"Txn Commit\", d.xact_rollback AS \"Txn Rollback\",\n"
+					"  d.blks_read AS \"Blocks Read\", d.blks_hit AS \"Blocks Hit\"\n"
+					" FROM pg_catalog.pg_stat_database d");
+
+		res = PSQLexec(buf.data, false);
+		termPQExpBuffer(&buf);
+		if (!res)
+			goto error;
+
+		if (PQntuples(res) == 0 && !QUIET())
+				fprintf(pset.queryFout, _("No status available.\n"));
+		else
+			{
+				myopt.nullPrint = NULL;
+				myopt.title = _("Status");
+
+				printQuery(res, &myopt, pset.queryFout);
+			}
+
+		PQclear(res);
+		run_cmd = false;
+		options_string = &my_line[2];
+	} else if (strncasecmp(tkn1, "TABLES", tkn1_len) == 0) {
+		/* SHOW TABLES [LIKE pattern] */
+		/* \dt [PATTERN]  list tables */
+		MYSQL_SHOW_PARSE_OPT_LIKE("dt", 3);
+	} else if (strncasecmp(tkn1, "TYPES", tkn1_len) == 0) {
+		/* SHOW TYPES [LIKE pattern] */
+		/* \dT [PATTERN]  list data types */
+		MYSQL_SHOW_PARSE_OPT_LIKE("dT", 3);
+	} else if (strncasecmp(tkn1, "USERS", tkn1_len) == 0) {
+		/* SHOW USERS [LIKE pattern] */
+		/* \du [PATTERN]  list users */
+		MYSQL_SHOW_PARSE_OPT_LIKE("du", 3);
+	} else if (strncasecmp(tkn1, "VARIABLES", tkn1_len) == 0) {
+		/* INCOMPLETE - XXXXXXXXXXXXXXXXXX
+		 * Pushing "SHOW [TAB][TAB]y" however, does work */
+		/* SHOW VARIABLES */
+		/* Need to iterate through the list of variables that would
+		 * normally be shown via "SHOW [TAB][TAB]" when not in mysql
+		 * compatibility mode.	This mode will be an oddity in that
+		 * it won't return normal results and will look a tad funky,
+		 * I think.  This could iterate through the list of
+		 * variables stored in *pgsql_variables[] and show their
+		 * values. */
+		MYSQL_SHOW_COPY_ARG("?", 2);
+	} else if (strncasecmp(tkn1, "VIEWS", tkn1_len) == 0) {
+		/* SHOW VIEWS [LIKE pattern] */
+		/* \dv [PATTERN	list views */
+		MYSQL_SHOW_PARSE_OPT_LIKE("dv", 3);
+	} else {
+		/* SHOW [variable_name] */
+		/* This is syntatically the same the normal SHOW syntax
+		 * without MySQL compatibility mode turned on. */
+		/* BUG XXXXXXXXXXXXXX This currently works, but in an odd
+		 * manner, and why, I'm not sure.
+		 *
+		 * "SHOW TimeZone;" Doesn't work, but
+		 * "SHOW TimeZone\n;" Does work.
+		 */
+		strncpy(my_line, "SHOW", 5);
+		tkn2 = eat_whitespace(&tkn1[tkn1_len]);
+		tkn2_len = token_len(tkn2);
+		strncpy(&my_line[6], tkn2, tkn2_len);
+		options_string = tkn2;
+	}
+
+	if (run_cmd == true)
+		status = exec_command(my_line, options_string, &continue_parse, query_buf, paren_level);
+
+    error:
+	free(my_line);
+	query_buf->len = 0;
+
+	return status;
+}
+
+
+/*----------
  * HandleSlashCmds:
  *
  * Handles all the different commands that start with '\',
@@ -1726,6 +2105,48 @@
 			break;
 	}
 	return "unknown";
+}
+
+
+static char *eat_token(char *cp, char *token, size_t token_len) {
+	size_t cp_pos;
+	char *p;
+
+	p = cp;
+	/* Skip past the token */
+	cp_pos = strncasecmp(p, token, token_len);
+	if (cp_pos != 0)
+		return(NULL);
+
+	/* Skip past the spaces after the token */
+	p = eat_whitespace(&p[token_len]);
+	return(p);
+}
+
+static char *eat_whitespace(char *cp) {
+	size_t cp_pos;
+
+	if (cp == NULL)
+		return(NULL);
+
+	for (cp_pos = 0; cp[cp_pos] != NULL; cp_pos++)
+		if (!isspace(cp[cp_pos]))
+			break;
+
+	return(&cp[cp_pos]);
+}
+
+static size_t token_len(char *cp) {
+	size_t cp_pos;
+
+	if (cp == NULL)
+		return((size_t)0);
+
+	for (cp_pos = 0; cp[cp_pos] != NULL; cp_pos++)
+		if (isspace(cp[cp_pos]))
+			break;
+
+	return(cp_pos);
 }
 
 
Index: command.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/psql/command.h,v
retrieving revision 1.14
diff -u -r1.14 command.h
--- command.h	2002/03/27 19:16:13	1.14
+++ command.h	2003/04/16 15:37:53
@@ -26,6 +26,11 @@
 } backslashResult;
 
 
+backslashResult HandleMySQLShowCmds(const char *line,
+				PQExpBuffer query_buf,
+				const char **end_of_cmd,
+				volatile int *paren_level);
+
 backslashResult HandleSlashCmds(const char *line,
 				PQExpBuffer query_buf,
 				const char **end_of_cmd,
Index: help.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/psql/help.c,v
retrieving revision 1.72
diff -u -r1.72 help.c
--- help.c	2003/04/14 16:23:36	1.72
+++ help.c	2003/04/16 15:37:54
@@ -97,6 +97,7 @@
 	puts(_("  -v NAME=VALUE   set psql variable 'NAME' to 'VALUE'"));
 	puts(_("  -X              do not read startup file (~/.psqlrc)"));
 	puts(_("  --help          show this help, then exit"));
+	puts(_("  --mysql, -m     Run in quasi-MySQL compatibility mode"));
 	puts(_("  --version       output version information, then exit"));
 
 	puts(_("\nInput and output options:"));
Index: mainloop.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/psql/mainloop.c,v
retrieving revision 1.55
diff -u -r1.55 mainloop.c
--- mainloop.c	2003/03/21 03:28:29	1.55
+++ mainloop.c	2003/04/16 15:37:54
@@ -274,7 +274,7 @@
 			else if (line[i] == '/' && line[i + thislen] == '*')
 			{
 				in_xcomment++;
-				if (in_xcomment == 1) 
+				if (in_xcomment == 1)
 					ADVANCE_1;
 			}
 
@@ -404,11 +404,12 @@
 			}
 
 			/* backslash command */
-			else if (bslash_count)
+			else if (bslash_count || (pset.mode_mysql == true && i > 0 && strncasecmp(&line[i - 1], "SHOW", 4) == 0))
 			{
 				const char *end_of_cmd = NULL;
 
-				line[i - prevlen] = '\0';		/* overwrites backslash */
+				if (bslash_count)
+					line[i - prevlen] = '\0';		/* overwrites backslash */
 
 				/* is there anything else on the line for the command? */
 				if (line[query_start + strspn(line + query_start, " \t\n\r")] != '\0')
@@ -423,9 +424,15 @@
 					appendPQExpBufferStr(query_buf, line + query_start);
 				}
 
-				/* handle backslash command */
-				slashCmdStatus = HandleSlashCmds(&line[i],
-						   query_buf->len > 0 ? query_buf : previous_buf,
+				/* Handle backslash command/MySQL compatibility */
+				if (pset.mode_mysql == true && strncasecmp(&line[i - 1], "SHOW", 4) == 0)
+					slashCmdStatus = HandleMySQLShowCmds(&line[i - 1],
+							   query_buf->len > 0 ? query_buf : previous_buf,
+												 &end_of_cmd,
+												 &paren_level);
+				else
+	  				slashCmdStatus = HandleSlashCmds(&line[i],
+							   query_buf->len > 0 ? query_buf : previous_buf,
 												 &end_of_cmd,
 												 &paren_level);
 
Index: settings.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/psql/settings.h,v
retrieving revision 1.13
diff -u -r1.13 settings.h
--- settings.h	2002/03/05 00:01:02	1.13
+++ settings.h	2003/04/16 15:37:54
@@ -51,6 +51,7 @@
 	bool		issuper;		/* is the current user a superuser? (used
 								 * to form the prompt) */
 	bool		timing;			/* timing of all queries */
+	bool		mode_mysql;		/* MySQL command compatibility (show commands) */
 } PsqlSettings;
 
 extern PsqlSettings pset;
Index: startup.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/psql/startup.c,v
retrieving revision 1.73
diff -u -r1.73 startup.c
--- startup.c	2003/04/04 20:42:13	1.73
+++ startup.c	2003/04/16 15:37:54
@@ -128,6 +128,7 @@
 
 	pset.cur_cmd_source = stdin;
 	pset.cur_cmd_interactive = false;
+	pset.mode_mysql = false;
 	pset.encoding = PQenv2encoding();
 
 	pset.vars = CreateVariableSpace();
@@ -330,6 +331,7 @@
 		{"host", required_argument, NULL, 'h'},
 		{"html", no_argument, NULL, 'H'},
 		{"list", no_argument, NULL, 'l'},
+		{"mysql", no_argument, NULL, 'm'},
 		{"no-readline", no_argument, NULL, 'n'},
 		{"output", required_argument, NULL, 'o'},
 		{"port", required_argument, NULL, 'p'},
@@ -359,7 +361,7 @@
 
 	memset(options, 0, sizeof *options);
 
-	while ((c = getopt_long(argc, argv, "aAc:d:eEf:F:h:Hlno:p:P:qR:sStT:uU:v:VWxX?",
+	while ((c = getopt_long(argc, argv, "aAc:d:eEf:F:h:Hlmno:p:P:qR:sStT:uU:v:VWxX?",
 							long_options, &optindex)) != -1)
 	{
 		switch (c)
@@ -404,6 +406,9 @@
 				break;
 			case 'l':
 				options->action = ACT_LIST_DB;
+				break;
+			case 'm':
+				pset.mode_mysql = true;
 				break;
 			case 'n':
 				options->no_readline = true;
Index: tab-complete.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/bin/psql/tab-complete.c,v
retrieving revision 1.76
diff -u -r1.76 tab-complete.c
--- tab-complete.c	2003/04/03 20:18:16	1.76
+++ tab-complete.c	2003/04/16 15:37:55
@@ -85,8 +85,11 @@
 static char *complete_from_list(const char *text, int state);
 
 static PGresult *exec_query(char *query);
+static bool find_word_in_list(char **word_list, int *valid_indexes, char *word);
 char	   *quote_file_name(char *text, int match_type, char *quote_pointer);
 
+static char **merge_lists(char **list1, char **list2);
+
 /*static char * dequote_file_name(char *text, char quote_char);*/
 static char *previous_word(int point, int skip);
 
@@ -435,7 +438,178 @@
 	{NULL, NO_SCHEMA, NULL}				/* end of list */
 };
 
+char *sql_commands[] = {
+	"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT",
+	"COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", "DELETE", "DROP", "EXECUTE",
+	"EXPLAIN", "FETCH", "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY",
+	"PREPARE", "REINDEX", "RESET", "REVOKE", "ROLLBACK", "SELECT", "SET", "SHOW",
+	"TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL
+};
+
+char **mysql_mode_show_merged = NULL;
+
+char *mysql_mode_show_words[] = {
+	"AGGREGATES", "CASTS", "CATALOGS", "COLUMNS", "COMMENTS", "CONVERSIONS", "DATABASES",
+	"DOMAINS", "FUNCTIONS", "HELP", "INDEXES", "LARGEOBJECTS", "OPERATORS", "PRIVILEGES",
+	"PROCESSLIST", "SCHEMAS", "SEQUENCES", "STATUS", "TABLES", "TYPES", "USERS",
+	"VARIABLES", "VIEWS", NULL
+};
+
+int mysql_mode_show_valid_like_commands[] = {
+  0, 2, 4, 5, 7, 8, 10, 12, 13, 15, 16, 18, 19, 20, 22, -1
+};
 
+char *mysql_mode_show_select_origin_words[] = {
+	"FROM", "LIKE", NULL
+};
+
+int mysql_mode_show_valid_from_like[] = {
+  10, -1
+};
+
+char *pgsql_variables[] = {
+	/* these SET arguments are known in gram.y */
+	"CONSTRAINTS",
+	"NAMES",
+	"SESSION",
+	"TRANSACTION",
+
+	/*
+	 * the rest should match USERSET and possibly SUSET entries in
+	 * backend/utils/misc/guc.c.
+	 */
+	"australian_timezones",
+	"authentication_timeout",
+	"autocommit",
+	"checkpoint_segments",
+	"checkpoint_timeout",
+	"checkpoint_warning",
+	"client_encoding",
+	"client_min_messages",
+	"commit_delay",
+	"commit_siblings",
+	"cpu_index_tuple_cost",
+	"cpu_operator_cost",
+	"cpu_tuple_cost",
+	"DateStyle",
+	"db_user_namespace",
+	"deadlock_timeout",
+#ifdef USE_ASSERT_CHECKING
+	"debug_assertions",
+#endif
+	"debug_pretty_print",
+	"debug_print_parse",
+	"debug_print_plan",
+	"debug_print_rewritten",
+#ifdef LOCK_DEBUG
+	"debug_deadlocks",
+#endif
+	"default_statistics_target",
+	"default_transaction_isolation",
+	"default_transaction_read_only",
+	"dynamic_library_path",
+	"effective_cache_size",
+	"enable_hashagg",
+	"enable_hashjoin",
+	"enable_indexscan",
+	"enable_mergejoin",
+	"enable_nestloop",
+	"enable_seqscan",
+	"enable_sort",
+	"enable_tidscan",
+	"explain_pretty_print",
+	"extra_float_digits",
+	"from_collapse_limit",
+	"fsync",
+	"geqo",
+	"geqo_effort",
+	"geqo_generations",
+	"geqo_pool_size",
+	"geqo_random_seed",
+	"geqo_selection_bias",
+	"geqo_threshold",
+	"join_collapse_limit",
+	"krb_server_keyfile",
+	"lc_messages",
+	"lc_monetary",
+	"lc_numeric",
+	"lc_time",
+#ifdef BTREE_BUILD_STATS
+	"log_btree_build_stats",
+#endif
+	"log_connections",
+	"log_duration",
+	"log_executor_stats",
+	"log_hostname",
+	"log_min_error_statement",
+	"log_min_messages",
+	"log_parser_stats",
+	"log_pid",
+	"log_planner_stats",
+	"log_source_port",
+	"log_statement",
+	"log_statement_stats",
+	"log_timestamp",
+	"max_connections",
+	"max_expr_depth",
+	"max_files_per_process",
+	"max_fsm_pages",
+	"max_fsm_relations",
+	"max_locks_per_transaction",
+	"password_encryption",
+	"pre_auth_delay",
+	"preload_libraries",
+	"port",
+	"random_page_cost",
+	"regex_flavor",
+	"search_path",
+	"server_encoding",
+	"silent_mode",
+	"shared_buffers",
+	"seed",
+	"server_encoding",
+	"session_authorization",
+	"shared_buffers",
+	"sort_mem",
+	"sql_inheritance",
+	"ssl",
+	"statement_timeout",
+	"stats_block_level",
+	"stats_command_string",
+	"stats_reset_on_server_start",
+	"stats_row_level",
+	"stats_start_collector",
+	"superuser_reserved_connections",
+#ifdef HAVE_SYSLOG
+	"syslog",
+	"syslog_facility",
+	"syslog_ident",
+#endif
+	"tcpip_socket",
+	"TimeZone",
+	"trace_notify",
+#ifdef LOCK_DEBUG
+	"trace_lock_oidmin",
+	"trace_lock_table",
+	"trace_locks",
+	"trace_lwlocks",
+	"trace_userlocks",
+#endif
+	"transaction_isolation",
+	"transaction_read_only",
+	"transform_null_equals",
+	"unix_socket_directory",
+	"unix_socket_group",
+	"unix_socket_permissions",
+	"vacuum_mem",
+	"virtual_host",
+	"wal_buffers",
+	"wal_debug",
+	"wal_sync_method",
+	"zero_damaged_pages",
+	NULL
+};
+
 /* A couple of macros to ease typing. You can use these to complete the given
    string with
    1) The results from a query you pass it. (Perhaps one of those above?)
@@ -473,123 +647,10 @@
 			   *prev3_wd,
 			   *prev4_wd;
 
-	static char *sql_commands[] = {
-		"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT",
-		"COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", "DELETE", "DROP", "EXECUTE",
-		"EXPLAIN", "FETCH", "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY",
-		"PREPARE", "REINDEX", "RESET", "REVOKE", "ROLLBACK", "SELECT", "SET", "SHOW",
-		"TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL
-	};
-
-	static char *pgsql_variables[] = {
-		/* these SET arguments are known in gram.y */
-		"CONSTRAINTS",
-		"NAMES",
-		"SESSION",
-		"TRANSACTION",
-
-		/*
-		 * the rest should match USERSET and possibly SUSET entries in
-		 * backend/utils/misc/guc.c.
-		 */
-		"australian_timezones",
-		"autocommit",
-		"client_encoding",
-		"client_min_messages",
-		"commit_delay",
-		"commit_siblings",
-		"cpu_index_tuple_cost",
-		"cpu_operator_cost",
-		"cpu_tuple_cost",
-		"DateStyle",
-		"deadlock_timeout",
-		"debug_pretty_print",
-		"debug_print_parse",
-		"debug_print_plan",
-		"debug_print_rewritten",
-		"default_statistics_target",
-		"default_transaction_isolation",
-		"default_transaction_read_only",
-		"dynamic_library_path",
-		"effective_cache_size",
-		"enable_hashagg",
-		"enable_hashjoin",
-		"enable_indexscan",
-		"enable_mergejoin",
-		"enable_nestloop",
-		"enable_seqscan",
-		"enable_sort",
-		"enable_tidscan",
-		"explain_pretty_print",
-		"extra_float_digits",
-		"from_collapse_limit",
-		"fsync",
-		"geqo",
-		"geqo_effort",
-		"geqo_generations",
-		"geqo_pool_size",
-		"geqo_random_seed",
-		"geqo_selection_bias",
-		"geqo_threshold",
-		"join_collapse_limit",
-		"krb_server_keyfile",
-		"lc_messages",
-		"lc_monetary",
-		"lc_numeric",
-		"lc_time",
-		"log_duration",
-		"log_executor_stats",
-		"log_min_error_statement",
-		"log_min_messages",
-		"log_parser_stats",
-		"log_planner_stats",
-		"log_statement",
-		"log_statement_stats",
-		"max_connections",
-		"max_expr_depth",
-		"max_files_per_process",
-		"max_fsm_pages",
-		"max_fsm_relations",
-		"max_locks_per_transaction",
-		"password_encryption",
-		"port",
-		"random_page_cost",
-		"regex_flavor",
-		"search_path",
-		"shared_buffers",
-		"seed",
-		"server_encoding",
-		"sort_mem",
-		"sql_inheritance",
-		"ssl",
-		"statement_timeout",
-		"stats_block_level",
-		"stats_command_string",
-		"stats_reset_on_server_start",
-		"stats_row_level",
-		"stats_start_collector",
-		"superuser_reserved_connections",
-		"syslog",
-		"syslog_facility",
-		"syslog_ident",
-		"tcpip_socket",
-		"TimeZone",
-		"trace_notify",
-		"transform_null_equals",
-		"unix_socket_directory",
-		"unix_socket_group",
-		"unix_socket_permissions",
-		"vacuum_mem",
-		"wal_buffers",
-		"wal_debug",
-		"wal_sync_method",
-		NULL
-	};
-
 	static char *backslash_commands[] = {
-		"\\a", "\\connect", "\\C", "\\cd", "\\copy", "\\copyright", 
+		"\\a", "\\connect", "\\C", "\\cd", "\\copy", "\\copyright",
 		"\\d",  "\\da", "\\dc", "\\dC", "\\dd", "\\dD", "\\df", "\\di",
-		"\\dl", "\\dn", "\\do", "\\dp", "\\ds", "\\dS", "\\dt", "\\dT", 
+		"\\dl", "\\dn", "\\do", "\\dp", "\\ds", "\\dS", "\\dt", "\\dT",
 		"\\dv", "\\du",
 		"\\e", "\\echo", "\\encoding",
 		"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
@@ -630,13 +691,13 @@
 
 /* CREATE or DROP but not ALTER TABLE sth DROP */
 	/* complete with something you can create or drop */
-	else if (strcasecmp(prev_wd, "CREATE") == 0 || 
+	else if (strcasecmp(prev_wd, "CREATE") == 0 ||
 			 (strcasecmp(prev_wd, "DROP") == 0 &&
 			  strcasecmp(prev3_wd,"TABLE") != 0 ))
         matches = completion_matches(text, create_command_generator);
 
 /* ALTER */
-    /* complete with what you can alter (TABLE, GROUP, USER, ...) 
+    /* complete with what you can alter (TABLE, GROUP, USER, ...)
      * unless we're in ALTER TABLE sth ALTER*/
     else if (strcasecmp(prev_wd, "ALTER") == 0  &&
 			 strcasecmp(prev3_wd, "TABLE") != 0 )
@@ -695,7 +756,7 @@
 	}
 	/* If we have TABLE <sth> DROP COLUMN, provide list of columns */
 	else if (strcasecmp(prev4_wd, "TABLE") == 0 &&
-			 strcasecmp(prev2_wd, "DROP") == 0 && 
+			 strcasecmp(prev2_wd, "DROP") == 0 &&
 			 strcasecmp(prev_wd, "COLUMN") == 0)
 		COMPLETE_WITH_ATTR(prev3_wd);
 
@@ -983,7 +1044,7 @@
                             "SELECT 'SCHEMA' AS relname ");
 
 	/* Complete "GRANT/REVOKE * ON * " with "TO" */
-	else if ((strcasecmp(prev4_wd, "GRANT") == 0 || 
+	else if ((strcasecmp(prev4_wd, "GRANT") == 0 ||
 			  strcasecmp(prev4_wd, "REVOKE") == 0) &&
 			 strcasecmp(prev2_wd, "ON") == 0)
 	{
@@ -1102,6 +1163,42 @@
 
 /* SET, RESET, SHOW */
 	/* Complete with a variable name */
+	else if (pset.mode_mysql == true &&
+		 (strcasecmp(prev3_wd, "SHOW") == 0 &&
+		  find_word_in_list(mysql_mode_show_words, mysql_mode_show_valid_like_commands, prev2_wd) == true  &&
+		  strncasecmp(prev_wd, "LIKE", 4) == 0))
+	{
+		if (strncasecmp(prev2_wd, "INDEX", 5) == 0)
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
+		else
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+	}
+	else if (pset.mode_mysql == true && (strcasecmp(prev2_wd, "SHOW") == 0 &&
+					     find_word_in_list(mysql_mode_show_words, mysql_mode_show_valid_from_like, prev_wd) == true))
+		COMPLETE_WITH_LIST(mysql_mode_show_select_origin_words);
+	else if (pset.mode_mysql == true && (strcasecmp(prev2_wd, "SHOW") == 0 &&
+					     find_word_in_list(mysql_mode_show_words, mysql_mode_show_valid_like_commands, prev_wd) == true))
+		COMPLETE_WITH_CONST("LIKE");
+	else if (pset.mode_mysql == true && (strcasecmp(prev2_wd, "SHOW") == 0 &&
+					     strcasecmp(prev_wd, "COLUMNS") == 0))
+		COMPLETE_WITH_CONST("FROM");
+	else if (pset.mode_mysql == true && (strcasecmp(prev2_wd, "SHOW") == 0 &&
+					     strcasecmp(prev_wd, "HELP") == 0))
+		COMPLETE_WITH_CONST("ON");
+	else if (pset.mode_mysql == true && (strcasecmp(prev3_wd, "SHOW") == 0 &&
+					     strcasecmp(prev2_wd, "HELP") == 0 &&
+					     strcasecmp(prev_wd, "ON") == 0))
+		COMPLETE_WITH_LIST(sql_commands);
+	else if (pset.mode_mysql == true && strcasecmp(prev_wd, "SHOW") == 0)
+	{
+		if (mysql_mode_show_merged == NULL)
+			mysql_mode_show_merged = merge_lists(mysql_mode_show_words, pgsql_variables);
+
+		if (mysql_mode_show_merged != NULL)
+			COMPLETE_WITH_LIST(mysql_mode_show_merged);
+		else
+			COMPLETE_WITH_LIST(mysql_mode_show_words);
+	}
 	else if ((strcasecmp(prev_wd, "SET") == 0 &&
 			  strcasecmp(prev3_wd, "UPDATE") != 0) ||
 			 strcasecmp(prev_wd, "RESET") == 0 ||
@@ -1399,9 +1496,9 @@
 /* This creates a list of matching things, according to a query pointed to
    by completion_charp.
    The query can be one of two kinds:
-   - A simple query which must contain a %d and a %s, which will be replaced 
+   - A simple query which must contain a %d and a %s, which will be replaced
    by the string length of the text and the text itself. The query may also
-   have another %s in it, which will be replaced by the value of 
+   have another %s in it, which will be replaced by the value of
    completion_info_charp.
      or:
    - A schema query used for completion of both schema and relation names;
@@ -1658,6 +1755,46 @@
 	return s;
 }
 
+
+
+static bool
+find_word_in_list(char **word_list, int *valid_indexes, char *word)
+{
+  size_t i;
+
+  for (i = 0; valid_indexes[i] != -1; i++)
+    if (strcasecmp(word_list[valid_indexes[i]], word) == 0)
+      return(true);
+
+  return(false);
+}
+
+static char **
+merge_lists(char **list1, char **list2)
+{
+  size_t i, list1_len, list2_len;
+  char *p;
+  char **newlist;
+
+  for (list1_len = 0; list1[list1_len] != NULL; list1_len++)
+    p = list1[list1_len];
+
+  for (list2_len = 0; list2[list2_len] != NULL; list2_len++)
+    p = list2[list2_len];
+
+  newlist = malloc(sizeof(char *) * (list1_len + list2_len));
+  if (newlist == NULL)
+    return(NULL);
+
+  for (list1_len = 0, i = 0; list1[list1_len] != NULL; list1_len++, i++)
+    newlist[i] = list1[list1_len];
+
+  for (list2_len = 0; list2[list2_len] != NULL; list2_len++, i++)
+    newlist[i] = list2[list2_len];
+
+  newlist[i] = NULL;
+  return(newlist);
+}
 
 
 #if 0