From c90e36020a6993166bae1013402c30d827014317 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Thu, 9 Oct 2014 17:55:36 -0700 Subject: [PATCH] Add the database schema to the sqlalchemy docs In order to show people that are reading the docs what the current schema for the database is create a helper tool that upgrades the schema to the newest version and then uses tabulate to create a restructured text version of that schema which is then included in the documentation. Change-Id: Id215f88430971a4a083f9739fb2ec59d971dc8fa --- doc/source/persistence.rst | 48 ++++++++++++++++++++++ tools/schema_generator.py | 83 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100755 tools/schema_generator.py diff --git a/doc/source/persistence.rst b/doc/source/persistence.rst index 18fe6bff5..0da68de15 100644 --- a/doc/source/persistence.rst +++ b/doc/source/persistence.rst @@ -177,6 +177,54 @@ Useful when you need a higher level of durability than offered by the previous solutions. When using these connection types it is possible to resume a engine from a peer machine (this does not apply when using sqlite). +Schema +^^^^^^ + +*Logbooks* + +========== ======== ============= +Name Type Primary Key +========== ======== ============= +created_at DATETIME False +updated_at DATETIME False +uuid VARCHAR True +name VARCHAR False +meta TEXT False +========== ======== ============= + +*Flow details* + +=========== ======== ============= +Name Type Primary Key +=========== ======== ============= +created_at DATETIME False +updated_at DATETIME False +uuid VARCHAR True +name VARCHAR False +meta TEXT False +state VARCHAR False +parent_uuid VARCHAR False +=========== ======== ============= + +*Atom details* + +=========== ======== ============= +Name Type Primary Key +=========== ======== ============= +created_at DATETIME False +updated_at DATETIME False +uuid VARCHAR True +name VARCHAR False +meta TEXT False +atom_type VARCHAR False +state VARCHAR False +intention VARCHAR False +results TEXT False +failure TEXT False +version TEXT False +parent_uuid VARCHAR False +=========== ======== ============= + .. _sqlalchemy: http://www.sqlalchemy.org/docs/ .. _ACID: https://en.wikipedia.org/wiki/ACID diff --git a/tools/schema_generator.py b/tools/schema_generator.py new file mode 100755 index 000000000..3685a0a13 --- /dev/null +++ b/tools/schema_generator.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import contextlib +import re + +import six +import tabulate + +from taskflow.persistence.backends import impl_sqlalchemy + +NAME_MAPPING = { + 'flowdetails': 'Flow details', + 'atomdetails': 'Atom details', + 'logbooks': 'Logbooks', +} +CONN_CONF = { + # This uses an in-memory database (aka nothing is written) + "connection": "sqlite://", +} +TABLE_QUERY = "SELECT name, sql FROM sqlite_master WHERE type='table'" +SCHEMA_QUERY = "pragma table_info(%s)" + + +def to_bool_string(val): + if isinstance(val, (int, bool)): + return six.text_type(bool(val)) + if not isinstance(val, six.string_types): + val = six.text_type(val) + if val.lower() in ('0', 'false'): + return 'False' + if val.lower() in ('1', 'true'): + return 'True' + raise ValueError("Unknown boolean input '%s'" % (val)) + + +def main(): + backend = impl_sqlalchemy.SQLAlchemyBackend(CONN_CONF) + with contextlib.closing(backend) as backend: + # Make the schema exist... + with contextlib.closing(backend.get_connection()) as conn: + conn.upgrade() + # Now make a prettier version of that schema... + tables = backend.engine.execute(TABLE_QUERY) + table_names = [r[0] for r in tables] + for i, table_name in enumerate(table_names): + pretty_name = NAME_MAPPING.get(table_name, table_name) + print("*" + pretty_name + "*") + # http://www.sqlite.org/faq.html#q24 + table_name = table_name.replace("\"", "\"\"") + rows = [] + for r in backend.engine.execute(SCHEMA_QUERY % table_name): + # Cut out the numbers from things like VARCHAR(12) since + # this is not very useful to show users who just want to + # see the basic schema... + row_type = re.sub(r"\(.*?\)", "", r['type']).strip() + if not row_type: + raise ValueError("Row %s of table '%s' was empty after" + " cleaning" % (r['cid'], table_name)) + rows.append([r['name'], row_type, to_bool_string(r['pk'])]) + contents = tabulate.tabulate( + rows, headers=['Name', 'Type', 'Primary Key'], + tablefmt="rst") + print("\n%s" % contents.strip()) + if i + 1 != len(table_names): + print("") + + +if __name__ == '__main__': + main()