diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 4c468a8..0aba07c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -303,6 +303,57 @@ static const SchemaQuery Query_for_list_of_tables = { NULL }; +/* The bit masks for the following three functions come from + * src/include/catalog/pg_trigger.h. + */ +static const SchemaQuery Query_for_list_of_insertables = { + /* catname */ + "pg_catalog.pg_class c", + /* selcondition */ + "c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS " + "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype | (1 << 2) = t.tgtype))", + /* viscondition */ + "pg_catalog.pg_table_is_visible(c.oid)", + /* namespace */ + "c.relnamespace", + /* result */ + "pg_catalog.quote_ident(c.relname)", + /* qualresult */ + NULL +}; + +static const SchemaQuery Query_for_list_of_deletables = { + /* catname */ + "pg_catalog.pg_class c", + /* selcondition */ + "c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS " + "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype | (1 << 3) = t.tgtype))", + /* viscondition */ + "pg_catalog.pg_table_is_visible(c.oid)", + /* namespace */ + "c.relnamespace", + /* result */ + "pg_catalog.quote_ident(c.relname)", + /* qualresult */ + NULL +}; + +static const SchemaQuery Query_for_list_of_updatables = { + /* catname */ + "pg_catalog.pg_class c", + /* selcondition */ + "c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS " + "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype | (1 << 4) = t.tgtype))", + /* viscondition */ + "pg_catalog.pg_table_is_visible(c.oid)", + /* namespace */ + "c.relnamespace", + /* result */ + "pg_catalog.quote_ident(c.relname)", + /* qualresult */ + NULL +}; + static const SchemaQuery Query_for_list_of_tisv = { /* catname */ "pg_catalog.pg_class c", @@ -333,6 +384,21 @@ static const SchemaQuery Query_for_list_of_tsv = { NULL }; +static const SchemaQuery Query_for_list_of_tv = { + /* catname */ + "pg_catalog.pg_class c", + /* selcondition */ + "c.relkind IN ('r', 'v')", + /* viscondition */ + "pg_catalog.pg_table_is_visible(c.oid)", + /* namespace */ + "c.relnamespace", + /* result */ + "pg_catalog.quote_ident(c.relname)", + /* qualresult */ + NULL +}; + static const SchemaQuery Query_for_list_of_views = { /* catname */ "pg_catalog.pg_class c", @@ -630,7 +696,8 @@ psql_completion(char *text, int start, int end) *prev2_wd, *prev3_wd, *prev4_wd, - *prev5_wd; + *prev5_wd, + *prev6_wd; static const char *const sql_commands[] = { "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", @@ -669,7 +736,7 @@ psql_completion(char *text, int start, int end) completion_info_charp2 = NULL; /* - * Scan the input line before our current position for the last five + * Scan the input line before our current position for the last six * words. According to those we'll make some smart decisions on what the * user is probably intending to type. TODO: Use strtokx() to do this. */ @@ -678,6 +745,7 @@ psql_completion(char *text, int start, int end) prev3_wd = previous_word(start, 2); prev4_wd = previous_word(start, 3); prev5_wd = previous_word(start, 4); + prev6_wd = previous_word(start, 5); /* If a backslash command was started, continue */ if (text[0] == '\\') @@ -974,14 +1042,6 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger); } - /* - * If we have ALTER TRIGGER ON, then add the correct tablename - */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - /* ALTER TRIGGER ON */ else if (pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && pg_strcasecmp(prev2_wd, "ON") == 0) @@ -1593,7 +1653,7 @@ psql_completion(char *text, int start, int end) else if (pg_strcasecmp(prev4_wd, "AS") == 0 && pg_strcasecmp(prev3_wd, "ON") == 0 && pg_strcasecmp(prev_wd, "TO") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tv, NULL); /* CREATE SERVER */ else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && @@ -1655,15 +1715,20 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev2_wd, "TRIGGER") == 0) { static const char *const list_CREATETRIGGER[] = - {"BEFORE", "AFTER", NULL}; + {"BEFORE", "AFTER", "INSTEAD OF", NULL}; COMPLETE_WITH_LIST(list_CREATETRIGGER); } /* complete CREATE TRIGGER BEFORE,AFTER with an event */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && + else if ((pg_strcasecmp(prev4_wd, "CREATE") == 0 && pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && (pg_strcasecmp(prev_wd, "BEFORE") == 0 || - pg_strcasecmp(prev_wd, "AFTER") == 0)) + pg_strcasecmp(prev_wd, "AFTER") == 0)) || + (pg_strcasecmp(prev5_wd, "CREATE") == 0 && + pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && + pg_strcasecmp(prev2_wd, "INSTEAD") == 0 && + pg_strcasecmp(prev_wd, "OF") == 0)) + { static const char *const list_CREATETRIGGER_EVENTS[] = {"INSERT", "DELETE", "UPDATE", "TRUNCATE", NULL}; @@ -1671,10 +1736,15 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS); } /* complete CREATE TRIGGER BEFORE,AFTER sth with OR,ON */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && + else if ( + (pg_strcasecmp(prev5_wd, "CREATE") == 0 && pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && (pg_strcasecmp(prev2_wd, "BEFORE") == 0 || - pg_strcasecmp(prev2_wd, "AFTER") == 0)) + pg_strcasecmp(prev2_wd, "AFTER") == 0)) || + (pg_strcasecmp(prev6_wd, "CREATE") == 0 && + pg_strcasecmp(prev5_wd, "TRIGGER") == 0 && + (pg_strcasecmp(prev3_wd, "INSTEAD") == 0 && + (pg_strcasecmp(prev2_wd, "OF") == 0)))) { static const char *const list_CREATETRIGGER2[] = {"ON", "OR", NULL}; @@ -1686,11 +1756,14 @@ psql_completion(char *text, int start, int end) * complete CREATE TRIGGER BEFORE,AFTER event ON with a list of * tables */ - else if (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 && + else if ((pg_strcasecmp(prev5_wd, "TRIGGER") == 0 && (pg_strcasecmp(prev3_wd, "BEFORE") == 0 || - pg_strcasecmp(prev3_wd, "AFTER") == 0) && + pg_strcasecmp(prev3_wd, "AFTER") == 0)) || + (pg_strcasecmp(prev6_wd, "TRIGGER") == 0 && + pg_strcasecmp(prev4_wd, "INSTEAD") == 0 && + pg_strcasecmp(prev3_wd, "OF") == 0) && pg_strcasecmp(prev_wd, "ON") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tv, NULL); /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */ else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0) COMPLETE_WITH_CONST("PROCEDURE"); @@ -1778,7 +1851,7 @@ psql_completion(char *text, int start, int end) /* Complete DELETE FROM with a list of tables */ else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 && pg_strcasecmp(prev_wd, "FROM") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_deletables, NULL); /* Complete DELETE FROM */ else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 && pg_strcasecmp(prev2_wd, "FROM") == 0) @@ -2066,7 +2139,7 @@ psql_completion(char *text, int start, int end) /* Complete INSERT INTO with table names */ else if (pg_strcasecmp(prev2_wd, "INSERT") == 0 && pg_strcasecmp(prev_wd, "INTO") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_insertables, NULL); /* Complete "INSERT INTO
(" with attribute names */ else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 && pg_strcasecmp(prev3_wd, "INTO") == 0 && @@ -2423,7 +2496,7 @@ psql_completion(char *text, int start, int end) /* UPDATE */ /* If prev. word is UPDATE suggest a list of tables */ else if (pg_strcasecmp(prev_wd, "UPDATE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); /* Complete UPDATE
with "SET" */ else if (pg_strcasecmp(prev2_wd, "UPDATE") == 0) COMPLETE_WITH_CONST("SET");