/src/mdbtools/src/libmdb/backend.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* MDB Tools - A library for reading MS Access database files |
2 | | * Copyright (C) 2000-2011 Brian Bruns and others |
3 | | * |
4 | | * This library is free software; you can redistribute it and/or |
5 | | * modify it under the terms of the GNU Library General Public |
6 | | * License as published by the Free Software Foundation; either |
7 | | * version 2 of the License, or (at your option) any later version. |
8 | | * |
9 | | * This library is distributed in the hope that it will be useful, |
10 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | | * Library General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU Library General Public |
15 | | * License along with this library; if not, write to the Free Software |
16 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
17 | | */ |
18 | | |
19 | | /* |
20 | | ** functions to deal with different backend database engines |
21 | | */ |
22 | | |
23 | | #include "mdbtools.h" |
24 | | #include "mdbprivate.h" |
25 | | |
26 | | /* Access data types */ |
27 | | static const MdbBackendType mdb_access_types[] = { |
28 | | [MDB_BOOL] = { .name = "Boolean" }, |
29 | | [MDB_BYTE] = { .name = "Byte" }, |
30 | | [MDB_INT] = { .name = "Integer" }, |
31 | | [MDB_LONGINT] = { .name = "Long Integer" }, |
32 | | [MDB_MONEY] = { .name = "Currency" }, |
33 | | [MDB_FLOAT] = { .name = "Single" }, |
34 | | [MDB_DOUBLE] = { .name = "Double" }, |
35 | | [MDB_DATETIME] = { .name = "DateTime" }, |
36 | | [MDB_BINARY] = { .name = "Binary" }, |
37 | | [MDB_TEXT] = { .name = "Text", .needs_char_length = 1 }, |
38 | | [MDB_OLE] = { .name = "OLE", .needs_byte_length = 1 }, |
39 | | [MDB_MEMO] = { .name = "Memo/Hyperlink", .needs_char_length = 1 }, |
40 | | [MDB_REPID] = { .name = "Replication ID" }, |
41 | | [MDB_NUMERIC] = { .name = "Numeric", .needs_precision = 1, .needs_scale = 1 }, |
42 | | }; |
43 | | |
44 | | /* Oracle data types */ |
45 | | static const MdbBackendType mdb_oracle_types[] = { |
46 | | [MDB_BOOL] = { .name = "NUMBER(1)" }, |
47 | | [MDB_BYTE] = { .name = "NUMBER(3)" }, |
48 | | [MDB_INT] = { .name = "NUMBER(5)" }, |
49 | | [MDB_LONGINT] = { .name = "NUMBER(11)" }, |
50 | | [MDB_MONEY] = { .name = "NUMBER(15,2)" }, |
51 | | [MDB_FLOAT] = { .name = "FLOAT" }, |
52 | | [MDB_DOUBLE] = { .name = "FLOAT" }, |
53 | | [MDB_DATETIME] = { .name = "TIMESTAMP" }, |
54 | | [MDB_BINARY] = { .name = "BINARY" }, |
55 | | [MDB_TEXT] = { .name = "VARCHAR2", .needs_char_length= 1 }, |
56 | | [MDB_OLE] = { .name = "BLOB" }, |
57 | | [MDB_MEMO] = { .name = "CLOB" }, |
58 | | [MDB_REPID] = { .name = "NUMBER", .needs_precision = 1 }, |
59 | | [MDB_NUMERIC] = { .name = "NUMBER", .needs_precision = 1 }, |
60 | | }; |
61 | | static const MdbBackendType mdb_oracle_shortdate_type = |
62 | | { .name = "DATE" }; |
63 | | |
64 | | /* Sybase/MSSQL data types */ |
65 | | static const MdbBackendType mdb_sybase_types[] = { |
66 | | [MDB_BOOL] = { .name = "bit" }, |
67 | | [MDB_BYTE] = { .name = "char", .needs_byte_length = 1 }, |
68 | | [MDB_INT] = { .name = "smallint" }, |
69 | | [MDB_LONGINT] = { .name = "int" }, |
70 | | [MDB_MONEY] = { .name = "money" }, |
71 | | [MDB_FLOAT] = { .name = "real" }, |
72 | | [MDB_DOUBLE] = { .name = "float" }, |
73 | | [MDB_DATETIME] = { .name = "smalldatetime" }, |
74 | | [MDB_BINARY] = { .name = "varbinary", .needs_byte_length = 1 }, |
75 | | [MDB_TEXT] = { .name = "nvarchar", .needs_char_length = 1 }, |
76 | | [MDB_OLE] = { .name = "varbinary(max)" }, |
77 | | [MDB_MEMO] = { .name = "nvarchar(max)" }, |
78 | | [MDB_REPID] = { .name = "Sybase_Replication ID" }, |
79 | | [MDB_NUMERIC] = { .name = "numeric", .needs_precision = 1, .needs_scale = 1 }, |
80 | | }; |
81 | | static const MdbBackendType mdb_sybase_shortdate_type = |
82 | | { .name = "DATE" }; |
83 | | |
84 | | /* Postgres data types */ |
85 | | static const MdbBackendType mdb_postgres_types[] = { |
86 | | [MDB_BOOL] = { .name = "BOOLEAN" }, |
87 | | [MDB_BYTE] = { .name = "SMALLINT" }, |
88 | | [MDB_INT] = { .name = "INTEGER" }, |
89 | | [MDB_LONGINT] = { .name = "INTEGER" }, /* bigint */ |
90 | | [MDB_MONEY] = { .name = "NUMERIC(15,2)" }, /* money deprecated ? */ |
91 | | [MDB_FLOAT] = { .name = "REAL" }, |
92 | | [MDB_DOUBLE] = { .name = "DOUBLE PRECISION" }, |
93 | | [MDB_DATETIME] = { .name = "TIMESTAMP WITHOUT TIME ZONE" }, |
94 | | [MDB_BINARY] = { .name = "BYTEA" }, |
95 | | [MDB_TEXT] = { .name = "VARCHAR", .needs_char_length = 1 }, |
96 | | [MDB_OLE] = { .name = "BYTEA" }, |
97 | | [MDB_MEMO] = { .name = "TEXT" }, |
98 | | [MDB_REPID] = { .name = "UUID" }, |
99 | | [MDB_NUMERIC] = { .name = "NUMERIC", .needs_precision = 1, .needs_scale = 1 }, |
100 | | }; |
101 | | static const MdbBackendType mdb_postgres_shortdate_type = |
102 | | { .name = "DATE" }; |
103 | | static const MdbBackendType mdb_postgres_serial_type = |
104 | | { .name = "SERIAL" }; |
105 | | |
106 | | /* MySQL data types */ |
107 | | static const MdbBackendType mdb_mysql_types[] = { |
108 | | [MDB_BOOL] = { .name = "boolean" }, |
109 | | [MDB_BYTE] = { .name = "tinyint" }, |
110 | | [MDB_INT] = { .name = "smallint" }, |
111 | | [MDB_LONGINT] = { .name = "int" }, |
112 | | [MDB_MONEY] = { .name = "float" }, |
113 | | [MDB_FLOAT] = { .name = "float" }, |
114 | | [MDB_DOUBLE] = { .name = "float" }, |
115 | | [MDB_DATETIME] = { .name = "datetime" }, |
116 | | [MDB_BINARY] = { .name = "blob" }, |
117 | | [MDB_TEXT] = { .name = "varchar", .needs_char_length = 1 }, |
118 | | [MDB_OLE] = { .name = "blob" }, |
119 | | [MDB_MEMO] = { .name = "text" }, |
120 | | [MDB_REPID] = { .name = "char(38)" }, |
121 | | [MDB_NUMERIC] = { .name = "numeric", .needs_precision = 1, .needs_scale = 1 }, |
122 | | }; |
123 | | static const MdbBackendType mdb_mysql_shortdate_type = |
124 | | { .name = "date" }; |
125 | | /* We can't use the MySQL SERIAL type because that uses a bigint which |
126 | | * is 64 bits wide, whereas MDB long ints are 32 bits */ |
127 | | static const MdbBackendType mdb_mysql_serial_type = |
128 | | { .name = "int not null auto_increment unique" }; |
129 | | |
130 | | /* sqlite data types */ |
131 | | static const MdbBackendType mdb_sqlite_types[] = { |
132 | | [MDB_BOOL] = { .name = "INTEGER" }, |
133 | | [MDB_BYTE] = { .name = "INTEGER" }, |
134 | | [MDB_INT] = { .name = "INTEGER" }, |
135 | | [MDB_LONGINT] = { .name = "INTEGER" }, |
136 | | [MDB_MONEY] = { .name = "REAL" }, |
137 | | [MDB_FLOAT] = { .name = "REAL" }, |
138 | | [MDB_DOUBLE] = { .name = "REAL" }, |
139 | | [MDB_DATETIME] = { .name = "DateTime" }, |
140 | | [MDB_BINARY] = { .name = "BLOB" }, |
141 | | [MDB_TEXT] = { .name = "varchar" }, |
142 | | [MDB_OLE] = { .name = "BLOB" }, |
143 | | [MDB_MEMO] = { .name = "TEXT" }, |
144 | | [MDB_REPID] = { .name = "INTEGER" }, |
145 | | [MDB_NUMERIC] = { .name = "INTEGER" }, |
146 | | }; |
147 | | |
148 | | enum { |
149 | | MDB_BACKEND_ACCESS = 1, |
150 | | MDB_BACKEND_ORACLE, |
151 | | MDB_BACKEND_SYBASE, |
152 | | MDB_BACKEND_POSTGRES, |
153 | | MDB_BACKEND_MYSQL, |
154 | | MDB_BACKEND_SQLITE, |
155 | | }; |
156 | | |
157 | | static void mdb_drop_backend(gpointer key, gpointer value, gpointer data); |
158 | | |
159 | | |
160 | 0 | static gchar *passthrough_unchanged(const gchar *str) { |
161 | 0 | return (gchar *)str; |
162 | 0 | } |
163 | | |
164 | 0 | static gchar *to_lower_case(const gchar *str) { |
165 | 0 | return g_utf8_strdown(str, -1); |
166 | 0 | } |
167 | | |
168 | | /** |
169 | | * Convenience function to replace an input string with its database specific normalised version. |
170 | | * |
171 | | * This function throws away the input string after normalisation, freeing its memory, and replaces it with a new |
172 | | * normalised version allocated on the stack. |
173 | | * |
174 | | * @param mdb Database specific MDB handle containing pointers to utility methods |
175 | | * @param str string to normalise |
176 | | * @return a pointer to the normalised version of the input string |
177 | | */ |
178 | 0 | gchar *mdb_normalise_and_replace(MdbHandle *mdb, gchar **str) { |
179 | 0 | gchar *normalised_str = mdb->default_backend->normalise_case(*str); |
180 | 0 | if (normalised_str != *str) { |
181 | | /* Free and replace the old string only and only if a new string was created at a different memory location |
182 | | * so that we can account for the case where strings a just passed through unchanged. |
183 | | */ |
184 | 0 | free(*str); |
185 | 0 | *str = normalised_str; |
186 | 0 | } |
187 | 0 | return *str; |
188 | 0 | } |
189 | | |
190 | | static gchar* |
191 | 0 | quote_generic(const gchar *value, gchar quote_char, gchar escape_char) { |
192 | 0 | gchar *result, *pr; |
193 | 0 | unsigned char c; |
194 | |
|
195 | 0 | pr = result = g_malloc(1+4*strlen(value)+2); // worst case scenario |
196 | |
|
197 | 0 | *pr++ = quote_char; |
198 | 0 | while ((c=*(unsigned char*)value++)) { |
199 | 0 | if (c<32) { |
200 | 0 | sprintf(pr, "\\%03o", c); |
201 | 0 | pr+=4; |
202 | 0 | continue; |
203 | 0 | } |
204 | 0 | else if (c == quote_char) { |
205 | 0 | *pr++ = escape_char; |
206 | 0 | } |
207 | 0 | *pr++ = c; |
208 | 0 | } |
209 | 0 | *pr++ = quote_char; |
210 | 0 | *pr++ = '\0'; |
211 | 0 | return result; |
212 | 0 | } |
213 | | static gchar* |
214 | 0 | quote_schema_name_bracket_merge(const gchar* schema, const gchar *name) { |
215 | 0 | if (schema) |
216 | 0 | return g_strconcat("[", schema, "_", name, "]", NULL); |
217 | 0 | else |
218 | 0 | return g_strconcat("[", name, "]", NULL); |
219 | 0 | } |
220 | | |
221 | | /* |
222 | | * For backends that really does support schema |
223 | | * returns "name" or "schema"."name" |
224 | | */ |
225 | | static gchar* |
226 | | quote_schema_name_dquote(const gchar* schema, const gchar *name) |
227 | 0 | { |
228 | 0 | if (schema) { |
229 | 0 | gchar *frag1 = quote_generic(schema, '"', '"'); |
230 | 0 | gchar *frag2 = quote_generic(name, '"', '"'); |
231 | 0 | gchar *result = g_strconcat(frag1, ".", frag2, NULL); |
232 | 0 | g_free(frag1); |
233 | 0 | g_free(frag2); |
234 | 0 | return result; |
235 | 0 | } |
236 | 0 | return quote_generic(name, '"', '"'); |
237 | 0 | } |
238 | | |
239 | | /* |
240 | | * For backends that really do NOT support schema |
241 | | * returns "name" or "schema_name" |
242 | | */ |
243 | | /* |
244 | | static gchar* |
245 | | quote_schema_name_dquote_merge(const gchar* schema, const gchar *name) |
246 | | { |
247 | | if (schema) { |
248 | | gchar *combined = g_strconcat(schema, "_", name, NULL); |
249 | | gchar *result = quote_generic(combined, '"', '"'); |
250 | | g_free(combined); |
251 | | return result; |
252 | | } |
253 | | return quote_generic(name, '"', '"'); |
254 | | }*/ |
255 | | |
256 | | static gchar* |
257 | | quote_schema_name_rquotes_merge(const gchar* schema, const gchar *name) |
258 | 0 | { |
259 | 0 | if (schema) { |
260 | 0 | gchar *combined = g_strconcat(schema, "_", name, NULL); |
261 | 0 | gchar *result = quote_generic(combined, '`', '`'); |
262 | 0 | g_free(combined); |
263 | 0 | return result; |
264 | 0 | } |
265 | 0 | return quote_generic(name, '`', '`'); |
266 | 0 | } |
267 | | |
268 | | static gchar* |
269 | | quote_with_squotes(const gchar* value) |
270 | 0 | { |
271 | 0 | return quote_generic(value, '\'', '\''); |
272 | 0 | } |
273 | | |
274 | | const MdbBackendType* |
275 | 0 | mdb_get_colbacktype(const MdbColumn *col) { |
276 | 0 | MdbBackend *backend = col->table->entry->mdb->default_backend; |
277 | 0 | int col_type = col->col_type; |
278 | 0 | if (col_type > MDB_NUMERIC) |
279 | 0 | return NULL; |
280 | 0 | if (col_type == MDB_LONGINT && col->is_long_auto && backend->type_autonum) |
281 | 0 | return backend->type_autonum; |
282 | 0 | if (col_type == MDB_DATETIME && backend->type_shortdate) { |
283 | 0 | if (mdb_col_is_shortdate(col)) |
284 | 0 | return backend->type_shortdate; |
285 | 0 | } |
286 | 0 | if (!backend->types_table[col_type].name[0]) |
287 | 0 | return NULL; |
288 | 0 | return &backend->types_table[col_type]; |
289 | 0 | } |
290 | | |
291 | | const char * |
292 | | mdb_get_colbacktype_string(const MdbColumn *col) |
293 | 0 | { |
294 | 0 | const MdbBackendType *type = mdb_get_colbacktype(col); |
295 | 0 | if (!type) { |
296 | | // return NULL; |
297 | 0 | static TLS char buf[16]; |
298 | 0 | snprintf(buf, sizeof(buf), "Unknown_%04x", col->col_type); |
299 | 0 | return buf; |
300 | 0 | } |
301 | 0 | return type->name; |
302 | 0 | } |
303 | | int |
304 | | mdb_colbacktype_takes_length(const MdbColumn *col) |
305 | 0 | { |
306 | 0 | const MdbBackendType *type = mdb_get_colbacktype(col); |
307 | 0 | if (!type) return 0; |
308 | 0 | return type->needs_precision || type->needs_char_length || type->needs_byte_length; |
309 | 0 | } |
310 | | static int |
311 | | mdb_colbacktype_takes_length_in_characters(const MdbColumn *col) |
312 | 0 | { |
313 | 0 | const MdbBackendType *type = mdb_get_colbacktype(col); |
314 | 0 | if (!type) return 0; |
315 | 0 | return type->needs_char_length; |
316 | 0 | } |
317 | | |
318 | | /** |
319 | | * mdb_init_backends |
320 | | * |
321 | | * Initializes the mdb_backends hash and loads the builtin backends. |
322 | | * Use mdb_remove_backends() to destroy this hash when done. |
323 | | */ |
324 | | void mdb_init_backends(MdbHandle *mdb) |
325 | 19 | { |
326 | 19 | if (mdb->backends) { |
327 | 0 | mdb_remove_backends(mdb); |
328 | 0 | } |
329 | 19 | mdb->backends = g_hash_table_new(g_str_hash, g_str_equal); |
330 | | |
331 | 19 | mdb_register_backend(mdb, "access", |
332 | 19 | MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_DEFVALUES, |
333 | 19 | mdb_access_types, NULL, NULL, |
334 | 19 | "Date()", "Date()", |
335 | 19 | NULL, |
336 | 19 | NULL, |
337 | 19 | "-- That file uses encoding %s\n", |
338 | 19 | "DROP TABLE %s;\n", |
339 | 19 | NULL, |
340 | 19 | NULL, |
341 | 19 | NULL, |
342 | 19 | NULL, |
343 | 19 | NULL, |
344 | 19 | quote_schema_name_bracket_merge); |
345 | 19 | mdb_register_backend(mdb, "sybase", |
346 | 19 | MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_CST_NOTEMPTY|MDB_SHEXP_COMMENTS|MDB_SHEXP_DEFVALUES, |
347 | 19 | mdb_sybase_types, &mdb_sybase_shortdate_type, NULL, |
348 | 19 | "getdate()", "getdate()", |
349 | 19 | NULL, |
350 | 19 | NULL, |
351 | 19 | "-- That file uses encoding %s\n", |
352 | 19 | "DROP TABLE %s;\n", |
353 | 19 | "ALTER TABLE %s ADD CHECK (%s <>'');\n", |
354 | 19 | "COMMENT ON COLUMN %s.%s IS %s;\n", |
355 | 19 | NULL, |
356 | 19 | "COMMENT ON TABLE %s IS %s;\n", |
357 | 19 | NULL, |
358 | 19 | quote_schema_name_dquote); |
359 | 19 | mdb_register_backend(mdb, "oracle", |
360 | 19 | MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_COMMENTS|MDB_SHEXP_INDEXES|MDB_SHEXP_RELATIONS|MDB_SHEXP_DEFVALUES, |
361 | 19 | mdb_oracle_types, &mdb_oracle_shortdate_type, NULL, |
362 | 19 | "current_date", "sysdate", |
363 | 19 | NULL, |
364 | 19 | NULL, |
365 | 19 | "-- That file uses encoding %s\n", |
366 | 19 | "DROP TABLE %s;\n", |
367 | 19 | NULL, |
368 | 19 | "COMMENT ON COLUMN %s.%s IS %s;\n", |
369 | 19 | NULL, |
370 | 19 | "COMMENT ON TABLE %s IS %s;\n", |
371 | 19 | NULL, |
372 | 19 | quote_schema_name_dquote); |
373 | 19 | mdbi_register_backend2(mdb, "postgres", |
374 | 19 | MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_CST_NOTEMPTY|MDB_SHEXP_COMMENTS|MDB_SHEXP_INDEXES|MDB_SHEXP_RELATIONS|MDB_SHEXP_DEFVALUES|MDB_SHEXP_BULK_INSERT, |
375 | 19 | mdb_postgres_types, &mdb_postgres_shortdate_type, &mdb_postgres_serial_type, |
376 | 19 | "current_date", "now()", |
377 | 19 | "%Y-%m-%d %H:%M:%S", |
378 | 19 | "%Y-%m-%d", |
379 | 19 | "SET client_encoding = '%s';\n", |
380 | 19 | "CREATE TABLE IF NOT EXISTS %s\n", |
381 | 19 | "DROP TABLE IF EXISTS %s;\n", |
382 | 19 | "ALTER TABLE %s ADD CHECK (%s <>'');\n", |
383 | 19 | "COMMENT ON COLUMN %s.%s IS %s;\n", |
384 | 19 | NULL, |
385 | 19 | "COMMENT ON TABLE %s IS %s;\n", |
386 | 19 | NULL, |
387 | 19 | quote_schema_name_dquote, |
388 | 19 | to_lower_case); |
389 | 19 | mdb_register_backend(mdb, "mysql", |
390 | 19 | MDB_SHEXP_DROPTABLE|MDB_SHEXP_CST_NOTNULL|MDB_SHEXP_CST_NOTEMPTY|MDB_SHEXP_INDEXES|MDB_SHEXP_RELATIONS|MDB_SHEXP_DEFVALUES|MDB_SHEXP_BULK_INSERT, |
391 | 19 | mdb_mysql_types, &mdb_mysql_shortdate_type, &mdb_mysql_serial_type, |
392 | 19 | "current_date", "now()", |
393 | 19 | "%Y-%m-%d %H:%M:%S", |
394 | 19 | "%Y-%m-%d", |
395 | 19 | "-- That file uses encoding %s\n", |
396 | 19 | "DROP TABLE IF EXISTS %s;\n", |
397 | 19 | "ALTER TABLE %s ADD CHECK (%s <>'');\n", |
398 | 19 | NULL, |
399 | 19 | "COMMENT %s", |
400 | 19 | NULL, |
401 | 19 | "COMMENT %s", |
402 | 19 | quote_schema_name_rquotes_merge); |
403 | 19 | mdb_register_backend(mdb, "sqlite", |
404 | 19 | MDB_SHEXP_DROPTABLE|MDB_SHEXP_DEFVALUES|MDB_SHEXP_BULK_INSERT, |
405 | 19 | mdb_sqlite_types, NULL, NULL, |
406 | 19 | "date('now')", "date('now')", |
407 | 19 | "%Y-%m-%d %H:%M:%S", |
408 | 19 | "%Y-%m-%d", |
409 | 19 | "-- That file uses encoding %s\n", |
410 | 19 | "DROP TABLE IF EXISTS %s;\n", |
411 | 19 | NULL, |
412 | 19 | NULL, |
413 | 19 | NULL, |
414 | 19 | NULL, |
415 | 19 | NULL, |
416 | 19 | quote_schema_name_rquotes_merge); |
417 | 19 | } |
418 | | |
419 | | MdbBackend *mdbi_register_backend2(MdbHandle *mdb, char *backend_name, guint32 capabilities, |
420 | | const MdbBackendType *backend_type, const MdbBackendType *type_shortdate, const MdbBackendType *type_autonum, |
421 | | const char *short_now, const char *long_now, |
422 | | const char *date_fmt, const char *shortdate_fmt, |
423 | | const char *charset_statement, |
424 | | const char *create_table_statement, |
425 | | const char *drop_statement, |
426 | | const char *constraint_not_empty_statement, |
427 | | const char *column_comment_statement, |
428 | | const char *per_column_comment_statement, |
429 | | const char *table_comment_statement, |
430 | | const char *per_table_comment_statement, |
431 | | gchar* (*quote_schema_name)(const gchar*, const gchar*), |
432 | 114 | gchar* (*normalise_case)(const gchar*)) { |
433 | 114 | MdbBackend *backend = g_malloc0(sizeof(MdbBackend)); |
434 | 114 | backend->capabilities = capabilities; |
435 | 114 | backend->types_table = backend_type; |
436 | 114 | backend->type_shortdate = type_shortdate; |
437 | 114 | backend->type_autonum = type_autonum; |
438 | 114 | backend->short_now = short_now; |
439 | 114 | backend->long_now = long_now; |
440 | 114 | backend->date_fmt = date_fmt; |
441 | 114 | backend->shortdate_fmt = shortdate_fmt; |
442 | 114 | backend->charset_statement = charset_statement; |
443 | 114 | backend->create_table_statement = create_table_statement; |
444 | 114 | backend->drop_statement = drop_statement; |
445 | 114 | backend->constaint_not_empty_statement = constraint_not_empty_statement; |
446 | 114 | backend->column_comment_statement = column_comment_statement; |
447 | 114 | backend->per_column_comment_statement = per_column_comment_statement; |
448 | 114 | backend->table_comment_statement = table_comment_statement; |
449 | 114 | backend->per_table_comment_statement = per_table_comment_statement; |
450 | 114 | backend->quote_schema_name = quote_schema_name; |
451 | 114 | backend->normalise_case = normalise_case; |
452 | 114 | g_hash_table_insert(mdb->backends, backend_name, backend); |
453 | 114 | return backend; |
454 | 114 | } |
455 | | |
456 | | void mdb_register_backend(MdbHandle *mdb, char *backend_name, guint32 capabilities, |
457 | | const MdbBackendType *backend_type, const MdbBackendType *type_shortdate, const MdbBackendType *type_autonum, |
458 | | const char *short_now, const char *long_now, |
459 | | const char *date_fmt, const char *shortdate_fmt, |
460 | | const char *charset_statement, |
461 | | const char *drop_statement, |
462 | | const char *constraint_not_empty_statement, |
463 | | const char *column_comment_statement, |
464 | | const char *per_column_comment_statement, |
465 | | const char *table_comment_statement, |
466 | | const char *per_table_comment_statement, |
467 | | gchar* (*quote_schema_name)(const gchar*, const gchar*)) |
468 | 95 | { |
469 | 95 | mdbi_register_backend2(mdb, backend_name, capabilities, |
470 | 95 | backend_type, type_shortdate, type_autonum, |
471 | 95 | short_now, long_now, |
472 | 95 | date_fmt, shortdate_fmt, |
473 | 95 | charset_statement, |
474 | 95 | "CREATE TABLE %s\n", |
475 | 95 | drop_statement, |
476 | 95 | constraint_not_empty_statement, |
477 | 95 | column_comment_statement, |
478 | 95 | per_column_comment_statement, |
479 | 95 | table_comment_statement, |
480 | 95 | per_table_comment_statement, |
481 | 95 | quote_schema_name, |
482 | 95 | passthrough_unchanged); |
483 | 95 | } |
484 | | |
485 | | /** |
486 | | * mdb_remove_backends |
487 | | * |
488 | | * Removes all entries from and destroys the mdb_backends hash. |
489 | | */ |
490 | | void |
491 | | mdb_remove_backends(MdbHandle *mdb) |
492 | 19 | { |
493 | 19 | g_hash_table_foreach(mdb->backends, mdb_drop_backend, NULL); |
494 | 19 | g_hash_table_destroy(mdb->backends); |
495 | 19 | } |
496 | | static void mdb_drop_backend(gpointer key, gpointer value, gpointer data) |
497 | 114 | { |
498 | 114 | MdbBackend *backend = (MdbBackend *)value; |
499 | 114 | g_free (backend); |
500 | 114 | } |
501 | | |
502 | | /** |
503 | | * mdb_set_default_backend |
504 | | * @mdb: Handle to open MDB database file |
505 | | * @backend_name: Name of the backend to set as default |
506 | | * |
507 | | * Sets the default backend of the handle @mdb to @backend_name. |
508 | | * |
509 | | * Returns: 1 if successful, 0 if unsuccessful. |
510 | | */ |
511 | | int mdb_set_default_backend(MdbHandle *mdb, const char *backend_name) |
512 | 19 | { |
513 | 19 | MdbBackend *backend; |
514 | | |
515 | 19 | if (!mdb->backends) { |
516 | 19 | mdb_init_backends(mdb); |
517 | 19 | } |
518 | 19 | backend = (MdbBackend *) g_hash_table_lookup(mdb->backends, backend_name); |
519 | 19 | if (backend) { |
520 | 19 | mdb->default_backend = backend; |
521 | 19 | g_free(mdb->backend_name); // NULL is ok |
522 | 19 | mdb->backend_name = (char *) g_strdup(backend_name); |
523 | 19 | mdb->relationships_table = NULL; |
524 | 19 | if (backend->date_fmt) { |
525 | 0 | mdb_set_date_fmt(mdb, backend->date_fmt); |
526 | 19 | } else { |
527 | 19 | mdb_set_date_fmt(mdb, "%x %X"); |
528 | 19 | } |
529 | 19 | if (backend->shortdate_fmt) { |
530 | 0 | mdb_set_shortdate_fmt(mdb, backend->shortdate_fmt); |
531 | 19 | } else { |
532 | 19 | mdb_set_shortdate_fmt(mdb, "%x"); |
533 | 19 | } |
534 | 19 | } |
535 | 19 | return (backend != NULL); |
536 | 19 | } |
537 | | |
538 | | |
539 | | /** |
540 | | * Generates index name based on backend. |
541 | | * |
542 | | * You should free() the returned value once you are done with it. |
543 | | * |
544 | | * @param backend backend we are generating indexes for |
545 | | * @param table table being processed |
546 | | * @param idx index being processed |
547 | | * @return the index name |
548 | | */ |
549 | | static char * |
550 | | mdb_get_index_name(int backend, MdbTableDef *table, MdbIndex *idx) |
551 | 0 | { |
552 | 0 | char *index_name; |
553 | |
|
554 | 0 | switch(backend){ |
555 | 0 | case MDB_BACKEND_MYSQL: |
556 | | // appending table name to index often makes it too long for mysql |
557 | 0 | if (idx->index_type==1) |
558 | | // for mysql name of primary key is not used |
559 | 0 | index_name = g_strdup("_pkey"); |
560 | 0 | else { |
561 | 0 | index_name = g_strdup(idx->name); |
562 | 0 | } |
563 | 0 | break; |
564 | 0 | default: |
565 | 0 | if (idx->index_type==1) |
566 | 0 | index_name = g_strconcat(table->name, "_pkey", NULL); |
567 | 0 | else { |
568 | 0 | index_name = g_strconcat(table->name, "_", idx->name, "_idx", NULL); |
569 | 0 | } |
570 | 0 | } |
571 | | |
572 | 0 | return index_name; |
573 | 0 | } |
574 | | |
575 | | /** |
576 | | * mdb_print_indexes |
577 | | * @output: Where to print the sql |
578 | | * @table: Table to process |
579 | | * @dbnamespace: Target namespace/schema name |
580 | | */ |
581 | | static void |
582 | | mdb_print_indexes(FILE* outfile, MdbTableDef *table, char *dbnamespace) |
583 | 0 | { |
584 | 0 | unsigned int i, j; |
585 | 0 | char* quoted_table_name; |
586 | 0 | char* index_name; |
587 | 0 | char* quoted_name; |
588 | 0 | int backend; |
589 | 0 | MdbHandle* mdb = table->entry->mdb; |
590 | 0 | MdbIndex *idx; |
591 | 0 | MdbColumn *col; |
592 | |
|
593 | 0 | if (!strcmp(mdb->backend_name, "postgres")) { |
594 | 0 | backend = MDB_BACKEND_POSTGRES; |
595 | 0 | } else if (!strcmp(mdb->backend_name, "mysql")) { |
596 | 0 | backend = MDB_BACKEND_MYSQL; |
597 | 0 | } else if (!strcmp(mdb->backend_name, "oracle")) { |
598 | 0 | backend = MDB_BACKEND_ORACLE; |
599 | 0 | } else { |
600 | 0 | fprintf(outfile, "-- Indexes are not implemented for %s\n\n", mdb->backend_name); |
601 | 0 | return; |
602 | 0 | } |
603 | | |
604 | | /* read indexes */ |
605 | 0 | mdb_read_indices(table); |
606 | |
|
607 | 0 | fprintf (outfile, "-- CREATE INDEXES ...\n"); |
608 | |
|
609 | 0 | quoted_table_name = mdb->default_backend->quote_schema_name(dbnamespace, table->name); |
610 | 0 | quoted_table_name = mdb->default_backend->normalise_case(quoted_table_name); |
611 | |
|
612 | 0 | for (i=0;i<table->num_idxs;i++) { |
613 | 0 | idx = g_ptr_array_index (table->indices, i); |
614 | 0 | if (idx->index_type==2) |
615 | 0 | continue; |
616 | | |
617 | 0 | index_name = mdb_get_index_name(backend, table, idx); |
618 | 0 | switch (backend) { |
619 | 0 | case MDB_BACKEND_POSTGRES: |
620 | | /* PostgreSQL index and constraint names are |
621 | | * never namespaced in DDL (they are always |
622 | | * created in same namespace as table), so |
623 | | * omit namespace. |
624 | | */ |
625 | 0 | quoted_name = mdb->default_backend->quote_schema_name(NULL, index_name); |
626 | 0 | break; |
627 | | |
628 | 0 | default: |
629 | 0 | quoted_name = mdb->default_backend->quote_schema_name(dbnamespace, index_name); |
630 | 0 | } |
631 | | |
632 | 0 | quoted_name = mdb_normalise_and_replace(mdb, "ed_name); |
633 | |
|
634 | 0 | if (idx->index_type==1) { |
635 | 0 | switch (backend) { |
636 | 0 | case MDB_BACKEND_ORACLE: |
637 | 0 | case MDB_BACKEND_POSTGRES: |
638 | 0 | fprintf (outfile, "ALTER TABLE %s ADD CONSTRAINT %s PRIMARY KEY (", quoted_table_name, quoted_name); |
639 | 0 | break; |
640 | 0 | case MDB_BACKEND_MYSQL: |
641 | 0 | fprintf (outfile, "ALTER TABLE %s ADD PRIMARY KEY (", quoted_table_name); |
642 | 0 | break; |
643 | 0 | } |
644 | 0 | } else { |
645 | 0 | switch (backend) { |
646 | 0 | case MDB_BACKEND_ORACLE: |
647 | 0 | case MDB_BACKEND_POSTGRES: |
648 | 0 | fprintf(outfile, "CREATE"); |
649 | 0 | if (idx->flags & MDB_IDX_UNIQUE) |
650 | 0 | fprintf (outfile, " UNIQUE"); |
651 | 0 | fprintf(outfile, " INDEX %s ON %s (", quoted_name, quoted_table_name); |
652 | 0 | break; |
653 | 0 | case MDB_BACKEND_MYSQL: |
654 | 0 | fprintf(outfile, "ALTER TABLE %s ADD", quoted_table_name); |
655 | 0 | if (idx->flags & MDB_IDX_UNIQUE) |
656 | 0 | fprintf (outfile, " UNIQUE"); |
657 | 0 | fprintf(outfile, " INDEX %s (", quoted_name); |
658 | 0 | break; |
659 | 0 | } |
660 | 0 | } |
661 | 0 | g_free(quoted_name); |
662 | 0 | free(index_name); |
663 | |
|
664 | 0 | for (j=0;j<idx->num_keys;j++) { |
665 | 0 | if (j) |
666 | 0 | fprintf(outfile, ", "); |
667 | 0 | col=g_ptr_array_index(table->columns,idx->key_col_num[j]-1); |
668 | 0 | quoted_name = mdb->default_backend->quote_schema_name(NULL, col->name); |
669 | 0 | quoted_name = mdb_normalise_and_replace(mdb, "ed_name); |
670 | 0 | fprintf (outfile, "%s", quoted_name); |
671 | 0 | if (idx->index_type!=1 && idx->key_col_order[j]) |
672 | | /* no DESC for primary keys */ |
673 | 0 | fprintf(outfile, " DESC"); |
674 | |
|
675 | 0 | g_free(quoted_name); |
676 | |
|
677 | 0 | } |
678 | 0 | fprintf (outfile, ");\n"); |
679 | 0 | } |
680 | 0 | fputc ('\n', outfile); |
681 | |
|
682 | 0 | g_free(quoted_table_name); |
683 | 0 | } |
684 | | |
685 | | /** |
686 | | * mdb_get_relationships |
687 | | * @mdb: Handle to open MDB database file |
688 | | * @tablename: Name of the table to process. Process all tables if NULL. |
689 | | * |
690 | | * Generates relationships by reading the MSysRelationships table. |
691 | | * 'szColumn' contains the column name of the child table. |
692 | | * 'szObject' contains the table name of the child table. |
693 | | * 'szReferencedColumn' contains the column name of the parent table. |
694 | | * 'szReferencedObject' contains the table name of the parent table. |
695 | | * 'grbit' contains integrity constraints. |
696 | | * |
697 | | * Returns: a string stating that relationships are not supported for the |
698 | | * selected backend, or a string containing SQL commands for setting up |
699 | | * the relationship, tailored for the selected backend. |
700 | | * Returns NULL on last iteration. |
701 | | * The caller is responsible for freeing this string. |
702 | | */ |
703 | | static char * |
704 | | mdb_get_relationships(MdbHandle *mdb, const gchar *dbnamespace, const char* tablename) |
705 | 0 | { |
706 | 0 | unsigned int i; |
707 | 0 | gchar *text = NULL; /* String to be returned */ |
708 | 0 | char **bound = mdb->relationships_values; /* Bound values */ |
709 | 0 | int backend = 0; |
710 | 0 | char *quoted_table_1, *quoted_column_1, |
711 | 0 | *quoted_table_2, *quoted_column_2, |
712 | 0 | *constraint_name, *quoted_constraint_name; |
713 | 0 | long grbit; |
714 | |
|
715 | 0 | if (!strcmp(mdb->backend_name, "oracle")) { |
716 | 0 | backend = MDB_BACKEND_ORACLE; |
717 | 0 | } else if (!strcmp(mdb->backend_name, "postgres")) { |
718 | 0 | backend = MDB_BACKEND_POSTGRES; |
719 | 0 | } else if (!strcmp(mdb->backend_name, "mysql")) { |
720 | 0 | backend = MDB_BACKEND_MYSQL; |
721 | 0 | } else if (!mdb->relationships_table) { |
722 | 0 | return NULL; |
723 | 0 | } |
724 | | |
725 | 0 | if (!mdb->relationships_table) { |
726 | 0 | mdb->relationships_table = mdb_read_table_by_name(mdb, "MSysRelationships", MDB_TABLE); |
727 | 0 | if (!mdb->relationships_table || !mdb->relationships_table->num_rows) { |
728 | 0 | fprintf(stderr, "No MSysRelationships\n"); |
729 | 0 | return NULL; |
730 | 0 | } |
731 | 0 | if (!mdb_read_columns(mdb->relationships_table)) { |
732 | 0 | fprintf(stderr, "Unable to read columns of MSysRelationships\n"); |
733 | 0 | return NULL; |
734 | 0 | } |
735 | 0 | for (i=0;i<5;i++) { |
736 | 0 | bound[i] = g_malloc0(mdb->bind_size); |
737 | 0 | } |
738 | 0 | mdb_bind_column_by_name(mdb->relationships_table, "szColumn", bound[0], NULL); |
739 | 0 | mdb_bind_column_by_name(mdb->relationships_table, "szObject", bound[1], NULL); |
740 | 0 | mdb_bind_column_by_name(mdb->relationships_table, "szReferencedColumn", bound[2], NULL); |
741 | 0 | mdb_bind_column_by_name(mdb->relationships_table, "szReferencedObject", bound[3], NULL); |
742 | 0 | mdb_bind_column_by_name(mdb->relationships_table, "grbit", bound[4], NULL); |
743 | 0 | mdb_rewind_table(mdb->relationships_table); |
744 | 0 | } |
745 | 0 | if (mdb->relationships_table->cur_row >= mdb->relationships_table->num_rows) { /* past the last row */ |
746 | 0 | for (i=0;i<5;i++) |
747 | 0 | g_free(bound[i]); |
748 | 0 | mdb->relationships_table = NULL; |
749 | 0 | return NULL; |
750 | 0 | } |
751 | | |
752 | 0 | while (1) { |
753 | 0 | if (!mdb_fetch_row(mdb->relationships_table)) { |
754 | 0 | for (i=0;i<5;i++) |
755 | 0 | g_free(bound[i]); |
756 | 0 | mdb->relationships_table = NULL; |
757 | 0 | return NULL; |
758 | 0 | } |
759 | 0 | if (!tablename || !strcmp(bound[1], tablename)) |
760 | 0 | break; |
761 | 0 | } |
762 | | |
763 | 0 | quoted_table_1 = mdb->default_backend->quote_schema_name(dbnamespace, bound[1]); |
764 | 0 | quoted_table_2 = mdb->default_backend->quote_schema_name(dbnamespace, bound[3]); |
765 | 0 | grbit = atoi(bound[4]); |
766 | 0 | constraint_name = g_strconcat(bound[1], "_", bound[0], "_fk", NULL); |
767 | |
|
768 | 0 | switch (backend) { |
769 | 0 | case MDB_BACKEND_POSTGRES: |
770 | | /* PostgreSQL index and constraint names are |
771 | | * never namespaced in DDL (they are always |
772 | | * created in same namespace as table), so |
773 | | * omit namespace. Nor should column names |
774 | | * be namespaced. |
775 | | */ |
776 | 0 | quoted_constraint_name = mdb->default_backend->quote_schema_name(NULL, constraint_name); |
777 | 0 | quoted_constraint_name = mdb_normalise_and_replace(mdb, "ed_constraint_name); |
778 | 0 | quoted_column_1 = mdb->default_backend->quote_schema_name(NULL, bound[0]); |
779 | 0 | quoted_column_1 = mdb_normalise_and_replace(mdb, "ed_column_1); |
780 | 0 | quoted_column_2 = mdb->default_backend->quote_schema_name(NULL, bound[2]); |
781 | 0 | quoted_column_2 = mdb_normalise_and_replace(mdb, "ed_column_2); |
782 | 0 | break; |
783 | | |
784 | 0 | default: |
785 | | /* Other databases, namespace constraint and |
786 | | * column names. |
787 | | */ |
788 | 0 | quoted_constraint_name = mdb->default_backend->quote_schema_name(dbnamespace, constraint_name); |
789 | 0 | quoted_column_1 = mdb->default_backend->quote_schema_name(dbnamespace, bound[0]); |
790 | 0 | quoted_column_2 = mdb->default_backend->quote_schema_name(dbnamespace, bound[2]); |
791 | 0 | break; |
792 | 0 | } |
793 | 0 | g_free(constraint_name); |
794 | |
|
795 | 0 | if (grbit & 0x00000002) { |
796 | 0 | text = g_strconcat( |
797 | 0 | "-- Relationship from ", quoted_table_1, |
798 | 0 | " (", quoted_column_1, ")" |
799 | 0 | " to ", quoted_table_2, "(", quoted_column_2, ")", |
800 | 0 | " does not enforce integrity.\n", NULL); |
801 | 0 | } else { |
802 | 0 | switch (backend) { |
803 | 0 | case MDB_BACKEND_ORACLE: |
804 | 0 | text = g_strconcat( |
805 | 0 | "ALTER TABLE ", quoted_table_1, |
806 | 0 | " ADD CONSTRAINT ", quoted_constraint_name, |
807 | 0 | " FOREIGN KEY (", quoted_column_1, ")" |
808 | 0 | " REFERENCES ", quoted_table_2, "(", quoted_column_2, ")", |
809 | 0 | (grbit & 0x00001000) ? " ON DELETE CASCADE" : "", |
810 | 0 | ";\n", NULL); |
811 | |
|
812 | 0 | break; |
813 | 0 | case MDB_BACKEND_MYSQL: |
814 | 0 | text = g_strconcat( |
815 | 0 | "ALTER TABLE ", quoted_table_1, |
816 | 0 | " ADD CONSTRAINT ", quoted_constraint_name, |
817 | 0 | " FOREIGN KEY (", quoted_column_1, ")" |
818 | 0 | " REFERENCES ", quoted_table_2, "(", quoted_column_2, ")", |
819 | 0 | (grbit & 0x00000100) ? " ON UPDATE CASCADE" : "", |
820 | 0 | (grbit & 0x00001000) ? " ON DELETE CASCADE" : "", |
821 | 0 | ";\n", NULL); |
822 | 0 | break; |
823 | 0 | case MDB_BACKEND_POSTGRES: |
824 | 0 | text = g_strconcat( |
825 | 0 | "ALTER TABLE ", quoted_table_1, |
826 | 0 | " ADD CONSTRAINT ", quoted_constraint_name, |
827 | 0 | " FOREIGN KEY (", quoted_column_1, ")" |
828 | 0 | " REFERENCES ", quoted_table_2, "(", quoted_column_2, ")", |
829 | 0 | (grbit & 0x00000100) ? " ON UPDATE CASCADE" : "", |
830 | 0 | (grbit & 0x00001000) ? " ON DELETE CASCADE" : "", |
831 | | /* On some databases (eg PostgreSQL) we also want to set |
832 | | * the constraints to be optionally deferrable, to |
833 | | * facilitate out of order bulk loading. |
834 | | */ |
835 | 0 | " DEFERRABLE", |
836 | 0 | " INITIALLY IMMEDIATE", |
837 | 0 | ";\n", NULL); |
838 | |
|
839 | 0 | break; |
840 | 0 | } |
841 | 0 | } |
842 | 0 | g_free(quoted_table_1); |
843 | 0 | g_free(quoted_column_1); |
844 | 0 | g_free(quoted_table_2); |
845 | 0 | g_free(quoted_column_2); |
846 | 0 | g_free(quoted_constraint_name); |
847 | |
|
848 | 0 | return (char *)text; |
849 | 0 | } |
850 | | |
851 | | static void |
852 | | generate_table_schema(FILE *outfile, MdbCatalogEntry *entry, char *dbnamespace, guint32 export_options) |
853 | 0 | { |
854 | 0 | MdbTableDef *table; |
855 | 0 | MdbHandle *mdb = entry->mdb; |
856 | 0 | MdbColumn *col; |
857 | 0 | unsigned int i; |
858 | 0 | char* quoted_table_name; |
859 | 0 | char* quoted_name; |
860 | 0 | MdbProperties *props; |
861 | 0 | const char *prop_value; |
862 | |
|
863 | 0 | quoted_table_name = mdb->default_backend->quote_schema_name(dbnamespace, entry->object_name); |
864 | 0 | quoted_table_name = mdb_normalise_and_replace(mdb, "ed_table_name); |
865 | | |
866 | | /* drop the table if it exists */ |
867 | 0 | if (export_options & MDB_SHEXP_DROPTABLE) |
868 | 0 | fprintf (outfile, mdb->default_backend->drop_statement, quoted_table_name); |
869 | | |
870 | | /* create the table */ |
871 | 0 | fprintf (outfile, mdb->default_backend->create_table_statement, quoted_table_name); |
872 | 0 | fprintf (outfile, " (\n"); |
873 | |
|
874 | 0 | table = mdb_read_table (entry); |
875 | | |
876 | | /* get the columns */ |
877 | 0 | mdb_read_columns(table); |
878 | | |
879 | | /* loop over the columns, dumping the names and types */ |
880 | 0 | for (i = 0; i < table->num_cols; i++) { |
881 | 0 | col = g_ptr_array_index (table->columns, i); |
882 | |
|
883 | 0 | quoted_name = mdb->default_backend->quote_schema_name(NULL, col->name); |
884 | 0 | quoted_name = mdb_normalise_and_replace(mdb, "ed_name); |
885 | 0 | fprintf (outfile, "\t%s\t\t\t%s", quoted_name, |
886 | 0 | mdb_get_colbacktype_string (col)); |
887 | 0 | g_free(quoted_name); |
888 | |
|
889 | 0 | if (mdb_colbacktype_takes_length(col)) { |
890 | | /* more portable version from DW patch */ |
891 | 0 | if (col->col_size == 0) |
892 | 0 | fputs(" (255)", outfile); |
893 | 0 | else if (col->col_scale != 0) |
894 | 0 | fprintf(outfile, " (%d, %d)", col->col_scale, col->col_prec); |
895 | 0 | else if (!IS_JET3(mdb) && mdb_colbacktype_takes_length_in_characters(col)) |
896 | 0 | fprintf(outfile, " (%d)", col->col_size/2); |
897 | 0 | else |
898 | 0 | fprintf(outfile, " (%d)", col->col_size); |
899 | 0 | } |
900 | |
|
901 | 0 | if (mdb->default_backend->per_column_comment_statement && export_options & MDB_SHEXP_COMMENTS) { |
902 | 0 | prop_value = mdb_col_get_prop(col, "Description"); |
903 | 0 | if (prop_value) { |
904 | 0 | char *comment = quote_with_squotes(prop_value); |
905 | 0 | fputs(" ", outfile); |
906 | 0 | fprintf(outfile, |
907 | 0 | mdb->default_backend->per_column_comment_statement, |
908 | 0 | comment); |
909 | 0 | free(comment); |
910 | 0 | } |
911 | 0 | } |
912 | |
|
913 | 0 | if (export_options & MDB_SHEXP_CST_NOTNULL) { |
914 | 0 | if (col->col_type == MDB_BOOL) { |
915 | | /* access booleans are never null */ |
916 | 0 | fputs(" NOT NULL", outfile); |
917 | 0 | } else { |
918 | 0 | const gchar *not_null = mdb_col_get_prop(col, "Required"); |
919 | 0 | if (not_null && not_null[0]=='y') |
920 | 0 | fputs(" NOT NULL", outfile); |
921 | 0 | } |
922 | 0 | } |
923 | |
|
924 | 0 | if (export_options & MDB_SHEXP_DEFVALUES) { |
925 | 0 | int done = 0; |
926 | 0 | if (col->props) { |
927 | 0 | gchar *defval = g_hash_table_lookup(col->props->hash, "DefaultValue"); |
928 | 0 | if (defval) { |
929 | 0 | size_t def_len = strlen(defval); |
930 | 0 | fputs(" DEFAULT ", outfile); |
931 | | /* ugly hack to detect the type */ |
932 | 0 | if (defval[0]=='"' && defval[def_len-1]=='"') { |
933 | | /* this is a string */ |
934 | 0 | gchar *output_default = malloc(def_len-1); |
935 | 0 | gchar *output_default_escaped; |
936 | 0 | memcpy(output_default, defval+1, def_len-2); |
937 | 0 | output_default[def_len-2] = 0; |
938 | 0 | output_default_escaped = quote_with_squotes(output_default); |
939 | 0 | fputs(output_default_escaped, outfile); |
940 | 0 | g_free(output_default_escaped); |
941 | 0 | free(output_default); |
942 | 0 | } else if (!strcmp(defval, "Yes")) |
943 | 0 | fputs("TRUE", outfile); |
944 | 0 | else if (!strcmp(defval, "No")) |
945 | 0 | fputs("FALSE", outfile); |
946 | 0 | else if (!g_ascii_strcasecmp(defval, "date()")) { |
947 | 0 | if (mdb_col_is_shortdate(col)) |
948 | 0 | fputs(mdb->default_backend->short_now, outfile); |
949 | 0 | else |
950 | 0 | fputs(mdb->default_backend->long_now, outfile); |
951 | 0 | } |
952 | 0 | else |
953 | 0 | fputs(defval, outfile); |
954 | 0 | done = 1; |
955 | 0 | } |
956 | 0 | } |
957 | 0 | if (!done && col->col_type == MDB_BOOL) |
958 | | /* access booleans are false by default */ |
959 | 0 | fputs(" DEFAULT FALSE", outfile); |
960 | 0 | } |
961 | 0 | if (i < table->num_cols - 1) |
962 | 0 | fputs(", \n", outfile); |
963 | 0 | else |
964 | 0 | fputs("\n", outfile); |
965 | 0 | } /* for */ |
966 | |
|
967 | 0 | fputs(")", outfile); |
968 | 0 | if (mdb->default_backend->per_table_comment_statement && export_options & MDB_SHEXP_COMMENTS) { |
969 | 0 | prop_value = mdb_table_get_prop(table, "Description"); |
970 | 0 | if (prop_value) { |
971 | 0 | char *comment = quote_with_squotes(prop_value); |
972 | 0 | fputs(" ", outfile); |
973 | 0 | fprintf(outfile, mdb->default_backend->per_table_comment_statement, comment); |
974 | 0 | free(comment); |
975 | 0 | } |
976 | 0 | } |
977 | 0 | fputs(";\n", outfile); |
978 | | |
979 | | /* Add the constraints on columns */ |
980 | 0 | for (i = 0; i < table->num_cols; i++) { |
981 | 0 | col = g_ptr_array_index (table->columns, i); |
982 | 0 | props = col->props; |
983 | 0 | if (!props) |
984 | 0 | continue; |
985 | | |
986 | 0 | quoted_name = mdb->default_backend->quote_schema_name(NULL, col->name); |
987 | 0 | quoted_name = mdb_normalise_and_replace(mdb, "ed_name); |
988 | |
|
989 | 0 | if (export_options & MDB_SHEXP_CST_NOTEMPTY) { |
990 | 0 | prop_value = mdb_col_get_prop(col, "AllowZeroLength"); |
991 | 0 | if (prop_value && prop_value[0]=='n') |
992 | 0 | fprintf(outfile, |
993 | 0 | mdb->default_backend->constaint_not_empty_statement, |
994 | 0 | quoted_table_name, quoted_name); |
995 | 0 | } |
996 | |
|
997 | 0 | if (mdb->default_backend->column_comment_statement && export_options & MDB_SHEXP_COMMENTS) { |
998 | 0 | prop_value = mdb_col_get_prop(col, "Description"); |
999 | 0 | if (prop_value) { |
1000 | 0 | char *comment = quote_with_squotes(prop_value); |
1001 | 0 | fprintf(outfile, |
1002 | 0 | mdb->default_backend->column_comment_statement, |
1003 | 0 | quoted_table_name, quoted_name, comment); |
1004 | 0 | g_free(comment); |
1005 | 0 | } |
1006 | 0 | } |
1007 | |
|
1008 | 0 | g_free(quoted_name); |
1009 | 0 | } |
1010 | | |
1011 | | /* Add the constraints on table */ |
1012 | 0 | if (mdb->default_backend->table_comment_statement && export_options & MDB_SHEXP_COMMENTS) { |
1013 | 0 | prop_value = mdb_table_get_prop(table, "Description"); |
1014 | 0 | if (prop_value) { |
1015 | 0 | char *comment = quote_with_squotes(prop_value); |
1016 | 0 | fprintf(outfile, |
1017 | 0 | mdb->default_backend->table_comment_statement, |
1018 | 0 | quoted_table_name, comment); |
1019 | 0 | g_free(comment); |
1020 | 0 | } |
1021 | 0 | } |
1022 | 0 | fputc('\n', outfile); |
1023 | | |
1024 | |
|
1025 | 0 | if (export_options & MDB_SHEXP_INDEXES) |
1026 | | // prints all the indexes of that table |
1027 | 0 | mdb_print_indexes(outfile, table, dbnamespace); |
1028 | |
|
1029 | 0 | g_free(quoted_table_name); |
1030 | |
|
1031 | 0 | mdb_free_tabledef (table); |
1032 | 0 | } |
1033 | | |
1034 | | |
1035 | | int |
1036 | | mdb_print_schema(MdbHandle *mdb, FILE *outfile, char *tabname, char *dbnamespace, guint32 export_options) |
1037 | 0 | { |
1038 | 0 | unsigned int i; |
1039 | 0 | char *the_relation; |
1040 | 0 | MdbCatalogEntry *entry; |
1041 | 0 | const char *charset; |
1042 | 0 | int success = (tabname == NULL); |
1043 | | |
1044 | | /* clear unsupported options */ |
1045 | 0 | export_options &= mdb->default_backend->capabilities; |
1046 | | |
1047 | | /* Print out a little message to show that this came from mdb-tools. |
1048 | | I like to know how something is generated. DW */ |
1049 | 0 | fputs("-- ----------------------------------------------------------\n" |
1050 | 0 | "-- MDB Tools - A library for reading MS Access database files\n" |
1051 | 0 | "-- Copyright (C) 2000-2011 Brian Bruns and others.\n" |
1052 | 0 | "-- Files in libmdb are licensed under LGPL and the utilities under\n" |
1053 | 0 | "-- the GPL, see COPYING.LIB and COPYING files respectively.\n" |
1054 | 0 | "-- Check out http://mdbtools.sourceforge.net\n" |
1055 | 0 | "-- ----------------------------------------------------------\n\n", |
1056 | 0 | outfile); |
1057 | |
|
1058 | 0 | charset = mdb_target_charset(mdb); |
1059 | 0 | if (charset) { |
1060 | 0 | fprintf(outfile, mdb->default_backend->charset_statement, charset); |
1061 | 0 | fputc('\n', outfile); |
1062 | 0 | } |
1063 | |
|
1064 | 0 | for (i=0; i < mdb->num_catalog; i++) { |
1065 | 0 | entry = g_ptr_array_index (mdb->catalog, i); |
1066 | 0 | if (entry->object_type == MDB_TABLE) { |
1067 | 0 | if ((tabname && !strcmp(entry->object_name, tabname)) |
1068 | 0 | || (!tabname && mdb_is_user_table(entry))) { |
1069 | 0 | generate_table_schema(outfile, entry, dbnamespace, export_options); |
1070 | 0 | success = 1; |
1071 | 0 | } |
1072 | 0 | } |
1073 | 0 | } |
1074 | 0 | fprintf (outfile, "\n"); |
1075 | |
|
1076 | 0 | if (export_options & MDB_SHEXP_RELATIONS) { |
1077 | 0 | fputs ("-- CREATE Relationships ...\n", outfile); |
1078 | 0 | the_relation=mdb_get_relationships(mdb, dbnamespace, tabname); |
1079 | 0 | if (!the_relation) { |
1080 | 0 | fputs("-- relationships are not implemented for ", outfile); |
1081 | 0 | fputs(mdb->backend_name, outfile); |
1082 | 0 | fputs("\n", outfile); |
1083 | 0 | } else { |
1084 | 0 | do { |
1085 | 0 | fputs(the_relation, outfile); |
1086 | 0 | g_free(the_relation); |
1087 | 0 | } while ((the_relation=mdb_get_relationships(mdb, dbnamespace, tabname)) != NULL); |
1088 | 0 | } |
1089 | 0 | } |
1090 | 0 | return success; |
1091 | 0 | } |
1092 | | |
1093 | 0 | #define MDB_BINEXPORT_MASK 0x0F |
1094 | 0 | #define is_binary_type(x) (x==MDB_OLE || x==MDB_BINARY || x==MDB_REPID) |
1095 | 0 | #define is_quote_type(x) (is_binary_type(x) || x==MDB_TEXT || x==MDB_MEMO || x==MDB_DATETIME) |
1096 | | //#define DONT_ESCAPE_ESCAPE |
1097 | | void |
1098 | | mdb_print_col(FILE *outfile, gchar *col_val, int quote_text, int col_type, int bin_len, |
1099 | | char *quote_char, char *escape_char, int flags) |
1100 | | /* quote_text: Don't quote if 0. |
1101 | | */ |
1102 | 0 | { |
1103 | 0 | size_t quote_len = strlen(quote_char); /* multibyte */ |
1104 | |
|
1105 | 0 | size_t orig_escape_len = escape_char ? strlen(escape_char) : 0; |
1106 | 0 | int quoting = quote_text && is_quote_type(col_type); |
1107 | 0 | int bin_mode = (flags & MDB_BINEXPORT_MASK); |
1108 | 0 | int escape_cr_lf = !!(flags & MDB_EXPORT_ESCAPE_CONTROL_CHARS); |
1109 | | |
1110 | | /* double the quote char if no escape char passed */ |
1111 | 0 | if (!escape_char) |
1112 | 0 | escape_char = quote_char; |
1113 | |
|
1114 | 0 | if (quoting) |
1115 | 0 | fputs(quote_char, outfile); |
1116 | |
|
1117 | 0 | while (1) { |
1118 | 0 | if (is_binary_type(col_type)) { |
1119 | 0 | if (bin_mode == MDB_BINEXPORT_STRIP) |
1120 | 0 | break; |
1121 | 0 | if (!bin_len--) |
1122 | 0 | break; |
1123 | 0 | } else /* use \0 sentry */ |
1124 | 0 | if (!*col_val) |
1125 | 0 | break; |
1126 | | |
1127 | 0 | if (is_binary_type(col_type) && bin_mode == MDB_BINEXPORT_OCTAL) { |
1128 | 0 | fprintf(outfile, "\\%03o", *(unsigned char*)col_val++); |
1129 | 0 | } else if (is_binary_type(col_type) && bin_mode == MDB_BINEXPORT_HEXADECIMAL) { |
1130 | 0 | fprintf(outfile, "%02X", *(unsigned char*)col_val++); |
1131 | 0 | } else if (quoting && quote_len && !strncmp(col_val, quote_char, quote_len)) { |
1132 | 0 | fprintf(outfile, "%s%s", escape_char, quote_char); |
1133 | 0 | col_val += quote_len; |
1134 | 0 | #ifndef DONT_ESCAPE_ESCAPE |
1135 | 0 | } else if (quoting && orig_escape_len && !strncmp(col_val, escape_char, orig_escape_len)) { |
1136 | 0 | fprintf(outfile, "%s%s", escape_char, escape_char); |
1137 | 0 | col_val += orig_escape_len; |
1138 | 0 | #endif |
1139 | 0 | } else if (escape_cr_lf && is_quote_type(col_type) && *col_val=='\r') { |
1140 | 0 | col_val++; |
1141 | 0 | putc('\\', outfile); |
1142 | 0 | putc('r', outfile); |
1143 | 0 | } else if (escape_cr_lf && is_quote_type(col_type) && *col_val=='\n') { |
1144 | 0 | col_val++; |
1145 | 0 | putc('\\', outfile); |
1146 | 0 | putc('n', outfile); |
1147 | 0 | } else if (escape_cr_lf && is_quote_type(col_type) && *col_val=='\t') { |
1148 | 0 | col_val++; |
1149 | 0 | putc('\\', outfile); |
1150 | 0 | putc('t', outfile); |
1151 | 0 | } else if (escape_cr_lf && is_quote_type(col_type) && *col_val=='\\') { |
1152 | 0 | col_val++; |
1153 | 0 | putc('\\', outfile); |
1154 | 0 | putc('\\', outfile); |
1155 | 0 | } else |
1156 | 0 | putc(*col_val++, outfile); |
1157 | 0 | } |
1158 | 0 | if (quoting) |
1159 | 0 | fputs(quote_char, outfile); |
1160 | 0 | } |