Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 #!/usr/bin/python | |
1 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
2 """Script to extract the database schema from SQLite database files. | 3 """Script to extract the database schema from SQLite database files. |
3 | 4 |
4 The resulting schema will be placed into your clipboard which can then | 5 The resulting schema will be placed into your clipboard which can then |
5 be pasted directly into your plugin. | 6 be pasted directly into your plugin. |
6 | 7 |
7 This script requires the jinja2 and pyperclip Python modules. | 8 This script requires the pyperclip Python module. |
8 """ | 9 """ |
9 | 10 |
10 from __future__ import print_function | 11 from __future__ import print_function |
11 import argparse | 12 import argparse |
12 import os | 13 import os |
13 import sys | 14 import sys |
15 import textwrap | |
14 | 16 |
15 import jinja2 # pylint: disable=import-error | |
16 import pyperclip # pylint: disable=import-error | 17 import pyperclip # pylint: disable=import-error |
17 | 18 |
18 from plaso.parsers import sqlite | 19 # Change PYTHONPATH to include plaso. |
20 sys.path.insert(0, u'.') | |
21 | |
22 from plaso.parsers import sqlite # pylint: disable=wrong-import-position | |
19 | 23 |
20 | 24 |
21 class SQLiteSchemaExtractor(object): | 25 class SQLiteSchemaExtractor(object): |
22 """SQLite database file schema extractor.""" | 26 """SQLite database file schema extractor.""" |
dc3.plaso
2017/04/10 17:32:55
Why are these in a class? It seems unnecessary for
Joachim Metz
2017/04/10 18:57:23
Is more of a grouping for potential reuse and for
| |
23 | |
24 _JINJA2_TEMPLATE = (''' | |
25 {% for table, query in schema|dictsort %} | |
26 {% if loop.first %} | |
27 {u'{{ table }}': | |
28 {% else %} | |
29 u'{{ table }}': | |
30 {% endif %} | |
31 u'{{ query|wordwrap(67, wrapstring=" '\n u'") }}' | |
32 {%- if not loop.last -%} | |
33 , | |
34 {% else -%} | |
35 } | |
36 {%- endif %} | |
37 {%- endfor %}''') | |
38 | 27 |
39 def GetDatabaseSchema(self, database_path, wal_path=None): | 28 def GetDatabaseSchema(self, database_path, wal_path=None): |
40 """Retrieves schema from given database. | 29 """Retrieves schema from given database. |
41 | 30 |
42 Args: | 31 Args: |
43 database_path (str): file path to database. | 32 database_path (str): file path to database. |
44 wal_path (Optional[str]): file path to wal file. | 33 wal_path (Optional[str]): file path to WAL file. |
45 | 34 |
46 Returns: | 35 Returns: |
47 dict[str, str]: schema as an SQL query per table name. | 36 dict[str, str]: schema as an SQL query per table name. |
48 """ | 37 """ |
49 database = sqlite.SQLiteDatabase('database.db') | 38 database = sqlite.SQLiteDatabase('database.db') |
50 | 39 |
51 with open(database_path, u'rb') as file_object: | 40 with open(database_path, u'rb') as file_object: |
52 wal_file_object = None | 41 wal_file_object = None |
53 if wal_path: | 42 if wal_path: |
54 wal_file_object = open(wal_path, u'rb') | 43 wal_file_object = open(wal_path, u'rb') |
(...skipping 12 matching lines...) Expand all Loading... | |
67 | 56 |
68 def FormatSchema(self, schema): | 57 def FormatSchema(self, schema): |
69 """Formats a schema into a word-wrapped string. | 58 """Formats a schema into a word-wrapped string. |
70 | 59 |
71 Args: | 60 Args: |
72 schema (dict[str, str]): schema as an SQL query per table name. | 61 schema (dict[str, str]): schema as an SQL query per table name. |
73 | 62 |
74 Returns: | 63 Returns: |
75 str: schema formated as word-wrapped string. | 64 str: schema formated as word-wrapped string. |
76 """ | 65 """ |
77 schema = { | 66 textwrapper = textwrap.TextWrapper() |
78 table: u' '.join(query.split()).replace(u'\'', u'\\\'') | 67 textwrapper.break_long_words = False |
79 for table, query in schema.items()} | 68 textwrapper.drop_whitespace = True |
69 textwrapper.width = 80 - (10 + 4) | |
80 | 70 |
81 environment = jinja2.Environment(trim_blocks=True, lstrip_blocks=True) | 71 lines = [] |
82 template = environment.from_string(self._JINJA2_TEMPLATE) | 72 table_index = 1 |
83 return template.render(schema=schema) | 73 number_of_tables = len(schema) |
74 for table_name, query in schema.items(): | |
dc3.plaso
2017/05/09 18:52:44
The original Jinja2 template was using dictsort to
Joachim Metz
2017/05/28 12:02:46
Acknowledged.
| |
75 line = u' u\'{0:s}\': ('.format(table_name) | |
76 lines.append(line) | |
77 | |
78 query = query.replace(u'\'', u'\\\'') | |
79 query = textwrapper.wrap(query) | |
80 query = [u'{0:s}u\'{1:s} \''.format(u' ' * 10, line) for line in query] | |
81 | |
82 if table_index == number_of_tables: | |
83 query[-1] = u'{0:s}\')}}]'.format(query[-1][:-2]) | |
84 else: | |
85 query[-1] = u'{0:s}\'),'.format(query[-1][:-2]) | |
86 | |
87 lines.extend(query) | |
88 table_index += 1 | |
89 | |
90 return u'\n'.join(lines) | |
84 | 91 |
85 | 92 |
86 if __name__ == u'__main__': | 93 if __name__ == u'__main__': |
87 argument_parser = argparse.ArgumentParser() | 94 argument_parser = argparse.ArgumentParser() |
88 | 95 |
89 argument_parser.add_argument( | 96 argument_parser.add_argument( |
90 u'database_path', type=str, | 97 u'database_path', type=str, |
91 help=u'The path to the database file to extract schema from.') | 98 help=u'The path to the database file to extract schema from.') |
92 | 99 |
93 argument_parser.add_argument( | 100 argument_parser.add_argument( |
94 u'wal_path', type=str, nargs=u'?', default=None, | 101 u'wal_path', type=str, nargs=u'?', default=None, |
95 help=u'Optional path to a wal file to commit into the database.') | 102 help=u'Optional path to a WAL file to commit into the database.') |
96 | 103 |
97 options = argument_parser.parse_args() | 104 options = argument_parser.parse_args() |
98 | 105 |
99 if not os.path.exists(options.database_path): | 106 if not os.path.exists(options.database_path): |
100 print(u'No such database file: {0:s}'.format(options.database_path)) | 107 print(u'No such database file: {0:s}'.format(options.database_path)) |
101 sys.exit(1) | 108 sys.exit(1) |
102 | 109 |
103 if not os.path.exists(options.wal_path): | 110 if options.wal_path and not os.path.exists(options.wal_path): |
104 print(u'No such wal file: {0:s}'.format(options.wal_path)) | 111 print(u'No such WAL file: {0:s}'.format(options.wal_path)) |
105 sys.exit(1) | 112 sys.exit(1) |
106 | 113 |
107 extractor = SQLiteSchemaExtractor() | 114 extractor = SQLiteSchemaExtractor() |
108 | 115 |
109 database_schema = extractor.GetDatabaseSchema( | 116 database_schema = extractor.GetDatabaseSchema( |
110 options.database_path, options.wal_path) | 117 options.database_path, options.wal_path) |
111 | 118 |
112 database_schema = extractor.FormatSchema(database_schema) | 119 database_schema = extractor.FormatSchema(database_schema) |
113 | 120 |
114 pyperclip.copy(database_schema) | 121 pyperclip.copy(database_schema) |
115 | 122 |
116 sys.exit(0) | 123 sys.exit(0) |
LEFT | RIGHT |