From 145c9d04f807ebd4f85601420b21535657076db6 Mon Sep 17 00:00:00 2001 From: Alexander Polyakov Date: Tue, 5 Apr 2016 17:09:57 +0400 Subject: [PATCH] Basic tab-completion for policies --- pgadmin/ctl/ctlSQLBox.cpp | 6 +- pgadmin/utils/tab-complete.inc | 164 ++++++++++++++++++++++++++++++++++++++++- pgadmin/utils/tabcomplete.c | 4 +- 3 files changed, 165 insertions(+), 9 deletions(-) diff --git a/pgadmin/ctl/ctlSQLBox.cpp b/pgadmin/ctl/ctlSQLBox.cpp index a45bb5e..37967d6 100644 --- a/pgadmin/ctl/ctlSQLBox.cpp +++ b/pgadmin/ctl/ctlSQLBox.cpp @@ -828,7 +828,7 @@ void ctlSQLBox::OnMarginClick(wxStyledTextEvent &event) } -extern "C" char *tab_complete(const char *allstr, const int startptr, const int endptr, void *dbptr); +extern "C" char *tab_complete(const char *allstr, const int startptr, const int endptr, void *dbptr, int dbvmajor, int dbvminor); void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev) { if (GetReadOnly()) @@ -843,9 +843,9 @@ void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev) char *tab_ret; if (spaceidx == -1) - tab_ret = tab_complete(what.mb_str(wxConvUTF8), 0, what.Len() + 1, m_database); + tab_ret = tab_complete(what.mb_str(wxConvUTF8), 0, what.Len() + 1, m_database, m_database->GetMajorVersion(), m_database->GetMinorVersion()); else - tab_ret = tab_complete(what.mb_str(wxConvUTF8), spaceidx + 1, what.Len() + 1, m_database); + tab_ret = tab_complete(what.mb_str(wxConvUTF8), spaceidx + 1, what.Len() + 1, m_database, m_database->GetMajorVersion(), m_database->GetMinorVersion()); if (tab_ret == NULL || tab_ret[0] == '\0') return; /* No autocomplete available for this string */ diff --git a/pgadmin/utils/tab-complete.inc b/pgadmin/utils/tab-complete.inc index 98b7a35..2a42f85 100644 --- a/pgadmin/utils/tab-complete.inc +++ b/pgadmin/utils/tab-complete.inc @@ -315,6 +315,19 @@ static const SchemaQuery Query_for_list_of_views = { " (SELECT tgrelid FROM pg_catalog.pg_trigger "\ " WHERE pg_catalog.quote_ident(tgname)='%s')" +#define Query_for_list_of_policies \ +" SELECT pg_catalog.quote_ident(polname) "\ +" FROM pg_catalog.pg_policy "\ +" WHERE substring(pg_catalog.quote_ident(polname),1,%d)='%s'" + +#define Query_for_list_of_tables_for_policy \ +"SELECT pg_catalog.quote_ident(relname) "\ +" FROM pg_catalog.pg_class"\ +" WHERE (%d = pg_catalog.length('%s'))"\ +" AND oid IN "\ +" (SELECT polrelid FROM pg_catalog.pg_policy "\ +" WHERE pg_catalog.quote_ident(polname)='%s')" + /* * This is a list of all "things" in Pgsql, which can show up after CREATE or * DROP; and there is also a query to get a list of them. @@ -345,6 +358,7 @@ static const pgsql_thing_t words_after_create[] = { {"INDEX", NULL, &Query_for_list_of_indexes}, {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a * good idea. */ + {"POLICY", NULL, NULL}, {"ROLE", Query_for_list_of_roles}, {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"}, {"SCHEMA", Query_for_list_of_schemas}, @@ -360,13 +374,14 @@ static const pgsql_thing_t words_after_create[] = { {NULL, NULL, NULL} /* end of list */ }; +#define BACKEND_MINIMUM(major, minor) (dbvmajor > major || (dbvmajor == major && dbvminor >= minor)) /* The completion function. Acc. to readline spec this gets passed the text entered to far and its start and end in the readline buffer. The return value is some partially obscure list format that can be generated by the readline libraries completion_matches() function, so we don't have to worry about it. */ -static char * psql_completion(char *text, int start, int end, void *dbptr) +static char * psql_completion(char *text, int start, int end, void *dbptr, int dbvmajor, int dbvminor) { /* This is the variable we'll return. */ char *matches = NULL; @@ -376,7 +391,9 @@ static char * psql_completion(char *text, int start, int end, void *dbptr) *prev2_wd, *prev3_wd, *prev4_wd, - *prev5_wd; + *prev5_wd, + *prev6_wd, + *prev7_wd; static const char *const sql_commands[] = { "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT", @@ -419,6 +436,8 @@ static char * psql_completion(char *text, int start, int end, void *dbptr) prev3_wd = previous_word(start, 2); prev4_wd = previous_word(start, 3); prev5_wd = previous_word(start, 4); + prev6_wd = previous_word(start, 5); + prev7_wd = previous_word(start, 6); /* If a backslash command was started, continue */ if (text[0] == '\\') @@ -448,7 +467,7 @@ static char * psql_completion(char *text, int start, int end, void *dbptr) { static const char *const list_ALTER[] = {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FUNCTION", - "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SEQUENCE", "TABLE", + "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "POLICY", "ROLE", "SCHEMA", "SEQUENCE", "TABLE", "TABLESPACE", "TRIGGER", "TYPE", "USER", NULL}; COMPLETE_WITH_LIST(list_ALTER); @@ -781,7 +800,7 @@ static char * psql_completion(char *text, int start, int end, void *dbptr) pg_strcasecmp(prev_wd, "ON") == 0) { static const char *const list_COMMENT[] = - {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA", + {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "POLICY", "RULE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION", "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", NULL}; @@ -1265,6 +1284,141 @@ static char * psql_completion(char *text, int start, int end, void *dbptr) pg_strcasecmp(prev_wd, "GROUP") == 0) COMPLETE_WITH_CONST("BY"); +/* POLICY */ + /* Complete "CREATE POLICY ON" */ + else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && + pg_strcasecmp(prev2_wd, "POLICY") == 0) + COMPLETE_WITH_CONST("ON"); + /* Complete "CREATE POLICY ON " */ + else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && + pg_strcasecmp(prev3_wd, "POLICY") == 0 && + pg_strcasecmp(prev_wd, "ON") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + /* Complete "CREATE POLICY ON
FOR|TO|USING|WITH CHECK" */ + else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && + pg_strcasecmp(prev4_wd, "POLICY") == 0 && + pg_strcasecmp(prev2_wd, "ON") == 0) + { + static const char *const list_CREATEPOLICY[] = + {"FOR", "TO", "USING (", "WITH CHECK (", NULL}; + COMPLETE_WITH_LIST(list_CREATEPOLICY); + } + /* CREATE POLICY ON
FOR ALL|SELECT|INSERT|UPDATE|DELETE */ + else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 && + pg_strcasecmp(prev5_wd, "POLICY") == 0 && + pg_strcasecmp(prev3_wd, "ON") == 0 && + pg_strcasecmp(prev_wd, "FOR") == 0) + { + static const char *const list_CREATEPOLICYFOR[] = + {"ALL", "SELECT", "INSERT", "UPDATE", "DELETE", NULL}; + COMPLETE_WITH_LIST(list_CREATEPOLICYFOR); + } + /* Complete "CREATE POLICY ON
FOR INSERT TO|WITH CHECK" */ + /* Note this should happen before INSERT INTO/UPDATE checks*/ + else if (pg_strcasecmp(prev7_wd, "CREATE") == 0 && + pg_strcasecmp(prev6_wd, "POLICY") == 0 && + pg_strcasecmp(prev4_wd, "ON") == 0 && + pg_strcasecmp(prev2_wd, "FOR") == 0 && + pg_strcasecmp(prev_wd, "INSERT") == 0) + { + static const char *const list_CREATEPOLICYFORINSERT[] = + {"TO", "WITH CHECK (", NULL}; + COMPLETE_WITH_LIST(list_CREATEPOLICYFORINSERT); + } + /* Complete "CREATE POLICY ON
FOR SELECT|DELETE TO|USING" */ + else if (pg_strcasecmp(prev7_wd, "CREATE") == 0 && + pg_strcasecmp(prev6_wd, "POLICY") == 0 && + pg_strcasecmp(prev4_wd, "ON") == 0 && + pg_strcasecmp(prev2_wd, "FOR") == 0 && + (pg_strcasecmp(prev_wd, "SELECT") == 0 || pg_strcasecmp(prev_wd, "DELETE") == 0)) + { + static const char *const list_CREATEPOLICYFORSELECT[] = + {"TO", "USING (", NULL}; + COMPLETE_WITH_LIST(list_CREATEPOLICYFORSELECT); + } + /* CREATE POLICY ON
FOR ALL|UPDATE TO|USING|WITH CHECK */ + else if (pg_strcasecmp(prev7_wd, "CREATE") == 0 && + pg_strcasecmp(prev6_wd, "POLICY") == 0 && + pg_strcasecmp(prev4_wd, "ON") == 0 && + pg_strcasecmp(prev2_wd, "FOR") == 0 && + (pg_strcasecmp(prev_wd, "ALL") == 0 || pg_strcasecmp(prev_wd, "UPDATE") == 0)) + { + static const char *const list_CREATEPOLICYFORSELECT[] = + {"TO", "USING (", "WITH CHECK (", NULL}; + COMPLETE_WITH_LIST(list_CREATEPOLICYFORSELECT); + } + /* Complete "CREATE POLICY ON
TO " */ + else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 && + pg_strcasecmp(prev5_wd, "POLICY") == 0 && + pg_strcasecmp(prev3_wd, "ON") == 0 && + pg_strcasecmp(prev_wd, "TO") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); + /* CREATE POLICY ON
USING ( */ + else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 && + pg_strcasecmp(prev5_wd, "POLICY") == 0 && + pg_strcasecmp(prev3_wd, "ON") == 0 && + pg_strcasecmp(prev_wd, "USING") == 0) + COMPLETE_WITH_CONST("("); + + /* ALTER POLICY */ + /* Should check db version to avoid errors with nonexistent catalogs */ + else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && + pg_strcasecmp(prev_wd, "POLICY") == 0 && + BACKEND_MINIMUM(9, 5)) + COMPLETE_WITH_QUERY(Query_for_list_of_policies); + /* ALTER POLICY ON */ + else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && + pg_strcasecmp(prev2_wd, "POLICY") == 0) + COMPLETE_WITH_CONST("ON"); + /* ALTER POLICY ON
*/ + else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && + pg_strcasecmp(prev3_wd, "POLICY") == 0 && + pg_strcasecmp(prev_wd, "ON") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + /* ALTER POLICY ON
- show options */ + else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && + pg_strcasecmp(prev4_wd, "POLICY") == 0 && + pg_strcasecmp(prev2_wd, "ON") == 0) + { + static const char *const list_ALTERPOLICY[] = + {"RENAME TO", "TO", "USING (", "WITH CHECK (", NULL}; + COMPLETE_WITH_LIST(list_ALTERPOLICY); + } + /* ALTER POLICY ON
TO */ + else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && + pg_strcasecmp(prev5_wd, "POLICY") == 0 && + pg_strcasecmp(prev3_wd, "ON") == 0 && + pg_strcasecmp(prev_wd, "TO") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); + /* ALTER POLICY ON
USING ( */ + else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && + pg_strcasecmp(prev5_wd, "POLICY") == 0 && + pg_strcasecmp(prev3_wd, "ON") == 0 && + pg_strcasecmp(prev_wd, "USING") == 0) + COMPLETE_WITH_CONST("("); + /* ALTER POLICY ON
WITH CHECK ( */ + else if (pg_strcasecmp(prev7_wd, "CREATE") == 0 && + pg_strcasecmp(prev6_wd, "POLICY") == 0 && + pg_strcasecmp(prev4_wd, "ON") == 0 && + pg_strcasecmp(prev2_wd, "WITH") == 0 && + pg_strcasecmp(prev_wd, "CHECK") == 0) + COMPLETE_WITH_CONST("("); + + /* DROP POLICY */ + else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && + pg_strcasecmp(prev_wd, "POLICY") == 0 && + BACKEND_MINIMUM(9, 5)) + COMPLETE_WITH_QUERY(Query_for_list_of_policies); + /* DROP POLICY ON */ + else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && + pg_strcasecmp(prev2_wd, "POLICY") == 0) + COMPLETE_WITH_CONST("ON"); + /* DROP POLICY ON
*/ + else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && + pg_strcasecmp(prev3_wd, "POLICY") == 0 && + pg_strcasecmp(prev_wd, "ON") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + /* INSERT */ /* Complete INSERT with "INTO" */ else if (pg_strcasecmp(prev_wd, "INSERT") == 0) @@ -1730,6 +1884,8 @@ static char * psql_completion(char *text, int start, int end, void *dbptr) free(prev3_wd); free(prev4_wd); free(prev5_wd); + free(prev6_wd); + free(prev7_wd); /* Return our Grand List O' Matches */ return matches; diff --git a/pgadmin/utils/tabcomplete.c b/pgadmin/utils/tabcomplete.c index f8cba05..853915f 100644 --- a/pgadmin/utils/tabcomplete.c +++ b/pgadmin/utils/tabcomplete.c @@ -408,8 +408,8 @@ static char *complete_filename() /* * Entrypoint from the C++ world */ -char *tab_complete(const char *allstr, const int startptr, const int endptr, void *dbptr) +char *tab_complete(const char *allstr, const int startptr, const int endptr, void *dbptr, int dbvmajor, int dbvminor) { rl_line_buffer = (char *)allstr; - return psql_completion((char *)(allstr + startptr), startptr,endptr,dbptr); + return psql_completion((char *)(allstr + startptr), startptr,endptr,dbptr,dbvmajor,dbvminor); } -- 2.7.2.windows.1