Skip to content

Commit c4e63f7

Browse files
committed
Added new commands and intelligence to psql tab completion.
1 parent c7558b3 commit c4e63f7

File tree

1 file changed

+130
-50
lines changed

1 file changed

+130
-50
lines changed

src/bin/psql/tab-complete.c

Lines changed: 130 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,44 @@
33
*
44
* Copyright 2000 by PostgreSQL Global Development Group
55
*
6-
* $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.19 2000/05/05 08:44:27 petere Exp $
6+
* $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.20 2000/06/25 14:25:51 petere Exp $
77
*/
88

9-
/*-----------
10-
This file implements a somewhat more sophisticated readline "TAB completion"
11-
in psql. It is not intended to be AI, to replace learning SQL, or to relieve
12-
you from thinking about what you're doing. Also it does not always give you
13-
all the syntactically legal completions, only those that are the most common
14-
or the ones that the programmer felt most like implementing.
15-
16-
CAVEAT: Tab completion causes queries to be sent to the backend. The number
17-
tuples returned gets limited, in most default installations to 101, but if
18-
you still don't like this prospect, you can turn off tab completion in your
19-
~/.inputrc (or else ${INPUTRC}) file so:
20-
$if psql
21-
TAB: self-insert
22-
$endif
23-
See `man 3 readline` or `info readline` for the full details. Also, hence the
24-
25-
BUGS:
26-
* If you split your queries across lines, this whole things gets confused.
27-
(To fix this, one would have to read psql's query buffer rather than
28-
readline's line buffer, which would require some major revisions of
29-
things.)
30-
* Table or attribute names with spaces in it will equally confuse it.
31-
* Quotes, parenthesis, and other funny characters are not handled all that
32-
gracefully.
33-
-------------*/
9+
/*----------------------------------------------------------------------
10+
* This file implements a somewhat more sophisticated readline "TAB
11+
* completion" in psql. It is not intended to be AI, to replace
12+
* learning SQL, or to relieve you from thinking about what you're
13+
* doing. Also it does not always give you all the syntactically legal
14+
* completions, only those that are the most common or the ones that
15+
* the programmer felt most like implementing.
16+
*
17+
* CAVEAT: Tab completion causes queries to be sent to the backend.
18+
* The number tuples returned gets limited, in most default
19+
* installations to 101, but if you still don't like this prospect,
20+
* you can turn off tab completion in your ~/.inputrc (or else
21+
* ${INPUTRC}) file so:
22+
*
23+
* $if psql
24+
* set disable-completion on
25+
* $endif
26+
*
27+
* See `man 3 readline' or `info readline' for the full details. Also,
28+
* hence the
29+
*
30+
* BUGS:
31+
*
32+
* - If you split your queries across lines, this whole things gets
33+
* confused. (To fix this, one would have to read psql's query
34+
* buffer rather than readline's line buffer, which would require
35+
* some major revisions of things.)
36+
*
37+
* - Table or attribute names with spaces in it will equally confuse
38+
* it.
39+
*
40+
* - Quotes, parenthesis, and other funny characters are not handled
41+
* all that gracefully.
42+
*----------------------------------------------------------------------
43+
*/
3444

3545
#include "postgres.h"
3646
#include "tab-complete.h"
@@ -120,6 +130,7 @@ pgsql_thing_t words_after_create[] = {
120130
{"AGGREGATE", "SELECT distinct aggname FROM pg_aggregate WHERE substr(aggname,1,%d)='%s'"},
121131
{"DATABASE", "SELECT datname FROM pg_database WHERE substr(datname,1,%d)='%s'"},
122132
{"FUNCTION", "SELECT distinct proname FROM pg_proc WHERE substr(proname,1,%d)='%s'"},
133+
{"GROUP", "SELECT groname FROM pg_group WHERE substr(groname,1,%d)='%s'"},
123134
{"INDEX", "SELECT relname FROM pg_class WHERE relkind='i' and substr(relname,1,%d)='%s'"},
124135
{"OPERATOR", NULL}, /* Querying for this is probably not such
125136
* a good idea. */
@@ -138,11 +149,11 @@ pgsql_thing_t words_after_create[] = {
138149

139150
/* The query to get a list of tables and a list of indexes, which are used at
140151
various places. */
141-
#define Query_for_list_of_tables words_after_create[7].query
142-
#define Query_for_list_of_indexes words_after_create[3].query
152+
#define Query_for_list_of_tables words_after_create[8].query
153+
#define Query_for_list_of_indexes words_after_create[4].query
143154
#define Query_for_list_of_databases words_after_create[1].query
144155
#define Query_for_list_of_attributes "SELECT a.attname FROM pg_attribute a, pg_class c WHERE c.oid = a.attrelid and a.attnum>0 and substr(a.attname,1,%d)='%s' and c.relname='%s'"
145-
156+
#define Query_for_list_of_users words_after_create[13].query
146157

147158
/* A couple of macros to ease typing. You can use these to complete the given
148159
string with
@@ -179,39 +190,45 @@ psql_completion(char *text, int start, int end)
179190
*prev4_wd;
180191

181192
static char *sql_commands[] = {
182-
"ABORT", "ALTER", "BEGIN", "CLOSE", "CLUSTER", "COMMIT", "COPY",
193+
"ABORT", "ALTER", "BEGIN", "CLOSE", "CLUSTER", "COMMENT", "COMMIT", "COPY",
183194
"CREATE", "DECLARE", "DELETE", "DROP", "EXPLAIN", "FETCH", "GRANT",
184195
"INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "RESET",
185-
"REVOKE", "ROLLBACK", "SELECT", "SET", "SHOW", "UNLISTEN", "UPDATE",
196+
"REVOKE", "ROLLBACK", "SELECT", "SET", "SHOW", "TRUNCATE", "UNLISTEN", "UPDATE",
186197
"VACUUM", NULL
187198
};
188199

189200
static char *pgsql_variables[] = {
190201
/* these SET arguments are known in gram.y */
191-
"TRANSACTION ISOLATION LEVEL",
202+
"CONSTRAINTS",
192203
"NAMES",
193-
/* rest should match table in src/backend/commands/variable.c */
204+
"TRANSACTION ISOLATION LEVEL",
205+
/* these are treated in backend/commands/variable.c */
194206
"DateStyle",
195207
"TimeZone",
196-
"effective_cache_size",
197-
"random_page_cost",
198-
"cpu_tuple_cost",
199-
"cpu_index_tuple_cost",
200-
"cpu_operator_cost",
208+
"client_encoding",
209+
"server_encoding",
210+
"random_seed",
211+
/* the rest should match USERSET and SUSET entries in
212+
* backend/utils/misc/guc.c, but feel free to leave out the
213+
* esoteric or debug settings */
201214
"enable_seqscan",
202215
"enable_indexscan",
203216
"enable_tidscan",
204217
"enable_sort",
205218
"enable_nestloop",
206219
"enable_mergejoin",
207220
"enable_hashjoin",
208-
"GEQO",
209-
"client_encoding",
210-
"server_encoding",
211-
"KSQO",
221+
"geqo",
222+
"ksqo",
223+
"sql_inheritance",
224+
"sort_mem",
225+
"debug_level",
212226
"max_expr_depth",
213-
"XactIsoLevel",
214-
"PG_Options",
227+
"effective_cache_size",
228+
"random_page_cost",
229+
"cpu_tuple_cost",
230+
"cpu_index_tuple_cost",
231+
"cpu_operator_cost",
215232
NULL
216233
};
217234

@@ -260,25 +277,41 @@ psql_completion(char *text, int start, int end)
260277
matches = completion_matches(text, create_command_generator);
261278

262279
/* ALTER */
263-
/* complete with what you can alter (TABLE or USER) */
280+
/* complete with what you can alter (TABLE, GROUP, USER) */
264281
else if (strcasecmp(prev_wd, "ALTER") == 0)
265282
{
266-
char *list_ALTER[] = {"TABLE", "USER", NULL};
283+
char *list_ALTER[] = {"GROUP", "TABLE", "USER", NULL};
267284

268285
COMPLETE_WITH_LIST(list_ALTER);
269286
}
270-
/* If we detect ALTER TABLE <name>, suggest either "ADD" or "RENAME" */
287+
/* If we detect ALTER TABLE <name>, suggest either ADD, ALTER, or RENAME */
271288
else if (strcasecmp(prev3_wd, "ALTER") == 0 && strcasecmp(prev2_wd, "TABLE") == 0)
272289
{
273-
char *list_ALTER2[] = {"ADD", "RENAME", NULL};
290+
char *list_ALTER2[] = {"ADD", "ALTER", "RENAME", NULL};
274291

275292
COMPLETE_WITH_LIST(list_ALTER2);
276293
}
277-
/* If we have TABLE <sth> ADD|RENAME, provide list of columns */
294+
/* If we have TABLE <sth> ALTER|RENAME, provide list of columns */
278295
else if (strcasecmp(prev3_wd, "TABLE") == 0 &&
279-
(strcasecmp(prev_wd, "ADD") == 0 || strcasecmp(prev_wd, "RENAME") == 0))
296+
(strcasecmp(prev_wd, "ALTER") == 0 || strcasecmp(prev_wd, "RENAME") == 0))
280297
COMPLETE_WITH_ATTR(prev2_wd);
281298

299+
/* complete ALTER GROUP <foo> with ADD or DROP */
300+
else if (strcasecmp(prev3_wd, "ALTER") == 0 && strcasecmp(prev2_wd, "GROUP") == 0)
301+
{
302+
char *list_ALTERGROUP[] = {"ADD", "DROP", NULL};
303+
COMPLETE_WITH_LIST(list_ALTERGROUP);
304+
}
305+
/* complete ALTER GROUP <foo> ADD|DROP with USER */
306+
else if (strcasecmp(prev4_wd, "ALTER") == 0 && strcasecmp(prev3_wd, "GROUP") == 0
307+
&& (strcasecmp(prev_wd, "ADD")==0 || strcasecmp(prev_wd, "DROP")==0))
308+
COMPLETE_WITH_CONST("USER");
309+
/* complete {ALTER} GROUP <foo> ADD|DROP USER with a user name */
310+
else if (strcasecmp(prev4_wd, "GROUP") == 0
311+
&& (strcasecmp(prev2_wd, "ADD")==0 || strcasecmp(prev2_wd, "DROP")==0)
312+
&& strcasecmp(prev_wd, "USER") == 0)
313+
COMPLETE_WITH_QUERY(Query_for_list_of_users);
314+
282315
/* CLUSTER */
283316
/* If the previous word is CLUSTER, produce list of indexes. */
284317
else if (strcasecmp(prev_wd, "CLUSTER") == 0)
@@ -304,6 +337,19 @@ psql_completion(char *text, int start, int end)
304337
COMPLETE_WITH_QUERY(query_buffer);
305338
}
306339

340+
/* COMMENT */
341+
else if (strcasecmp(prev_wd, "COMMENT")==0)
342+
COMPLETE_WITH_CONST("ON");
343+
else if (strcasecmp(prev2_wd, "COMMENT")==0 && strcasecmp(prev_wd, "ON")==0)
344+
{
345+
char * list_COMMENT[] =
346+
{"DATABASE", "INDEX", "RULE", "SEQUENCE", "TABLE", "TYPE", "VIEW",
347+
"COLUMN", "AGGREGATE", "FUNCTION", "OPERATOR", "TRIGGER", NULL};
348+
COMPLETE_WITH_LIST(list_COMMENT);
349+
}
350+
else if (strcasecmp(prev4_wd, "COMMENT")==0 && strcasecmp(prev3_wd, "ON")==0)
351+
COMPLETE_WITH_CONST("IS");
352+
307353
/* COPY */
308354

309355
/*
@@ -510,6 +556,26 @@ psql_completion(char *text, int start, int end)
510556
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
511557
/* (If you want more with LOCK, you better think about it yourself.) */
512558

559+
/* NOTIFY */
560+
else if (strcasecmp(prev_wd, "NOTIFY")==0)
561+
COMPLETE_WITH_QUERY("SELECT relname FROM pg_listener WHERE substr(relname,1,%d)='%s'");
562+
563+
/* REINDEX */
564+
else if (strcasecmp(prev_wd, "REINDEX") == 0)
565+
{
566+
char *list_REINDEX[] = {"TABLE", "DATABASE", "INDEX", NULL};
567+
COMPLETE_WITH_LIST(list_REINDEX);
568+
}
569+
else if (strcasecmp(prev2_wd, "REINDEX")==0)
570+
{
571+
if (strcasecmp(prev_wd, "TABLE")==0)
572+
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
573+
else if (strcasecmp(prev_wd, "DATABASE")==0)
574+
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
575+
else if (strcasecmp(prev_wd, "INDEX")==0)
576+
COMPLETE_WITH_QUERY(Query_for_list_of_indexes);
577+
}
578+
513579
/* SELECT */
514580
/* naah . . . */
515581

@@ -540,6 +606,12 @@ psql_completion(char *text, int start, int end)
540606
strcasecmp(prev2_wd, "LEVEL") == 0 &&
541607
strcasecmp(prev_wd, "READ") == 0)
542608
COMPLETE_WITH_CONST("COMMITTED");
609+
/* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
610+
else if (strcasecmp(prev3_wd, "SET")==0 && strcasecmp(prev2_wd, "CONSTRAINTS")==0)
611+
{
612+
char *constraint_list[] = {"DEFERRED", "IMMEDIATE", NULL};
613+
COMPLETE_WITH_LIST(constraint_list);
614+
}
543615
/* Complete SET <var> with "TO" */
544616
else if (strcasecmp(prev2_wd, "SET") == 0 &&
545617
strcasecmp(prev4_wd, "UPDATE") != 0)
@@ -568,6 +640,14 @@ psql_completion(char *text, int start, int end)
568640
}
569641
}
570642

643+
/* TRUNCATE */
644+
else if (strcasecmp(prev_wd, "TRUNCATE")==0)
645+
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
646+
647+
/* UNLISTEN */
648+
else if (strcasecmp(prev_wd, "UNLISTEN")==0)
649+
COMPLETE_WITH_QUERY("SELECT relname FROM pg_listener WHERE substr(relname,1,%d)='%s' UNION SELECT '*'::text");
650+
571651
/* UPDATE */
572652
/* If prev. word is UPDATE suggest a list of tables */
573653
else if (strcasecmp(prev_wd, "UPDATE") == 0)

0 commit comments

Comments
 (0)