📄 tab-complete.c
字号:
/* If this is the first time for this completion, init some values */ if (state == 0) { list_index = 0; string_length = strlen(text); } /* find something that matches */ while ((name = words_after_create[list_index++].name)) if (strncasecmp(name, text, string_length) == 0) return xstrdup(name); /* if nothing matches, return NULL */ return NULL;}/* The following two functions are wrappers for _complete_from_query */static char *complete_from_query(const char *text, int state){ return _complete_from_query(0, text, state);}static char *complete_from_schema_query(const char *text, int state){ return _complete_from_query(1, text, state);}/* 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 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 completion_info_charp. or: - A schema query used for completion of both schema and relation names; these are more complex and must contain in the following order: %d %s %d %s %d %s %s %d %s where %d is the string length of the text and %s the text itself. It is assumed that strings should be escaped to become SQL literals (that is, what is in the query is actually ... '%s' ...) See top of file for examples of both kinds of query.*/static char *_complete_from_query(int is_schema_query, const char *text, int state){ static int list_index, string_length; static PGresult *result = NULL; /* * If this is the first time for this completion, we fetch a list of * our "things" from the backend. */ if (state == 0) { PQExpBufferData query_buffer; char *e_text; char *e_info_charp; list_index = 0; string_length = strlen(text); /* Free any prior result */ PQclear(result); result = NULL; /* Set up suitably-escaped copies of textual inputs */ if (text) { e_text = (char *) malloc(strlen(text) * 2 + 1); if (!e_text) return NULL; PQescapeString(e_text, text, strlen(text)); } else e_text = NULL; if (completion_info_charp) { e_info_charp = (char *) malloc(strlen(completion_info_charp) * 2 + 1); if (!e_info_charp) { if (e_text) free(e_text); return NULL; } PQescapeString(e_info_charp, completion_info_charp, strlen(completion_info_charp)); } else e_info_charp = NULL; initPQExpBuffer(&query_buffer); if (is_schema_query) { /* completion_squery gives us the pieces to assemble */ const char *qualresult = completion_squery->qualresult; if (qualresult == NULL) qualresult = completion_squery->result; /* Get unqualified names matching the input-so-far */ appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ", completion_squery->result, completion_squery->catname); if (completion_squery->selcondition) appendPQExpBuffer(&query_buffer, "%s AND ", completion_squery->selcondition); appendPQExpBuffer(&query_buffer, "%s AND ", completion_squery->viscondition); appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'", completion_squery->result, string_length, e_text); /* * When fetching relation names, suppress system catalogs unless * the input-so-far begins with "pg_". This is a compromise * between not offering system catalogs for completion at all, * and having them swamp the result when the input is just "p". */ if (strcmp(completion_squery->catname, "pg_catalog.pg_class c") == 0 && strncmp(text, "pg_", 3) != 0) { appendPQExpBuffer(&query_buffer, " AND c.relnamespace <> (SELECT oid FROM" " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')"); } /* * Add in matching schema names, but only if there is more than * one potential match among schema names. */ appendPQExpBuffer(&query_buffer, "\nUNION\n" "SELECT pg_catalog.quote_ident(n.nspname) || '.' " "FROM pg_catalog.pg_namespace n " "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'", string_length, e_text); appendPQExpBuffer(&query_buffer, " AND (SELECT pg_catalog.count(*)" " FROM pg_catalog.pg_namespace" " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1", string_length, e_text); /* * Add in matching qualified names, but only if there is exactly * one schema matching the input-so-far. */ appendPQExpBuffer(&query_buffer, "\nUNION\n" "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s " "FROM %s, pg_catalog.pg_namespace n " "WHERE %s = n.oid AND ", qualresult, completion_squery->catname, completion_squery->namespace); if (completion_squery->selcondition) appendPQExpBuffer(&query_buffer, "%s AND ", completion_squery->selcondition); appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'", qualresult, string_length, e_text); /* This condition exploits the single-matching-schema rule to speed up the query */ appendPQExpBuffer(&query_buffer, " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) =" " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)", string_length, e_text); appendPQExpBuffer(&query_buffer, " AND (SELECT pg_catalog.count(*)" " FROM pg_catalog.pg_namespace" " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1", string_length, e_text); /* If an addon query was provided, use it */ if (completion_charp) appendPQExpBuffer(&query_buffer, "\n%s", completion_charp); } else { /* completion_charp is an sprintf-style format string */ appendPQExpBuffer(&query_buffer, completion_charp, string_length, e_text, e_info_charp); } /* Limit the number of records in the result */ appendPQExpBuffer(&query_buffer, "\nLIMIT %d", completion_max_records); result = exec_query(query_buffer.data); termPQExpBuffer(&query_buffer); if (e_text) free(e_text); if (e_info_charp) free(e_info_charp); } /* Find something that matches */ if (result && PQresultStatus(result) == PGRES_TUPLES_OK) { const char *item; while (list_index < PQntuples(result) && (item = PQgetvalue(result, list_index++, 0))) if (strncasecmp(text, item, string_length) == 0) return xstrdup(item); } /* If nothing matches, free the db structure and return null */ PQclear(result); result = NULL; return NULL;}/* This function returns in order one of a fixed, NULL pointer terminated list of strings (if matching). This can be used if there are only a fixed number SQL words that can appear at certain spot.*/static char *complete_from_list(const char *text, int state){ static int string_length, list_index, matches; static bool casesensitive; const char *item; /* need to have a list */#ifdef USE_ASSERT_CHECKING assert(completion_charpp);#endif /* Initialization */ if (state == 0) { list_index = 0; string_length = strlen(text); casesensitive = true; matches = 0; } while ((item = completion_charpp[list_index++])) { /* First pass is case sensitive */ if (casesensitive && strncmp(text, item, string_length) == 0) { matches++; return xstrdup(item); } /* Second pass is case insensitive, don't bother counting matches */ if (!casesensitive && strncasecmp(text, item, string_length) == 0) return xstrdup(item); } /* * No matches found. If we're not case insensitive already, lets * switch to being case insensitive and try again */ if (casesensitive && matches == 0) { casesensitive = false; list_index = 0; state++; return (complete_from_list(text, state)); } /* If no more matches, return null. */ return NULL;}/* This function returns one fixed string the first time even if it doesn't match what's there, and nothing the second time. This should be used if there is only one possibility that can appear at a certain spot, so misspellings will be overwritten. The string to be passed must be in completion_charp.*/static char *complete_from_const(const char *text, int state){ (void) text; /* We don't care about what was entered * already. */#ifdef USE_ASSERT_CHECKING assert(completion_charp);#endif if (state == 0) return xstrdup(completion_charp); else return NULL;}/* HELPER FUNCTIONS *//* * Execute a query and report any errors. This should be the preferred way of * talking to the database in this file. */static PGresult *exec_query(const char *query){ PGresult *result; if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK) return NULL; result = PQexec(pset.db, query); if (result != NULL && PQresultStatus(result) != PGRES_TUPLES_OK) {#if 0 psql_error("tab completion: %s failed - %s\n", query, PQresStatus(PQresultStatus(result)));#endif PQclear(result); result = NULL; } return result;}/* Return the word (space delimited) before point. Set skip > 0 to skip that many words; e.g. skip=1 finds the word before the previous one.*/static char *previous_word(int point, int skip){ int i, start = 0, end = -1, inquotes = 0; char *s; while (skip-- >= 0) { /* first we look for a space before the current word */ for (i = point; i >= 0; i--) if (rl_line_buffer[i] == ' ') break; /* now find the first non-space which then constitutes the end */ for (; i >= 0; i--) if (rl_line_buffer[i] != ' ') { end = i; break; } /* * If no end found we return null, because there is no word before * the point */ if (end == -1) return NULL; /* * Otherwise we now look for the start. The start is either the * last character before any space going backwards from the end, * or it's simply character 0 */ for (start = end; start > 0; start--) { if (rl_line_buffer[start] == '"') inquotes = !inquotes; if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) break; } point = start; } /* make a copy */ s = (char *) malloc(end - start + 2); if (!s) { psql_error("out of memory\n"); if (!pset.cur_cmd_interactive) exit(EXIT_FAILURE); else return NULL; } strncpy(s, &rl_line_buffer[start], end - start + 1); s[end - start + 1] = '\0'; return s;}#if 0/* * Surround a string with single quotes. This works for both SQL and * psql internal. Currently disable because it is reported not to * cooperate with certain versions of readline. */static char *quote_file_name(char *text, int match_type, char *quote_pointer){ char *s; size_t length; (void) quote_pointer; /* not used */ length = strlen(text) +(match_type == SINGLE_MATCH ? 3 : 2); s = malloc(length); s[0] = '\''; strcpy(s + 1, text); if (match_type == SINGLE_MATCH) s[length - 2] = '\''; s[length - 1] = '\0'; return s;}static char *dequote_file_name(char *text, char quote_char){ char *s; size_t length; if (!quote_char) return xstrdup(text); length = strlen(text); s = malloc(length - 2 + 1); strncpy(s, text +1, length - 2); s[length] = '\0'; return s;}#endif /* 0 */#endif /* USE_READLINE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -