github存储库地址:https://github.com/OraDB-DUMP-Viewer/OraDB-DUMP-Viewer/releases/tag/v3.1.1
原文件位于test_dumps目录。
主要就是用#define将函数指针改为函数名映射,删除一些windows专有函数,改为Linux版本。修改结果如下:
c
/*****************************************************************************
OraDB DUMP Viewer - Export Test Harness
テスト用ダンプファイルから CSV/SQL を生成し、結果を検証する
*****************************************************************************/
#include "odv_api.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
//#include <windows.h>
#define __stdcall
#define __int64 __int64_t
/* --- DLL function pointers --- */
typedef struct _odv_session ODV_SESSION;
typedef void (__stdcall *ODV_TABLE_CALLBACK)(
const char *schema, const char *table, int col_count,
const char **col_names, const char **col_types,
const int *col_not_nulls, const char **col_defaults,
int constraint_count, const char *constraints_json,
__int64 row_count, __int64 data_offset, void *user_data);
#define fn_create odv_create_session
#define fn_destroy odv_destroy_session
#define fn_set_file odv_set_dump_file
#define fn_set_table_cb odv_set_table_callback
#define fn_check_kind odv_check_dump_kind
#define fn_list_tables odv_list_tables
#define fn_export_csv odv_export_csv
#define fn_export_sql odv_export_sql
#define fn_set_csv_opts odv_set_csv_options
#define fn_set_sql_opts odv_set_sql_options
#define fn_set_date_fmt odv_set_date_format
#define fn_get_version odv_get_version
#define fn_get_error odv_get_last_error
/* --- Table list state --- */
#define MAX_TABLES 200
typedef struct {
int count;
char names[MAX_TABLES][256];
char schemas[MAX_TABLES][256];
__int64 row_counts[MAX_TABLES];
} TableList;
static void __stdcall on_table(const char *schema, const char *table,
int col_count, const char **col_names, const char **col_types,
const int *col_not_nulls, const char **col_defaults,
int constraint_count, const char *constraints_json,
__int64 row_count, __int64 data_offset, void *ud)
{
TableList *tl = (TableList *)ud;
if (tl->count < MAX_TABLES) {
strncpy(tl->schemas[tl->count], schema, 255);
strncpy(tl->names[tl->count], table, 255);
tl->row_counts[tl->count] = row_count;
tl->count++;
}
}
/* Count lines in a file */
static int count_lines(const char *path) {
FILE *f = fopen(path, "rb");
if (!f) return -1;
int count = 0;
char buf[8192];
while (fgets(buf, sizeof(buf), f)) count++;
fclose(f);
return count;
}
/* Count INSERT statements in a SQL file */
static int count_inserts(const char *path) {
FILE *f = fopen(path, "rb");
if (!f) return -1;
int count = 0;
char buf[65536];
while (fgets(buf, sizeof(buf), f)) {
if (strncmp(buf, "INSERT INTO", 11) == 0) count++;
}
fclose(f);
return count;
}
/* Check if file contains CREATE TABLE */
static int has_create_table(const char *path) {
FILE *f = fopen(path, "rb");
if (!f) return 0;
char buf[65536];
while (fgets(buf, sizeof(buf), f)) {
if (strstr(buf, "CREATE TABLE") != NULL) {
fclose(f);
return 1;
}
}
fclose(f);
return 0;
}
/* Count COMMENT ON statements in a SQL file */
static int count_comment_on(const char *path) {
FILE *f = fopen(path, "rb");
if (!f) return 0;
int count = 0;
char buf[65536];
while (fgets(buf, sizeof(buf), f)) {
if (strncmp(buf, "COMMENT ON", 10) == 0) count++;
}
fclose(f);
return count;
}
/* Count CREATE INDEX statements in a SQL file */
static int count_create_indexes(const char *path) {
FILE *f = fopen(path, "rb");
if (!f) return 0;
int count = 0;
char buf[65536];
while (fgets(buf, sizeof(buf), f)) {
if (strncmp(buf, "CREATE INDEX", 12) == 0) count++;
}
fclose(f);
return count;
}
/* Get file size */
static long get_file_size(const char *path) {
FILE *f = fopen(path, "rb");
if (!f) return -1;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fclose(f);
return size;
}
/* DBMS type names */
static const char *dbms_name(int t) {
switch (t) {
case 0: return "Oracle";
case 4: return "PostgreSQL";
case 5: return "MySQL";
case 6: return "SQLServer";
default: return "Unknown";
}
}
int main(int argc, char *argv[]) {
//SetConsoleOutputCP(65001);
printf("OraDB DUMP Viewer - Export Test Harness\n");
printf("========================================\n");
/* Create output directory */
//CreateDirectoryA("export_output", NULL);
mkdir("export_output", 0755);
/* Test dump file */
const char *dump_file = "./exp_index_test.dmp";
/* Step 1: List tables */
printf("Phase 1: Listing tables in %s\n", dump_file);
printf("----------------------------------------\n");
ODV_SESSION *s = NULL;
TableList tables;
memset(&tables, 0, sizeof(tables));
fn_create(&s);
fn_set_file(s, dump_file);
int dump_type;
fn_check_kind(s, &dump_type);
fn_set_table_cb(s, on_table, &tables);
fn_list_tables(s);
fn_destroy(s);
printf("Found %d tables\n\n", tables.count);
/* Select test tables (mix of types) */
const char *test_tables[] = {
"T_BASIC_TYPES",
"T_NUMERIC_TYPES",
"T_DATETIME_TYPES",
"T_NCHAR_TYPES",
"T_TRAILING_NULLS",
"T_SPECIAL_CHARS",
"T_SINGLE_ROW",
"T_EMPTY",
"T_WIDE_TABLE",
NULL
};
/* Also test Japanese tables */
const char *jp_test_tables[] = {
"\xe7\xa4\xbe\xe5\x93\xa1\xe3\x83\x9e\xe3\x82\xb9\xe3\x82\xbf", /* 社員マスタ */
"\xe9\x83\xa8\xe7\xbd\xb2\xe3\x83\x9e\xe3\x82\xb9\xe3\x82\xbf", /* 部署マスタ */
"\xe5\x8f\x97\xe6\xb3\xa8\xe3\x83\x87\xe3\x83\xbc\xe3\x82\xbf", /* 受注データ */
NULL
};
int pass = 0, fail = 0;
/* ================================================================
Phase 2: CSV Export Tests
================================================================ */
printf("Phase 2: CSV Export Tests\n");
printf("========================================\n");
for (int i = 0; test_tables[i]; i++) {
char out_path[512];
snprintf(out_path, 512, "export_output/%s.csv", test_tables[i]);
s = NULL;
fn_create(&s);
fn_set_file(s, dump_file);
fn_check_kind(s, &dump_type);
fn_set_table_cb(s, on_table, &tables);
fn_list_tables(s);
if (fn_set_csv_opts) fn_set_csv_opts(s, 1, 1); /* header + types */
int rc = fn_export_csv(s, test_tables[i], out_path);
if (rc == 0) {
long fsize = get_file_size(out_path);
int lines = count_lines(out_path);
printf(" CSV %-25s -> %s (%ld bytes, %d lines) OK\n",
test_tables[i], out_path, fsize, lines);
pass++;
} else {
printf(" CSV %-25s -> FAIL: %s\n", test_tables[i],
fn_get_error ? fn_get_error(s) : "unknown error");
fail++;
}
fn_destroy(s);
}
/* Japanese table CSV */
for (int i = 0; jp_test_tables[i]; i++) {
char out_path[512];
snprintf(out_path, 512, "export_output/jp_%d.csv", i);
s = NULL;
fn_create(&s);
fn_set_file(s, dump_file);
fn_check_kind(s, &dump_type);
fn_set_table_cb(s, on_table, &tables);
fn_list_tables(s);
if (fn_set_csv_opts) fn_set_csv_opts(s, 1, 0);
int rc = fn_export_csv(s, jp_test_tables[i], out_path);
if (rc == 0) {
long fsize = get_file_size(out_path);
printf(" CSV %-25s -> %s (%ld bytes) OK\n",
jp_test_tables[i], out_path, fsize);
pass++;
} else {
printf(" CSV %-25s -> FAIL: %s\n", jp_test_tables[i],
fn_get_error ? fn_get_error(s) : "unknown error");
fail++;
}
fn_destroy(s);
}
printf("\n");
/* ================================================================
Phase 3: SQL Export Tests (All 4 DBMS types)
================================================================ */
int dbms_types[] = {0, 4, 5, 6}; /* Oracle, PostgreSQL, MySQL, SQL Server */
for (int d = 0; d < 4; d++) {
int dbms = dbms_types[d];
printf("Phase 3.%d: SQL Export - %s\n", d+1, dbms_name(dbms));
printf("----------------------------------------\n");
for (int i = 0; test_tables[i]; i++) {
char out_path[512];
snprintf(out_path, 512, "export_output/%s_%s.sql",
test_tables[i], dbms_name(dbms));
s = NULL;
fn_create(&s);
fn_set_file(s, dump_file);
fn_check_kind(s, &dump_type);
fn_set_table_cb(s, on_table, &tables);
fn_list_tables(s);
if (fn_set_sql_opts) fn_set_sql_opts(s, 1); /* include CREATE TABLE */
int rc = fn_export_sql(s, test_tables[i], out_path, dbms);
if (rc == 0) {
long fsize = get_file_size(out_path);
int inserts = count_inserts(out_path);
int has_ddl = has_create_table(out_path);
printf(" SQL %-25s -> %s (%ld bytes, %d INSERTs, DDL=%s) OK\n",
test_tables[i], out_path, fsize, inserts,
has_ddl ? "YES" : "NO");
pass++;
} else {
printf(" SQL %-25s -> FAIL: %s\n", test_tables[i],
fn_get_error ? fn_get_error(s) : "unknown error");
fail++;
}
fn_destroy(s);
}
/* Japanese table SQL */
for (int i = 0; jp_test_tables[i]; i++) {
char out_path[512];
snprintf(out_path, 512, "export_output/jp_%d_%s.sql", i, dbms_name(dbms));
s = NULL;
fn_create(&s);
fn_set_file(s, dump_file);
fn_check_kind(s, &dump_type);
fn_set_table_cb(s, on_table, &tables);
fn_list_tables(s);
if (fn_set_sql_opts) fn_set_sql_opts(s, 1);
int rc = fn_export_sql(s, jp_test_tables[i], out_path, dbms);
if (rc == 0) {
long fsize = get_file_size(out_path);
int inserts = count_inserts(out_path);
printf(" SQL %-25s -> %s (%ld bytes, %d INSERTs) OK\n",
jp_test_tables[i], out_path, fsize, inserts);
pass++;
} else {
printf(" SQL %-25s -> FAIL: %s\n", jp_test_tables[i],
fn_get_error ? fn_get_error(s) : "unknown error");
fail++;
}
fn_destroy(s);
}
printf("\n");
}
/* ================================================================
Phase 4: EXP format test
================================================================ */
const char *exp_file = "./exp_user.dmp";
printf("Phase 4: EXP Format Export Tests (%s)\n", exp_file);
printf("========================================\n");
const char *exp_tables[] = {"T_BASIC_TYPES", "T_NUMERIC_TYPES", "T_DATETIME_TYPES", NULL};
for (int i = 0; exp_tables[i]; i++) {
for (int d = 0; d < 4; d++) {
int dbms = dbms_types[d];
char out_path[512];
snprintf(out_path, 512, "export_output/exp_%s_%s.sql",
exp_tables[i], dbms_name(dbms));
s = NULL;
fn_create(&s);
fn_set_file(s, exp_file);
fn_check_kind(s, &dump_type);
fn_set_table_cb(s, on_table, &tables);
fn_list_tables(s);
if (fn_set_sql_opts) fn_set_sql_opts(s, 1);
int rc = fn_export_sql(s, exp_tables[i], out_path, dbms);
if (rc == 0) {
int inserts = count_inserts(out_path);
printf(" EXP SQL %-20s [%s] -> %d INSERTs OK\n",
exp_tables[i], dbms_name(dbms), inserts);
pass++;
} else {
printf(" EXP SQL %-20s [%s] -> FAIL: %s\n",
exp_tables[i], dbms_name(dbms),
fn_get_error ? fn_get_error(s) : "unknown error");
fail++;
}
fn_destroy(s);
}
}
/* ================================================================
Phase 5: EXP INDEX Export Test (exp_index_test.dmp)
================================================================ */
const char *idx_file = "./exp_index_test.dmp";
printf("\nPhase 5: INDEX Export Test (%s)\n", idx_file);
printf("========================================\n");
for (int d = 0; d < 4; d++) {
int dbms = dbms_types[d];
char out_path[512];
snprintf(out_path, 512, "export_output/idx_T_INDEX_TEST_%s.sql",
dbms_name(dbms));
s = NULL;
fn_create(&s);
fn_set_file(s, idx_file);
fn_check_kind(s, &dump_type);
fn_set_table_cb(s, on_table, &tables);
fn_list_tables(s);
if (fn_set_sql_opts) fn_set_sql_opts(s, 1);
int rc = fn_export_sql(s, "T_INDEX_TEST", out_path, dbms);
if (rc == 0) {
int inserts = count_inserts(out_path);
int indexes = count_create_indexes(out_path);
int has_ddl = has_create_table(out_path);
printf(" IDX SQL [%-10s] -> %d INSERTs, DDL=%s, %d CREATE INDEX",
dbms_name(dbms), inserts, has_ddl ? "YES" : "NO", indexes);
if (indexes == 3) {
printf(" OK\n");
pass++;
} else {
printf(" FAIL (expected 3 CREATE INDEX)\n");
fail++;
}
} else {
printf(" IDX SQL [%-10s] -> FAIL: %s\n", dbms_name(dbms),
fn_get_error ? fn_get_error(s) : "unknown error");
fail++;
}
fn_destroy(s);
}
/* ================================================================
Phase 6: EXP COMMENT Export Test (exp_comment_test.dmp)
================================================================ */
const char *cmt_file = "./exp_comment_test.dmp";
printf("\nPhase 6: COMMENT Export Test (%s)\n", cmt_file);
printf("========================================\n");
/* Oracle and PostgreSQL should have COMMENT ON statements */
for (int d = 0; d < 4; d++) {
int dbms = dbms_types[d];
char out_path[512];
snprintf(out_path, 512, "export_output/cmt_T_COMMENT_TEST_%s.sql",
dbms_name(dbms));
s = NULL;
fn_create(&s);
fn_set_file(s, cmt_file);
fn_check_kind(s, &dump_type);
fn_set_table_cb(s, on_table, &tables);
fn_list_tables(s);
if (fn_set_sql_opts) fn_set_sql_opts(s, 1);
int rc = fn_export_sql(s, "T_COMMENT_TEST", out_path, dbms);
if (rc == 0) {
int inserts = count_inserts(out_path);
int comments = count_comment_on(out_path);
int has_ddl = has_create_table(out_path);
printf(" CMT SQL [%-10s] -> %d INSERTs, DDL=%s, %d COMMENT ON",
dbms_name(dbms), inserts, has_ddl ? "YES" : "NO", comments);
/* Oracle/PostgreSQL: 1 table comment + 4 column comments = 5 */
if ((dbms == 0 || dbms == 4) && comments == 5) {
printf(" OK\n");
pass++;
} else if ((dbms == 5 || dbms == 6) && comments == 0) {
/* MySQL/SQL Server: comments as SQL comments, not COMMENT ON */
printf(" OK (as SQL comments)\n");
pass++;
} else {
printf(" FAIL (expected %d COMMENT ON)\n",
(dbms == 0 || dbms == 4) ? 5 : 0);
fail++;
}
} else {
printf(" CMT SQL [%-10s] -> FAIL: %s\n", dbms_name(dbms),
fn_get_error ? fn_get_error(s) : "unknown error");
fail++;
}
fn_destroy(s);
}
printf("\n========================================\n");
printf("EXPORT TEST RESULTS: %d passed, %d failed (total %d)\n", pass, fail, pass + fail);
printf("========================================\n");
//FreeLibrary(dll);
return fail > 0 ? 1 : 0;
}
执行结果如下:
./a.out
OraDB DUMP Viewer - Export Test Harness
========================================
Phase 1: Listing tables in ./exp_index_test.dmp
----------------------------------------
Found 1 tables
Phase 2: CSV Export Tests
========================================
。。。
Phase 3.1: SQL Export - Oracle
----------------------------------------
SQL T_BASIC_TYPES -> export_output/T_BASIC_TYPES_Oracle.sql (0 bytes, 0 INSERTs, DDL=NO) OK
SQL T_NUMERIC_TYPES -> export_output/T_NUMERIC_TYPES_Oracle.sql (0 bytes, 0 INSERTs, DDL=NO) OK
SQL T_DATETIME_TYPES -> export_output/T_DATETIME_TYPES_Oracle.sql (0 bytes, 0 INSERTs, DDL=NO) OK
SQL T_NCHAR_TYPES -> export_output/T_NCHAR_TYPES_Oracle.sql (0 bytes, 0 INSERTs, DDL=NO) OK
SQL T_TRAILING_NULLS -> export_output/T_TRAILING_NULLS_Oracle.sql (0 bytes, 0 INSERTs, DDL=NO) OK
SQL T_SPECIAL_CHARS -> export_output/T_SPECIAL_CHARS_Oracle.sql (0 bytes, 0 INSERTs, DDL=NO) OK
SQL T_SINGLE_ROW -> export_output/T_SINGLE_ROW_Oracle.sql (0 bytes, 0 INSERTs, DDL=NO) OK
SQL T_EMPTY -> export_output/T_EMPTY_Oracle.sql (0 bytes, 0 INSERTs, DDL=NO) OK
SQL T_WIDE_TABLE -> export_output/T_WIDE_TABLE_Oracle.sql (0 bytes, 0 INSERTs, DDL=NO) OK
SQL 社員マスタ -> export_output/jp_0_Oracle.sql (0 bytes, 0 INSERTs) OK
SQL 部署マスタ -> export_output/jp_1_Oracle.sql (0 bytes, 0 INSERTs) OK
SQL 受注データ -> export_output/jp_2_Oracle.sql (0 bytes, 0 INSERTs) OK
Phase 3.2: SQL Export - PostgreSQL
----------------------------------------
。。。
Phase 3.3: SQL Export - MySQL
----------------------------------------
。。。
Phase 3.4: SQL Export - SQLServer
----------------------------------------
。。。
Phase 4: EXP Format Export Tests (./exp_user.dmp)
========================================
。。。
Phase 5: INDEX Export Test (./exp_index_test.dmp)
========================================
IDX SQL [Oracle ] -> 3 INSERTs, DDL=YES, 3 CREATE INDEX OK
IDX SQL [PostgreSQL] -> 3 INSERTs, DDL=YES, 3 CREATE INDEX OK
IDX SQL [MySQL ] -> 3 INSERTs, DDL=YES, 3 CREATE INDEX OK
IDX SQL [SQLServer ] -> 3 INSERTs, DDL=YES, 3 CREATE INDEX OK
Phase 6: COMMENT Export Test (./exp_comment_test.dmp)
========================================
CMT SQL [Oracle ] -> 3 INSERTs, DDL=YES, 5 COMMENT ON OK
CMT SQL [PostgreSQL] -> 3 INSERTs, DDL=YES, 5 COMMENT ON OK
CMT SQL [MySQL ] -> 3 INSERTs, DDL=YES, 0 COMMENT ON OK (as SQL comments)
CMT SQL [SQLServer ] -> 3 INSERTs, DDL=YES, 0 COMMENT ON OK (as SQL comments)
========================================
EXPORT TEST RESULTS: 68 passed, 12 failed (total 80)
========================================
其中生成的一个SQL文件如下(idx_T_INDEX_TEST_Oracle.sql)
-- Table: ODV_TEST.T_INDEX_TEST
-- Generated by OraDB DUMP Viewer
BEGIN EXECUTE IMMEDIATE 'DROP TABLE "ODV_TEST"."T_INDEX_TEST" CASCADE CONSTRAINTS PURGE'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
CREATE TABLE "ODV_TEST"."T_INDEX_TEST" (
"ID" NUMBER(10, 0),
"COL_NAME" VARCHAR2(100),
"COL_EMAIL" VARCHAR2(200),
"COL_DEPT" VARCHAR2(50),
"COL_SALARY" NUMBER(10, 2),
"COL_CREATED" DATE
);
INSERT INTO "ODV_TEST"."T_INDEX_TEST" ("ID", "COL_NAME", "COL_EMAIL", "COL_DEPT", "COL_SALARY", "COL_CREATED") VALUES (1, 'Alice', 'alice@ex.com', 'Engineering', 85000, '2026/03/20 00:49:54');
INSERT INTO "ODV_TEST"."T_INDEX_TEST" ("ID", "COL_NAME", "COL_EMAIL", "COL_DEPT", "COL_SALARY", "COL_CREATED") VALUES (2, 'Bob', 'bob@ex.com', 'Sales', 72000, '2026/03/20 00:49:54');
INSERT INTO "ODV_TEST"."T_INDEX_TEST" ("ID", "COL_NAME", "COL_EMAIL", "COL_DEPT", "COL_SALARY", "COL_CREATED") VALUES (3, 'Charlie', 'charlie@ex.com', 'Engineering', 92000, '2026/03/20 00:49:54');
CREATE INDEX "IDX_INDEX_TEST_NAME" ON "ODV_TEST"."T_INDEX_TEST" ("COL_NAME" );
CREATE INDEX "IDX_INDEX_TEST_DEPT_SAL" ON "ODV_TEST"."T_INDEX_TEST" ("COL_DEPT" , "COL_SALARY" );
CREATE INDEX "IDX_INDEX_TEST_UPPER_EMAIL" ON "ODV_TEST"."T_INDEX_TEST" (UPPER("COL_EMAIL") );