mirror of
https://github.com/zebrajr/postgres.git
synced 2025-12-06 12:20:15 +01:00
pgbench wants to record the starting line number of each command in its scripts. It was computing that by scanning from the script start and counting newlines, so that O(N^2) work had to be done for an N-command script. In a script with 50K lines, this adds up to about 10 seconds on my machine. To add insult to injury, the results were subtly wrong, because expr_scanner_offset() scanned to find the NUL that flex inserts at the end of the current token --- and before the first yylex call, no such NUL has been inserted. So we ended by computing the script's last line number not its first one. This was visible only in case of \gset at the start of a script, which perhaps accounts for the lack of complaints. To fix, steal an idea from plpgsql and track the current lexer ending position and line count as we advance through the script. (It's a bit simpler than plpgsql since we can't need to back up.) Also adjust a couple of other places that were invoking scans from script start when they didn't really need to. I made a new psqlscan function psql_scan_get_location() that replaces both expr_scanner_offset() and expr_scanner_get_lineno(), since in practice expr_scanner_get_lineno() was only being invoked to find the line number of the current lexer end position. Reported-by: Daniel Vérité <daniel@manitou-mail.org> Author: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/84a8a89e-adb8-47a9-9d34-c13f7150ee45@manitou-mail.org
94 lines
2.9 KiB
C
94 lines
2.9 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* psqlscan.h
|
|
* lexical scanner for SQL commands
|
|
*
|
|
* This lexer used to be part of psql, and that heritage is reflected in
|
|
* the file name as well as function and typedef names, though it can now
|
|
* be used by other frontend programs as well. It's also possible to extend
|
|
* this lexer with a compatible add-on lexer to handle program-specific
|
|
* backslash commands.
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* src/include/fe_utils/psqlscan.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef PSQLSCAN_H
|
|
#define PSQLSCAN_H
|
|
|
|
#include "pqexpbuffer.h"
|
|
|
|
|
|
/* Abstract type for lexer's internal state */
|
|
typedef struct PsqlScanStateData *PsqlScanState;
|
|
|
|
/* Termination states for psql_scan() */
|
|
typedef enum
|
|
{
|
|
PSCAN_SEMICOLON, /* found command-ending semicolon */
|
|
PSCAN_BACKSLASH, /* found backslash command */
|
|
PSCAN_INCOMPLETE, /* end of line, SQL statement incomplete */
|
|
PSCAN_EOL, /* end of line, SQL possibly complete */
|
|
} PsqlScanResult;
|
|
|
|
/* Prompt type returned by psql_scan() */
|
|
typedef enum _promptStatus
|
|
{
|
|
PROMPT_READY,
|
|
PROMPT_CONTINUE,
|
|
PROMPT_COMMENT,
|
|
PROMPT_SINGLEQUOTE,
|
|
PROMPT_DOUBLEQUOTE,
|
|
PROMPT_DOLLARQUOTE,
|
|
PROMPT_PAREN,
|
|
PROMPT_COPY,
|
|
} promptStatus_t;
|
|
|
|
/* Quoting request types for get_variable() callback */
|
|
typedef enum
|
|
{
|
|
PQUOTE_PLAIN, /* just return the actual value */
|
|
PQUOTE_SQL_LITERAL, /* add quotes to make a valid SQL literal */
|
|
PQUOTE_SQL_IDENT, /* quote if needed to make a SQL identifier */
|
|
PQUOTE_SHELL_ARG, /* quote if needed to be safe in a shell cmd */
|
|
} PsqlScanQuoteType;
|
|
|
|
/* Callback functions to be used by the lexer */
|
|
typedef struct PsqlScanCallbacks
|
|
{
|
|
/* Fetch value of a variable, as a free'able string; NULL if unknown */
|
|
/* This pointer can be NULL if no variable substitution is wanted */
|
|
char *(*get_variable) (const char *varname, PsqlScanQuoteType quote,
|
|
void *passthrough);
|
|
} PsqlScanCallbacks;
|
|
|
|
|
|
extern PsqlScanState psql_scan_create(const PsqlScanCallbacks *callbacks);
|
|
extern void psql_scan_destroy(PsqlScanState state);
|
|
|
|
extern void psql_scan_set_passthrough(PsqlScanState state, void *passthrough);
|
|
|
|
extern void psql_scan_setup(PsqlScanState state,
|
|
const char *line, int line_len,
|
|
int encoding, bool std_strings);
|
|
extern void psql_scan_finish(PsqlScanState state);
|
|
|
|
extern PsqlScanResult psql_scan(PsqlScanState state,
|
|
PQExpBuffer query_buf,
|
|
promptStatus_t *prompt);
|
|
|
|
extern void psql_scan_reset(PsqlScanState state);
|
|
|
|
extern void psql_scan_reselect_sql_lexer(PsqlScanState state);
|
|
|
|
extern bool psql_scan_in_quote(PsqlScanState state);
|
|
|
|
extern void psql_scan_get_location(PsqlScanState state,
|
|
int *lineno, int *offset);
|
|
|
|
#endif /* PSQLSCAN_H */
|