diff --git a/src/pg_query_parse_plpgsql.c b/src/pg_query_parse_plpgsql.c index 79402515..9c5d1cd8 100644 --- a/src/pg_query_parse_plpgsql.c +++ b/src/pg_query_parse_plpgsql.c @@ -140,7 +140,10 @@ static PLpgSQL_function *compile_do_stmt(DoStmt* stmt) } } - assert(proc_source != NULL); + if (proc_source == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("no inline code specified"))); if(strcmp(language, "plpgsql") != 0) { return (PLpgSQL_function *) palloc0(sizeof(PLpgSQL_function)); @@ -431,11 +434,25 @@ compile_create_function_stmt_via_callback(CreateFunctionStmt *stmt) } } - assert(proc_source != NULL); - if (strcmp(language, "plpgsql") != 0) return (PLpgSQL_function *) palloc0(sizeof(PLpgSQL_function)); + /* Mirrors the checks in upstream interpret_AS_clause */ + if (stmt->sql_body == NULL && proc_source == NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("no function body specified"))); + + if (stmt->sql_body != NULL && proc_source != NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("duplicate function body specified"))); + + if (stmt->sql_body != NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("inline SQL function body only valid for language SQL"))); + wrapper = pg_query_create_function(stmt, language, proc_source, &is_dml_trigger, &is_event_trigger); @@ -624,6 +641,7 @@ PgQueryPlpgsqlParseResult pg_query_parse_plpgsql(const char* input) if (statements.stmts_count == 0) { result.plpgsql_funcs = strdup("[]"); + free(parse_result.stderr_buffer); pg_query_exit_memory_context(ctx); return result; } @@ -639,6 +657,7 @@ PgQueryPlpgsqlParseResult pg_query_parse_plpgsql(const char* input) result.error = func_and_error.error; if (result.error != NULL) { + free(parse_result.stderr_buffer); pg_query_exit_memory_context(ctx); return result; } diff --git a/test/parse_plpgsql.c b/test/parse_plpgsql.c index 50463691..feadd1fc 100644 --- a/test/parse_plpgsql.c +++ b/test/parse_plpgsql.c @@ -10,6 +10,40 @@ #include #include +static bool +expect_error(const char *input, const char *expected_message) +{ + PgQueryPlpgsqlParseResult result = pg_query_parse_plpgsql(input); + bool success = result.error != NULL && + strcmp(result.error->message, expected_message) == 0; + + if (!success) + { + printf("Expected error: %s\n", expected_message); + printf("Actual error: %s\n", + result.error == NULL ? "(none)" : result.error->message); + } + + pg_query_free_plpgsql_parse_result(result); + return success; +} + +static bool +expect_no_error(const char *input) +{ + PgQueryPlpgsqlParseResult result = pg_query_parse_plpgsql(input); + bool success = result.error == NULL; + + if (!success) + { + printf("Expected no error for: %s\n", input); + printf("Actual error: %s\n", result.error->message); + } + + pg_query_free_plpgsql_parse_result(result); + return success; +} + int main() { bool ret_code = EXIT_SUCCESS; char *sample_buffer; @@ -56,6 +90,31 @@ int main() { pg_query_free_plpgsql_parse_result(result); + if (!expect_error("CREATE FUNCTION f() RETURNS void LANGUAGE plpgsql;", + "no function body specified")) + ret_code = EXIT_FAILURE; + + if (!expect_error("DO LANGUAGE plpgsql;", + "no inline code specified")) + ret_code = EXIT_FAILURE; + + if (!expect_error("CREATE FUNCTION f() RETURNS int LANGUAGE plpgsql RETURN 1;", + "inline SQL function body only valid for language SQL")) + ret_code = EXIT_FAILURE; + + if (!expect_error("CREATE FUNCTION f() RETURNS int AS $$ BEGIN RETURN 1; END; $$ LANGUAGE plpgsql RETURN 1;", + "duplicate function body specified")) + ret_code = EXIT_FAILURE; + + if (!expect_no_error("CREATE FUNCTION f() RETURNS int LANGUAGE sql RETURN 1;")) + ret_code = EXIT_FAILURE; + + if (!expect_no_error("CREATE FUNCTION f() RETURNS int LANGUAGE sql BEGIN ATOMIC SELECT 1; END;")) + ret_code = EXIT_FAILURE; + + if (!expect_no_error("SELECT 1;")) + ret_code = EXIT_FAILURE; + pg_query_exit(); return ret_code;