4 * Date: Fri Aug 20 22:46:31 2010
6 * GNU recutils - mdb to rec converter.
10 /* Copyright (C) 2010-2019 Jose E. Marchesi */
12 /* This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
34 #define _(str) gettext (str)
42 /* Forward declarations. */
43 static void parse_args (int argc
, char **argv
);
44 static rec_db_t
process_mdb (void);
45 static rec_rset_t
process_table (MdbCatalogEntry
*entry
);
46 static char *get_field_name (MdbHandle
*mdb
, const char *table_name
, const char *col_name
);
47 static void get_relationships (MdbHandle
*mdb
);
57 char *referenced_table
;
58 char *referenced_column
;
65 char *mdb2rec_mdb_file
= NULL
;
66 char *mdb2rec_mdb_table
= NULL
;
67 bool mdb2rec_include_system
= false;
68 bool mdb2rec_keep_empty_fields
= false;
69 bool mdb2rec_list_tables
= false;
70 struct relationship_s
*relationships
;
71 size_t num_relationships
= 0;
74 * Command line options management
81 KEEP_EMPTY_FIELDS_ARG
,
85 static const struct option GNU_longOptions
[] =
88 {"system-tables", no_argument
, NULL
, SYSTEM_TABLES_ARG
},
89 {"keep-empty-fields", no_argument
, NULL
, KEEP_EMPTY_FIELDS_ARG
},
90 {"list-tables", no_argument
, NULL
, LIST_TABLES_ARG
},
99 recutl_print_help (void)
101 /* TRANSLATORS: --help output, mdb2rec synopsis.
104 Usage: mdb2rec [OPTIONS]... MDB_FILE [TABLE]\n"));
106 /* TRANSLATORS: --help output, mdb2rec short description.
109 Convert an mdb file into a rec file.\n"), stdout
);
112 /* TRANSLATORS: --help output, mdb2rec options.
115 -s, --system-tables include system tables.\n\
116 -e, --keep-empty-fields don't prune empty fields in the rec\n\
118 -l, --list-tables dump a list of the table names contained\n\
119 in the mdb file.\n"),
122 recutl_print_help_common ();
124 recutl_print_help_footer ();
128 parse_args (int argc
,
134 while ((ret
= getopt_long (argc
,
144 case SYSTEM_TABLES_ARG
:
147 mdb2rec_include_system
= true;
150 case KEEP_EMPTY_FIELDS_ARG
:
153 mdb2rec_keep_empty_fields
= true;
156 case LIST_TABLES_ARG
:
159 mdb2rec_list_tables
= true;
169 /* Read the name of the mdb file. */
170 if ((argc
- optind
) > 2)
172 recutl_print_help ();
177 mdb2rec_mdb_file
= argv
[optind
++];
179 if ((argc
- optind
) > 0)
181 mdb2rec_mdb_table
= argv
[optind
++];
187 get_relationships (MdbHandle
*mdb
)
193 table
= mdb_read_table_by_name (mdb
,
196 if ((!table
) || (table
->num_rows
== 0))
201 mdb_read_columns (table
);
202 for (i
= 0; i
< 4; i
++)
204 bound
[i
] = xmalloc (MDB_BIND_SIZE
);
207 mdb_bind_column_by_name (table
, "szColumn", bound
[0], NULL
);
208 mdb_bind_column_by_name (table
, "szObject", bound
[1], NULL
);
209 mdb_bind_column_by_name (table
, "szReferencedColumn", bound
[2], NULL
);
210 mdb_bind_column_by_name (table
, "szReferencedObject", bound
[3], NULL
);
211 mdb_rewind_table (table
);
213 num_relationships
= table
->num_rows
;
214 relationships
= xmalloc (sizeof (struct relationship_s
) * num_relationships
);
217 while (mdb_fetch_row (table
))
219 relationships
[i
].column
= xstrdup (bound
[0]);
220 relationships
[i
].table
= xstrdup (bound
[1]);
221 relationships
[i
].referenced_column
= xstrdup (bound
[2]);
222 relationships
[i
].referenced_table
= xstrdup (bound
[3]);
228 get_field_name (MdbHandle
*mdb
,
229 const char *table_name
,
230 const char *col_name
)
233 char *referenced_table
;
234 char *referenced_column
;
237 /* If this field is a relationship to other table, build a compound
240 referenced_table
= NULL
;
241 referenced_column
= NULL
;
242 for (i
= 0; i
< num_relationships
; i
++)
244 if ((strcmp (relationships
[i
].table
, table_name
) == 0)
245 && (strcmp (relationships
[i
].column
, col_name
) == 0))
248 rec_field_name_normalise (relationships
[i
].referenced_table
);
249 if (!referenced_table
)
251 recutl_fatal (_("failed to normalise record type name %s\n"),
252 relationships
[i
].referenced_table
);
256 rec_field_name_normalise (relationships
[i
].referenced_column
);
257 if (!referenced_column
)
259 recutl_fatal (_("failed to normalise field name %s\n"),
260 relationships
[i
].referenced_column
);
267 field_name
= rec_field_name_normalise (col_name
);
270 recutl_fatal (_("failed to normalise field name %s\n"),
274 if (referenced_table
&& referenced_column
)
276 /* TODO: handle foreign keys. */
283 process_table (MdbCatalogEntry
*entry
)
297 #define TYPE_VALUE_SIZE 256
298 char type_value
[TYPE_VALUE_SIZE
];
303 table_name
= entry
->object_name
;
304 table
= mdb_read_table (entry
);
306 /* Create the record set. */
307 rset
= rec_rset_new ();
309 recutl_out_of_memory ();
311 /* Create the record descriptor and add the %rec: entry. */
312 field_name
= rec_field_name_normalise (table_name
);
315 recutl_fatal (_("failed to normalise record type name %s\n"),
319 record
= rec_record_new ();
320 field
= rec_field_new ("%rec", field_name
);
321 rec_mset_append (rec_record_mset (record
), MSET_FIELD
, (void *) field
, MSET_ANY
);
324 /* Get the columns of the table. */
325 mdb_read_columns (table
);
327 /* Loop on the columns. We will define the key and the types. */
328 for (i
= 0; i
< table
->num_cols
; i
++)
330 col
= g_ptr_array_index (table
->columns
, i
);
331 column_name
= col
->name
;
333 normalised
= rec_field_name_normalise (column_name
);
336 recutl_fatal (_("failed to normalise the field name %s\n"),
340 /* Emit a field type specification. */
341 switch (col
->col_type
)
345 snprintf (type_value
, TYPE_VALUE_SIZE
,
346 "%s bool", normalised
);
351 snprintf (type_value
, TYPE_VALUE_SIZE
,
352 "%s range 256", normalised
);
359 snprintf (type_value
, TYPE_VALUE_SIZE
,
360 "%s int", normalised
);
367 snprintf (type_value
, TYPE_VALUE_SIZE
,
368 "%s real", normalised
);
373 snprintf (type_value
, TYPE_VALUE_SIZE
,
374 "%s date", normalised
);
379 if (col
->col_size
> 0)
381 snprintf (type_value
, TYPE_VALUE_SIZE
,
382 "%s size %d", normalised
, col
->col_size
);
395 if (type_value
[0] != 0)
397 /* Insert a type field for this column. */
398 field
= rec_field_new ("%type", type_value
);
399 rec_mset_append (rec_record_mset (record
), MSET_FIELD
, (void *) field
, MSET_ANY
);
403 rec_rset_set_descriptor (rset
, record
);
405 /* Add the records for this table. */
406 mdb_rewind_table (table
);
408 bound_values
= xmalloc (table
->num_cols
* sizeof(char *));
409 bound_lens
= xmalloc(table
->num_cols
* sizeof(int));
410 for (i
= 0; i
< table
->num_cols
; i
++)
412 bound_values
[i
] = xmalloc (MDB_BIND_SIZE
);
413 mdb_bind_column (table
, i
+1, bound_values
[i
], &bound_lens
[i
]);
416 while (mdb_fetch_row (table
))
418 record
= rec_record_new ();
420 recutl_out_of_memory ();
422 for (i
= 0; i
< table
->num_cols
; i
++)
424 col
= g_ptr_array_index (table
->columns
, i
);
426 if (col
->col_type
== MDB_OLE
)
431 /* Compute the value of the field. */
432 field_value
= xmalloc (bound_lens
[i
] + 1);
433 memcpy (field_value
, bound_values
[i
], bound_lens
[i
]);
434 field_value
[bound_lens
[i
]] = '\0';
436 if (mdb2rec_keep_empty_fields
|| (strlen (field_value
) > 0))
438 /* Create the field and append it into the record. */
439 field
= rec_field_new (get_field_name (mdb
, table_name
, column_name
),
443 recutl_fatal (_("invalid field name %s.\n"), column_name
);
446 rec_mset_append (rec_record_mset (record
), MSET_FIELD
, (void *) field
, MSET_ANY
);
452 rec_record_set_container (record
, rset
);
453 rec_mset_append (rec_rset_mset (rset
), MSET_RECORD
, (void *) record
, MSET_ANY
);
456 mdb_free_tabledef (table
);
466 MdbCatalogEntry
*entry
;
470 /* Create the rec database. */
473 recutl_out_of_memory ();
475 /* Initialize libmdb and open the input file. */
477 mdb_set_date_fmt ("%Y-%m-%dT%H:%M:%S%z"); /* ISO 8601 */
479 mdb
= mdb_open (mdb2rec_mdb_file
, MDB_NOFLAGS
);
482 recutl_fatal (_("could not open file %s\n"),
486 /* Read the catalog. */
487 if (!mdb_read_catalog (mdb
, MDB_TABLE
))
489 recutl_fatal (_("file does not appear to be an Access database\n"));
492 /* Read relationships from the database. Relationships in mdb files
493 are stored in the MSysRelationships table. */
494 get_relationships (mdb
);
496 /* Iterate on the catalogs. */
497 for (i
= 0; i
< mdb
->num_catalog
; i
++)
499 entry
= g_ptr_array_index (mdb
->catalog
, i
);
501 table_name
= rec_field_name_normalise (entry
->object_name
);
503 if ((entry
->object_type
== MDB_TABLE
)
504 && (mdb_is_user_table (entry
) || mdb2rec_include_system
)
505 && (!mdb2rec_mdb_table
|| (strcmp (mdb2rec_mdb_table
, table_name
) == 0)))
507 if (mdb2rec_list_tables
)
509 fprintf (stdout
, "%s\n", table_name
);
513 rec_db_insert_rset (db
,
514 process_table (entry
),
524 main (int argc
, char *argv
[])
530 recutl_init ("mdb2rec");
532 parse_args (argc
, argv
);
537 writer
= rec_writer_new (stdout
);
538 rec_write_db (writer
, db
);
540 rec_writer_destroy (writer
);
553 /* End of mdb2rec.c */