#include <pg_query.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "deparse_tests.c"
void remove_numeric_key(char *parse_tree_json, char* key)
{
char *tokstart;
char *p;
size_t remaining_len;
char search[100];
sprintf(search, "\"%s\":", key);
p = parse_tree_json;
while ((p = strstr(p, search)) != NULL)
{
tokstart = p;
if (*(tokstart - 1) == ',')
tokstart--;
p += strlen(search);
if (*p == '-')
p++;
while (*p >= '0' && *p <= '9')
p++;
remaining_len = strlen(p);
memmove(tokstart, p, remaining_len);
p = tokstart;
*(p + remaining_len) = '\0';
}
}
void remove_node_locations(char *parse_tree_json)
{
remove_numeric_key(parse_tree_json, "location");
remove_numeric_key(parse_tree_json, "name_location");
}
int run_test(const char *query, bool compare_query_text) {
PgQueryProtobufParseResult parse_result = pg_query_parse_protobuf(query);
if (parse_result.error) {
if (!compare_query_text) { pg_query_free_protobuf_parse_result(parse_result);
return EXIT_SUCCESS;
}
printf("\nERROR for \"%s\"\n %s\n", query, parse_result.error->message);
pg_query_free_protobuf_parse_result(parse_result);
return EXIT_FAILURE;
}
PgQueryParseResult parse_result_original = pg_query_parse(query);
PgQueryDeparseResult deparse_result = pg_query_deparse_protobuf(parse_result.parse_tree);
if (deparse_result.error) {
printf("\nERROR for \"%s\"\n %s\n parsetree: %s\n",
query,
deparse_result.error->message,
parse_result_original.parse_tree);
pg_query_free_protobuf_parse_result(parse_result);
pg_query_free_deparse_result(deparse_result);
pg_query_free_parse_result(parse_result_original);
return EXIT_FAILURE;
} else if (compare_query_text && strcmp(deparse_result.query, query) != 0) {
printf("\nQUERY TEXT MISMATCH for \"%s\"\n actual: \"%s\"\n original parsetree: %s\n",
query,
deparse_result.query,
parse_result_original.parse_tree);
pg_query_free_protobuf_parse_result(parse_result);
pg_query_free_deparse_result(deparse_result);
pg_query_free_parse_result(parse_result_original);
return EXIT_FAILURE;
}
int ret_code = EXIT_SUCCESS;
PgQueryParseResult parse_result_deparse = pg_query_parse(deparse_result.query);
if (parse_result_original.error) {
ret_code = EXIT_FAILURE;
printf("\nERROR for parsing \"%s\"\n error: %s\n", query, parse_result_original.error->message);
} else if (parse_result_deparse.error) {
ret_code = EXIT_FAILURE;
remove_node_locations(parse_result_deparse.parse_tree);
printf("\nERROR for parsing deparse of \"%s\"\n deparsed sql: %s\n error: %s\n original parsetree: %s\n",
query,
deparse_result.query,
parse_result_deparse.error->message,
parse_result_original.parse_tree);
} else {
remove_node_locations(parse_result_original.parse_tree);
remove_node_locations(parse_result_deparse.parse_tree);
if (strcmp(parse_result_original.parse_tree, parse_result_deparse.parse_tree) != 0) {
ret_code = EXIT_FAILURE;
printf("\nPARSETREE MISMATCH for parsing deparse of \"%s\"\n deparsed sql: %s\n original parsetree: %s\n deparsed parsetree: %s\n",
query,
deparse_result.query,
parse_result_original.parse_tree,
parse_result_deparse.parse_tree);
} else {
printf(".");
}
}
pg_query_free_protobuf_parse_result(parse_result);
pg_query_free_parse_result(parse_result_original);
pg_query_free_parse_result(parse_result_deparse);
pg_query_free_deparse_result(deparse_result);
return ret_code;
}
int run_tests_from_file(const char * filename) {
char *sample_buffer;
struct stat sample_stat;
int fd;
fd = open(filename, O_RDONLY);
if (fd < 0) {
printf("\nERROR opening regression test file: %s\n", filename);
return EXIT_FAILURE;
}
fstat(fd, &sample_stat);
sample_buffer = malloc(sample_stat.st_size + 1);
read(fd, sample_buffer, sample_stat.st_size);
sample_buffer[sample_stat.st_size] = 0;
if (sample_buffer == (void *) -1)
{
printf("Could not read samples file\n");
close(fd);
return EXIT_FAILURE;
}
char *sample_buffer_p = sample_buffer;
if (strcmp(filename, "test/sql/postgres_regress/strings.sql") == 0)
{
sample_buffer_p = strstr(sample_buffer_p, "-- bytea\n");
}
PgQuerySplitResult split_result = pg_query_split_with_scanner(sample_buffer_p);
if (split_result.error != NULL)
{
printf("\nERROR splitting file \"%s\"\n error: %s\n", filename, split_result.error->message);
pg_query_free_split_result(split_result);
free(sample_buffer);
close(fd);
return EXIT_FAILURE;
}
int ret_code = EXIT_SUCCESS;
for (int i = 0; i < split_result.n_stmts; ++i)
{
int test_ret_code;
char *query = malloc(split_result.stmts[i]->stmt_len + 1);
memcpy(query, sample_buffer_p + split_result.stmts[i]->stmt_location, split_result.stmts[i]->stmt_len);
query[split_result.stmts[i]->stmt_len] = 0;
test_ret_code = run_test(query, false);
if (test_ret_code != EXIT_SUCCESS)
ret_code = test_ret_code;
free(query);
}
pg_query_free_split_result(split_result);
free(sample_buffer);
close(fd);
return ret_code;
}
const char* regressFilenames[] = {
"advisory_lock.sql",
"aggregates.sql",
"alter_generic.sql",
"alter_operator.sql",
"alter_table.sql",
"amutils.sql",
"arrays.sql",
"async.sql",
"bit.sql",
"bitmapops.sql",
"boolean.sql",
"box.sql",
"brin.sql",
"brin_bloom.sql",
"brin_multi.sql",
"btree_index.sql",
"case.sql",
"char.sql",
"circle.sql",
"cluster.sql",
"collate.icu.utf8.sql",
"collate.linux.utf8.sql",
"collate.sql",
"collate.utf8.sql",
"collate.windows.win1252.sql",
"combocid.sql",
"comments.sql",
"compression.sql",
"constraints.sql",
"conversion.sql",
"copy.sql",
"copy2.sql",
"copydml.sql",
"copyselect.sql",
"create_aggregate.sql",
"create_am.sql",
"create_cast.sql",
"create_function_c.sql",
"create_function_sql.sql",
"create_index.sql",
"create_index_spgist.sql",
"create_misc.sql",
"create_operator.sql",
"create_procedure.sql",
"create_role.sql",
"create_schema.sql",
"create_table.sql",
"create_table_like.sql",
"create_type.sql",
"create_view.sql",
"database.sql",
"date.sql",
"dbsize.sql",
"delete.sql",
"dependency.sql",
"domain.sql",
"drop_if_exists.sql",
"drop_operator.sql",
"enum.sql",
"equivclass.sql",
"errors.sql",
"event_trigger.sql",
"event_trigger_login.sql",
"explain.sql",
"expressions.sql",
"fast_default.sql",
"float4.sql",
"float8.sql",
"foreign_data.sql",
"foreign_key.sql",
"functional_deps.sql",
"generated.sql",
"geometry.sql",
"gin.sql",
"gist.sql",
"groupingsets.sql",
"guc.sql",
"hash_func.sql",
"hash_index.sql",
"hash_part.sql",
"horology.sql",
"identity.sql",
"incremental_sort.sql",
"index_including.sql",
"index_including_gist.sql",
"indexing.sql",
"indirect_toast.sql",
"inet.sql",
"infinite_recurse.sql",
"inherit.sql",
"init_privs.sql",
"insert.sql",
"insert_conflict.sql",
"int2.sql",
"int4.sql",
"int8.sql",
"interval.sql",
"join.sql",
"join_hash.sql",
"json.sql",
"json_encoding.sql",
"jsonb.sql",
"jsonb_jsonpath.sql",
"jsonpath.sql",
"jsonpath_encoding.sql",
"largeobject.sql",
"limit.sql",
"line.sql",
"lock.sql",
"lseg.sql",
"macaddr.sql",
"macaddr8.sql",
"matview.sql",
"md5.sql",
"memoize.sql",
"merge.sql",
"misc.sql",
"misc_functions.sql",
"misc_sanity.sql",
"money.sql",
"multirangetypes.sql",
"mvcc.sql",
"name.sql",
"namespace.sql",
"numeric.sql",
"numeric_big.sql",
"object_address.sql",
"oid.sql",
"oidjoins.sql",
"opr_sanity.sql",
"partition_aggregate.sql",
"partition_info.sql",
"partition_join.sql",
"partition_prune.sql",
"password.sql",
"path.sql",
"pg_lsn.sql",
"plancache.sql",
"plpgsql.sql",
"point.sql",
"polygon.sql",
"polymorphism.sql",
"portals.sql",
"portals_p2.sql",
"predicate.sql",
"prepare.sql",
"prepared_xacts.sql",
"privileges.sql",
"psql.sql",
"psql_crosstab.sql",
"publication.sql",
"random.sql",
"rangefuncs.sql",
"rangetypes.sql",
"regex.sql",
"regproc.sql",
"reindex_catalog.sql",
"reloptions.sql",
"replica_identity.sql",
"returning.sql",
"roleattributes.sql",
"rowsecurity.sql",
"rowtypes.sql",
"rules.sql",
"sanity_check.sql",
"security_label.sql",
"select.sql",
"select_distinct.sql",
"select_distinct_on.sql",
"select_having.sql",
"select_implicit.sql",
"select_into.sql",
"select_parallel.sql",
"select_views.sql",
"sequence.sql",
"spgist.sql",
"sqljson.sql",
"sqljson_jsontable.sql",
"sqljson_queryfuncs.sql",
"stats.sql",
"stats_ext.sql",
"strings.sql",
"subscription.sql",
"subselect.sql",
"sysviews.sql",
"tablesample.sql",
"tablespace.sql",
"temp.sql",
"test_setup.sql",
"text.sql",
"tid.sql",
"tidrangescan.sql",
"tidscan.sql",
"time.sql",
"timestamp.sql",
"timestamptz.sql",
"timetz.sql",
"transactions.sql",
"triggers.sql",
"truncate.sql",
"tsdicts.sql",
"tsearch.sql",
"tsrf.sql",
"tstypes.sql",
"tuplesort.sql",
"txid.sql",
"type_sanity.sql",
"typed_table.sql",
"unicode.sql",
"union.sql",
"updatable_views.sql",
"update.sql",
"uuid.sql",
"vacuum.sql",
"vacuum_parallel.sql",
"varchar.sql",
"window.sql",
"with.sql",
"write_parallel.sql",
"xid.sql",
"xml.sql",
"xmlmap.sql",
};
size_t regressFilenameCount = sizeof(regressFilenames) / sizeof(regressFilenames[0]);
const char* plpgsqlRegressFilenames[] =
{
"plpgsql_array.sql",
"plpgsql_cache.sql",
"plpgsql_call.sql",
"plpgsql_control.sql",
"plpgsql_copy.sql",
"plpgsql_domain.sql",
"plpgsql_misc.sql",
"plpgsql_record.sql",
"plpgsql_simple.sql",
"plpgsql_transaction.sql",
"plpgsql_trap.sql",
"plpgsql_trigger.sql",
"plpgsql_varprops.sql"
};
size_t plpgsqlRegressFilenameCount = sizeof(plpgsqlRegressFilenames) / sizeof(plpgsqlRegressFilenames[0]);
int main() {
size_t i;
int ret_code = EXIT_SUCCESS;
int test_ret_code;
for (i = 0; i < testsLength; i += 1) {
test_ret_code = run_test(tests[i], true);
if (test_ret_code != EXIT_SUCCESS)
ret_code = test_ret_code;
}
for (i = 0; i < regressFilenameCount; i += 1) {
printf("\n%s\n", regressFilenames[i]);
char *filename = malloc(sizeof(char) * strlen("test/sql/postgres_regress/") + strlen(regressFilenames[i]) + 1);
strcpy(filename, "test/sql/postgres_regress/");
strcat(filename, regressFilenames[i]);
test_ret_code = run_tests_from_file(filename);
free(filename);
if (test_ret_code != EXIT_SUCCESS)
ret_code = test_ret_code;
}
for (i = 0; i < plpgsqlRegressFilenameCount; i += 1) {
printf("\n%s\n", plpgsqlRegressFilenames[i]);
char *filename = malloc(sizeof(char) * strlen("test/sql/postgres_regress/") + strlen(plpgsqlRegressFilenames[i]) + 1);
strcpy(filename, "test/sql/plpgsql_regress/");
strcat(filename, plpgsqlRegressFilenames[i]);
test_ret_code = run_tests_from_file(filename);
free(filename);
if (test_ret_code != EXIT_SUCCESS)
ret_code = test_ret_code;
}
printf("\n");
pg_query_exit();
return ret_code;
}