postgresql/src/interfaces/ecpg/preproc/output.c
Tom Lane 96b6c82c9d Revert "Add DECLARE STATEMENT support to ECPG."
This reverts commit bd7c95f0c1,
along with assorted follow-on fixes.  There are some questions
about the definition and implementation of that statement, and
we don't have time to resolve them before v13 release.  Rather
than ship the feature and then have backwards-compatibility
concerns constraining any redesign, let's remove it for now
and try again later.

Discussion: https://postgr.es/m/TY2PR01MB2443EC8286995378AEB7D9F8F5B10@TY2PR01MB2443.jpnprd01.prod.outlook.com
2019-09-20 12:47:37 -04:00

260 lines
5.5 KiB
C

/* src/interfaces/ecpg/preproc/output.c */
#include "postgres_fe.h"
#include "preproc_extern.h"
static void output_escaped_str(char *cmd, bool quoted);
void
output_line_number(void)
{
char *line = hashline_number();
fprintf(base_yyout, "%s", line);
free(line);
}
void
output_simple_statement(char *stmt, int whenever_mode)
{
output_escaped_str(stmt, false);
if (whenever_mode)
whenever_action(whenever_mode);
output_line_number();
free(stmt);
}
/*
* store the whenever action here
*/
struct when when_error,
when_nf,
when_warn;
static void
print_action(struct when *w)
{
switch (w->code)
{
case W_SQLPRINT:
fprintf(base_yyout, "sqlprint();");
break;
case W_GOTO:
fprintf(base_yyout, "goto %s;", w->command);
break;
case W_DO:
fprintf(base_yyout, "%s;", w->command);
break;
case W_STOP:
fprintf(base_yyout, "exit (1);");
break;
case W_BREAK:
fprintf(base_yyout, "break;");
break;
case W_CONTINUE:
fprintf(base_yyout, "continue;");
break;
default:
fprintf(base_yyout, "{/* %d not implemented yet */}", w->code);
break;
}
}
void
whenever_action(int mode)
{
if ((mode & 1) == 1 && when_nf.code != W_NOTHING)
{
output_line_number();
fprintf(base_yyout, "\nif (sqlca.sqlcode == ECPG_NOT_FOUND) ");
print_action(&when_nf);
}
if (when_warn.code != W_NOTHING)
{
output_line_number();
fprintf(base_yyout, "\nif (sqlca.sqlwarn[0] == 'W') ");
print_action(&when_warn);
}
if (when_error.code != W_NOTHING)
{
output_line_number();
fprintf(base_yyout, "\nif (sqlca.sqlcode < 0) ");
print_action(&when_error);
}
if ((mode & 2) == 2)
fputc('}', base_yyout);
output_line_number();
}
char *
hashline_number(void)
{
/* do not print line numbers if we are in debug mode */
if (input_filename
#ifdef YYDEBUG
&& !base_yydebug
#endif
)
{
/* "* 2" here is for escaping '\' and '"' below */
char *line = mm_alloc(strlen("\n#line %d \"%s\"\n") + sizeof(int) * CHAR_BIT * 10 / 3 + strlen(input_filename) * 2);
char *src,
*dest;
sprintf(line, "\n#line %d \"", base_yylineno);
src = input_filename;
dest = line + strlen(line);
while (*src)
{
if (*src == '\\' || *src == '"')
*dest++ = '\\';
*dest++ = *src++;
}
*dest = '\0';
strcat(dest, "\"\n");
return line;
}
return EMPTY;
}
static char *ecpg_statement_type_name[] = {
"ECPGst_normal",
"ECPGst_execute",
"ECPGst_exec_immediate",
"ECPGst_prepnormal",
"ECPGst_prepare",
"ECPGst_exec_with_exprlist"
};
void
output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
{
fprintf(base_yyout, "{ ECPGdo(__LINE__, %d, %d, %s, %d, ", compat, force_indicator, connection ? connection : "NULL", questionmarks);
if (st == ECPGst_prepnormal && !auto_prepare)
st = ECPGst_normal;
/*
* In following cases, stmt is CSTRING or char_variable. They must be
* output directly. - prepared_name of EXECUTE without exprlist -
* execstring of EXECUTE IMMEDIATE
*/
fprintf(base_yyout, "%s, ", ecpg_statement_type_name[st]);
if (st == ECPGst_execute || st == ECPGst_exec_immediate)
fprintf(base_yyout, "%s, ", stmt);
else
{
fputs("\"", base_yyout);
output_escaped_str(stmt, false);
fputs("\", ", base_yyout);
}
/* dump variables to C file */
dump_variables(argsinsert, 1);
fputs("ECPGt_EOIT, ", base_yyout);
dump_variables(argsresult, 1);
fputs("ECPGt_EORT);", base_yyout);
reset_variables();
whenever_action(whenever_mode | 2);
free(stmt);
if (connection != NULL)
free(connection);
connection = NULL;
}
void
output_prepare_statement(char *name, char *stmt)
{
fprintf(base_yyout, "{ ECPGprepare(__LINE__, %s, %d, ", connection ? connection : "NULL", questionmarks);
output_escaped_str(name, true);
fputs(", ", base_yyout);
output_escaped_str(stmt, true);
fputs(");", base_yyout);
whenever_action(2);
free(name);
if (connection != NULL)
free(connection);
connection = NULL;
}
void
output_deallocate_prepare_statement(char *name)
{
const char *con = connection ? connection : "NULL";
if (strcmp(name, "all") != 0)
{
fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, ", compat, con);
output_escaped_str(name, true);
fputs(");", base_yyout);
}
else
fprintf(base_yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);
whenever_action(2);
free(name);
if (connection != NULL)
free(connection);
connection = NULL;
}
static void
output_escaped_str(char *str, bool quoted)
{
int i = 0;
int len = strlen(str);
if (quoted && str[0] == '"' && str[len - 1] == '"') /* do not escape quotes
* at beginning and end
* if quoted string */
{
i = 1;
len--;
fputs("\"", base_yyout);
}
/* output this char by char as we have to filter " and \n */
for (; i < len; i++)
{
if (str[i] == '"')
fputs("\\\"", base_yyout);
else if (str[i] == '\n')
fputs("\\\n", base_yyout);
else if (str[i] == '\\')
{
int j = i;
/*
* check whether this is a continuation line if it is, do not
* output anything because newlines are escaped anyway
*/
/* accept blanks after the '\' as some other compilers do too */
do
{
j++;
} while (str[j] == ' ' || str[j] == '\t');
if ((str[j] != '\n') && (str[j] != '\r' || str[j + 1] != '\n')) /* not followed by a
* newline */
fputs("\\\\", base_yyout);
}
else if (str[i] == '\r' && str[i + 1] == '\n')
{
fputs("\\\r\n", base_yyout);
i++;
}
else
fputc(str[i], base_yyout);
}
if (quoted && str[0] == '"' && str[len] == '"')
fputs("\"", base_yyout);
}