Commits

Kirill Simonov committed 1500bfb

Fixed `tweak.system.pgsql` addon; added tests.

Comments (0)

Files changed (11)

src/htsql/tr/dump.py

                 need_schema = False
                 for schema in table.schema.catalog.schemas:
                     if (schema != table.schema and
-                            schema.priority >= table.schema_priority and
+                            schema.priority >= table.schema.priority and
                             table.name in schema.tables):
                         need_schema = True
                         break

src/htsql_tweak/system/__init__.py

 #
 
 
-from htsql.addon import Addon
+from htsql.addon import Addon, addon_registry
 
 
 class TweakSystemAddon(Addon):
 
     name = 'tweak.system'
-    hint = """direct access to system catalog"""
+    hint = """add access to system tables"""
     help = """
-      This plugin adds the system catalog tables and links 
-      for the database's native system catalog.  This is 
-      supported only for PostgreSQL.
+    This addon adds access to system catalog tables.
+
+    Currently, only PostgreSQL backend is supported.
     """
 
     @classmethod
     def get_extension(cls, app, attributes):
-        return 'tweak.system.%s' % app.htsql.db.engine
+        if app.htsql.db is not None:
+            name = '%s.%s' % (cls.name, app.htsql.db.engine)
+            if name not in addon_registry:
+                raise ImportError("%s is not implemented for %s"
+                                  % (cls.name, app.htsql.db.engine))
+            return name
 
 

src/htsql_tweak/system/pgsql/__init__.py

 
     name = 'tweak.system.pgsql'
     prerequisites = ['engine.pgsql']
+    hint = """implement `tweak.system` for PostgreSQL"""
 
 

src/htsql_tweak/system/pgsql/introspect.py

 #
 
 
-from htsql_engine.pgsql.introspect import IntrospectPGSQL
-from htsql.entity import PrimaryKeyEntity, ForeignKeyEntity
+from htsql.adapter import named
+from htsql_engine.pgsql.introspect import IntrospectPGSQL, IntrospectPGSQLDomain
+from htsql_engine.pgsql.domain import PGTextDomain, PGIntegerDomain
 
 
 class PGCatalogIntrospectPGSQL(IntrospectPGSQL):
 
+    system_schema_names = []
+
     primary_keys = {
             ('pg_catalog', 'pg_aggregate'): ['aggfnoid'],
             ('pg_catalog', 'pg_am'): ['oid'],
 
     }
 
-    def permit_schema(self, schema_name):
-        if schema_name == 'pg_catalog':
-            return True
-        return super(PGCatalogIntrospectPGSQL, self).permit_schema(schema_name)
+    def __call__(self):
+        catalog = super(PGCatalogIntrospectPGSQL, self).__call__()
+        for schema_name, table_name in sorted(self.primary_keys):
+            column_names = self.primary_keys[schema_name, table_name]
+            schema_name = unicode(schema_name)
+            table_name = unicode(table_name)
+            column_names = map(unicode, column_names)
+            if schema_name not in catalog.schemas:
+                continue
+            schema = catalog.schemas[schema_name]
+            if table_name not in schema.tables:
+                continue
+            table = schema.tables[table_name]
+            if any(column_name not in table.columns
+                   for column_name in column_names):
+                continue
+            columns = [table.columns[column_name]
+                       for column_name in column_names]
+            if table.primary_key is not None:
+                table.primary_key.set_is_primary(False)
+            for column in columns:
+                column.set_is_nullable(False)
+            table.add_primary_key(columns)
+        for origin_schema_name, origin_table_name in sorted(self.foreign_keys):
+            keys = self.foreign_keys[origin_schema_name, origin_table_name]
+            origin_schema_name = unicode(origin_schema_name)
+            origin_table_name = unicode(origin_table_name)
+            if origin_schema_name not in catalog.schemas:
+                continue
+            origin_schema = catalog.schemas[schema_name]
+            if origin_table_name not in origin_schema.tables:
+                continue
+            origin = origin_schema.tables[origin_table_name]
+            for (origin_column_names, (target_schema_name, target_table_name),
+                    target_column_names) in keys:
+                origin_column_names = map(unicode, origin_column_names)
+                target_schema_name = unicode(target_schema_name)
+                target_table_name = unicode(target_table_name)
+                target_column_names = map(unicode, target_column_names)
+                if any(column_name not in origin.columns
+                       for column_name in origin_column_names):
+                    continue
+                origin_columns = [origin.columns[column_name]
+                                  for column_name in origin_column_names]
+                if target_schema_name not in catalog.schemas:
+                    continue
+                target_schema = catalog.schemas[schema_name]
+                if target_table_name not in target_schema.tables:
+                    continue
+                target = target_schema.tables[target_table_name]
+                if any(column_name not in target.columns
+                       for column_name in target_column_names):
+                    continue
+                target_columns = [target.columns[column_name]
+                                  for column_name in target_column_names]
+                origin.add_foreign_key(origin_columns, target, target_columns)
+        return catalog
 
-    def introspect_unique_keys(self, table_oid):
-        unique_keys = (super(PGCatalogIntrospectPGSQL, self)
-                        .introspect_unique_keys(table_oid))
-        rel = self.meta.pg_class[table_oid]
-        schema_name = self.meta.pg_namespace[rel.relnamespace].nspname
-        table_name = rel.relname
-        if (schema_name, table_name) in self.primary_keys:
-            column_names = self.primary_keys[schema_name, table_name]
-            unique_key = PrimaryKeyEntity(schema_name, table_name,
-                                          column_names)
-            unique_keys.append(unique_key)
-        return unique_keys
 
-    def introspect_foreign_keys(self, table_oid):
-        foreign_keys = (super(PGCatalogIntrospectPGSQL, self)
-                        .introspect_foreign_keys(table_oid))
-        rel = self.meta.pg_class[table_oid]
-        schema_name = self.meta.pg_namespace[rel.relnamespace].nspname
-        table_name = rel.relname
-        if (schema_name, table_name) in self.foreign_keys:
-            for key in self.foreign_keys[schema_name, table_name]:
-                column_names, target_name, target_column_names = key
-                target_schema_name, target_table_name = target_name
-                foreign_key = ForeignKeyEntity(schema_name, table_name,
-                                               column_names,
-                                               target_schema_name,
-                                               target_table_name,
-                                               target_column_names)
-                foreign_keys.append(foreign_key)
-        return foreign_keys
+class IntrospectPGSQLOIDDomain(IntrospectPGSQLDomain):
 
+    named(('pg_catalog', 'oid'))
 
+    def __call__(self):
+        return PGIntegerDomain(self.schema_name, self.name)
+
+
+class IntrospectPGSQLNameDomain(IntrospectPGSQLDomain):
+
+    named(('pg_catalog', 'name'))
+
+    def __call__(self):
+        return PGTextDomain(self.schema_name, self.name)
+
+

test/input/addon.yaml

     headers:
       Accept: text/html
 
+# TWEAK.SYSTEM - add access to system tables
+- title: tweak.system
+  ifdef: pgsql
+  tests:
+  # Addon description
+  - ctl: [ext, tweak.system]
 
+  # Test for added links
+  - load: demo
+    extensions:
+      tweak.system: {}
+  - uri: /pg_class{relname, relkind}?pg_namespace.nspname='ad'
+
+

test/output/pgsql.yaml

 
             </html>
 
+      - id: tweak.system
+        tests:
+        - ctl: [ext, tweak.system]
+          stdout: |+
+            TWEAK.SYSTEM - add access to system tables
+
+            This addon adds access to system catalog tables.
+
+            Currently, only PostgreSQL backend is supported.
+
+          exit: 0
+        - uri: /pg_class{relname, relkind}?pg_namespace.nspname='ad'
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | pg_class                     |
+             +------------------------------+
+             | relname            | relkind |
+            -+--------------------+---------+-
+             | school             | r       |
+             | school_pk          | i       |
+             | school_name_uk     | i       |
+             | department         | r       |
+             | department_pk      | i       |
+             | department_name_uk | i       |
+             | program            | r       |
+             | program_pk         | i       |
+             | program_title_uk   | i       |
+             | course             | r       |
+             | course_pk          | i       |
+             | course_title_uk    | i       |
+                                    (12 rows)
+
+             ----
+             /pg_class{relname,relkind}?pg_namespace.nspname='ad'
+             SELECT "pg_class"."relname",
+                    "pg_class"."relkind"
+             FROM "pg_class"
+                  INNER JOIN "pg_namespace"
+                             ON ("pg_class"."relnamespace" = "pg_namespace"."oid")
+             WHERE ("pg_namespace"."nspname" = 'ad')
+             ORDER BY "pg_class"."oid" ASC

test/sql/demo-mssql.sql

     campus              VARCHAR(5),
     CONSTRAINT school_pk
       PRIMARY KEY (code),
-    CONSTRAINT name_uk
+    CONSTRAINT school_name_uk
       UNIQUE (name),
     CONSTRAINT school_campus_ck
       CHECK (campus IN ('old', 'north', 'south'))

test/sql/demo-mysql.sql

     campus              VARCHAR(5),
     CONSTRAINT school_pk
       PRIMARY KEY (code),
-    CONSTRAINT name_uk
+    CONSTRAINT school_name_uk
       UNIQUE (name),
     CONSTRAINT school_campus_ck
       CHECK (campus IN ('old', 'north', 'south'))

test/sql/demo-oracle.sql

     campus              VARCHAR2(5),
     CONSTRAINT school_pk
       PRIMARY KEY (code),
-    CONSTRAINT name_uk
+    CONSTRAINT school_name_uk
       UNIQUE (name),
     CONSTRAINT school_campus_ck
       CHECK (campus IN ('old', 'north', 'south'))

test/sql/demo-pgsql.sql

     campus              VARCHAR(5),
     CONSTRAINT school_pk
       PRIMARY KEY (code),
-    CONSTRAINT name_uk
+    CONSTRAINT school_name_uk
       UNIQUE (name),
     CONSTRAINT school_campus_ck
       CHECK (campus IN ('old', 'north', 'south'))

test/sql/demo-sqlite.sql

     campus              VARCHAR(5),
     CONSTRAINT school_pk
       PRIMARY KEY (code),
-    CONSTRAINT name_uk
+    CONSTRAINT school_name_uk
       UNIQUE (name),
     CONSTRAINT school_campus_ck
       CHECK (campus IN ('old', 'north', 'south'))