Kirill Simonov avatar Kirill Simonov committed 22fd197

Added tests for tweak.override.

Comments (0)

Files changed (7)

src/htsql_tweak/override/pattern.py

         if isinstance(entity, SchemaEntity):
             return matches(entity, self.schema_pattern)
         return (matches(entity.schema, self.schema_pattern) and
-                matches(entity.name, self.table_pattern))
+                matches(entity, self.table_pattern))
 
     def matches_target(self, entity):
         assert isinstance(entity, (SchemaEntity, TableEntity))
             columns = []
             for pattern in self.target_column_patterns:
                 matching = [column for column in table.columns
-                                   if matches(column.name, pattern)]
+                                   if matches(column, pattern)]
                 if len(matching) != 1:
                     return
                 [column] = matching
                     return
                 columns.append(column)
         else:
-            if self.table.primary_key:
-                columns = self.table.primary_key.origin_columns
+            if table.primary_key:
+                columns = table.primary_key.origin_columns
                 if len(columns) != len(self.column_patterns):
                     return
         return columns

test/input/addon.yaml

       tweak.meta: {}
   - uri: /meta(/column.sort(table.name, field.sort))
 
+# TWEAK.OVERRIDE - adjust database metadata
+- title: tweak.override
+  tests:
+  # Addon description
+  - ctl: [ext, tweak.override]
 
+  # Test `included-tables`
+  - load: demo
+    extensions:
+      tweak.override:
+        included-tables: [school, program]
+  - uri: /avg(school.count(program))
+  - uri: /avg(school.count(department))
+    expect: 400
+
+  # Test `excluded-tables`
+  - load: demo
+    extensions:
+      tweak.override:
+        excluded-tables: [department, course]
+  - uri: /avg(school.count(program))
+  - uri: /avg(school.count(department))
+    expect: 400
+
+  # Test `included-tables` and `excluded-tables` together
+  - load: demo
+    extensions:
+      tweak.override:
+        included-tables: [school, program, department, course]
+        excluded-tables: [department, course]
+  - uri: /avg(school.count(program))
+  - uri: /avg(school.count(department))
+    expect: 400
+
+  # Test schema qualifiers
+  - load: demo
+    extensions:
+      tweak.override:
+        included-tables: [ad.*]
+    ifdef: [pgsql, mssql]
+  - uri: /count(program)
+    ifdef: [pgsql, mssql]
+  - uri: /count(student)
+    expect: 400
+    ifdef: [pgsql, mssql]
+
+  # Test `included-columns`
+  - load: demo
+    extensions:
+      tweak.override:
+        included-columns: ["*code", "*name"]
+  - uri: /school{code, count(department)}
+  - uri: /student{name, count(enrollment)}
+    expect: 400
+
+  # Test `excluded-columns`
+  - load: demo
+    extensions:
+      tweak.override:
+        excluded-columns: [confidential.ssn]
+  - uri: /max(confidential.pay_grade)
+  - uri: /confidential.ssn
+    expect: 400
+
+  # Test `not-nulls`, `unique-keys` and `foreign-keys`
+  - load: demo
+    extensions:
+      tweak.override:
+        not-nulls: [student.school_code, student.program_code]
+        unique-keys:
+          - student(name, dob)!
+        foreign-keys:
+          - student(school_code) -> school
+          - student(name)? -> instructor(full_name)
+  - uri: /student{name, dob, school.code, program.code, exists(instructor)}.limit(1)
+
+  # Test `class-labels`
+  - load: demo
+    extensions:
+      tweak.override:
+        class-labels:
+          c14n: classification
+          campus: (school^campus)
+  - uri: /count(c14n)
+  - uri: /campus{campus, count(school)}
+  - uri: /classification
+    expect: 400
+
+  # Test `field-labels` and `field-orders`
+  - load: demo
+    extensions:
+      tweak.override:
+        field-labels:
+          program.includes: program -> program(*, part_of_code)
+          student.full_name: name
+          student.class: student -> enrollment, enrollment -> class
+          student.avg_grade: (avg(enrollment.grade))
+        field-orders:
+          student: [full_name, gender, dob, avg_grade]
+          program: [code, title, degree]
+  - uri: /program?exists(includes)
+  - uri: /program?exists(program_via_part_of)
+    expect: 400
+  - uri: /student?count(class)>55
+  - uri: /student.name
+    expect: 400
+
+  # Test `unlabeled_tables` and `unlabeled_columns`
+  - load: demo
+    extensions:
+      tweak.override:
+        field-labels:
+          student.class: student -> enrollment, enrollment -> class
+        unlabeled-tables: [enrollment]
+        unlabeled-columns: [id]
+  - uri: /student{name}?count(class)>55
+  - uri: /enrollment
+    expect: 400
+  - uri: /student.enrollment
+    expect: 400
+  - uri: /student.id
+    expect: 400
+
+

test/output/mssql.yaml

             ((\"column\".\"table_name\" = \"field\".\"table_name\") AND (\"column\".\"name\"
             = \"field\".\"name\"))\n ORDER BY \"table\".\"name\" ASC, \"field\".\"sort\"
             ASC, 1 ASC, 2 ASC\n"
+      - id: tweak.override
+        tests:
+        - ctl: [ext, tweak.override]
+          stdout: |+
+            TWEAK.OVERRIDE - adjust database metadata
+
+            This addon provides several ways to adjust database metadata.
+            It allows you to restrict access to specific tables and columns,
+            specify additional database constraints, change the names of
+            tables, columns and links, and define calculated attributes.
+
+            Parameter `included-tables` is a list of tables allowed to be used
+            by HTSQL.  Any table not in this list is completely hidden from
+            the HTSQL processor.  Each table in the list must have the form
+            `<table>` or `<schema>.<table>` and may include `*` symbol to
+            indicate any number of characters.
+
+            Parameter `excluded-tables` is a list of tables which are not
+            allowed to be used by HTSQL.  When both `included-tables` and
+            `excluded-tables` are specified, the only tables available
+            are those which are in the `included-tables` list, but not in
+            the `excluded-tables` list.
+
+            Parameter `included-columns` is a list of columns allowed to be
+            used by HTSQL.  Any column not in this list is completely hidden
+            from the HTSQL processor.  The column must have the form
+            `<column>`, `<table>.<column>` or `<schema>.<table>.<column>`
+            and may include `*` symbol to indicate any number of characters.
+
+            Parameter `excluded-columns` is a list of columns not allowed to
+            be used by HTSQL.
+
+            Parameter `not-nulls` is a list of columns with a `NOT NULL`
+            constraint.
+
+            Parameter `unique-keys` is a list of `UNIQUE` and `PRIMARY KEY`
+            constraints.  Each constraint definition must have a form:
+            `<table>(<column>, ...)`, optionally followed by `!` symbol.
+            The `!` symbol indicates a `PRIMARY KEY` constraint.
+
+            Parameter `foreign-keys` is a list of `FOREIGN KEY` constraints.
+            Each constraint definition must have the form:
+            `<table>(<column>,...) -> <table>(<column>,...)`,
+
+            Parameter `class-labels` is a mapping of labels to class
+            definitions.  Each class definition is either a table name
+            or an HTSQL expression wrapped in parentheses.
+
+            Parameter `field-labels` is a mapping of qualified labels to
+            field definitions.  Each field definition is either a column name,
+            a comma-separated list of `FOREIGN KEY` and reverse `FOREIGN KEY`
+            constraints, or an HTSQL expression.
+
+            Parameter `field-orders` is a mapping of table labels to lists
+            of fields to be displayed when an explicit selector is not
+            provided.
+
+            Parameter `unlabeled-tables` is a list of tables hidden from
+            the user.  The tables could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameter `unlabeled-columns` is a list of columns hidden from
+            the user.  The columns could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameters:
+              included-tables=TABLES   : permitted tables
+              excluded-tables=TABLES   : forbidden tables
+              included-columns=COLUMNS : permitted columns
+              excluded-columns=COLUMNS : forbidden columns
+              not-nulls=COLUMNS        : `NOT NULL` constraints
+              unique-keys=KEYS         : `UNIQUE` and `PRIMARY KEY` constraints
+              foreign-keys=KEYS        : `FOREIGN KEY` constraints`
+              class-labels=LABELS      : labels for tables and calculations
+              field-labels=LABELS      : labels for table fields
+              field-orders=LABELS      : default table fields
+              unlabeled-tables=TABLES  : ignored tables
+              unlabeled-columns=COLUMNS : ignored columns
+
+          exit: 0
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program)) |
+            -+----------------------------+-
+             |                   4.444444 |
+                                    (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE([program].[count], 0) AS DECIMAL(38)))
+             FROM [ad].[school]
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS [count],
+                                          [program].[school_code]
+                                   FROM [ad].[program]
+                                   GROUP BY [program].[school_code]) AS [program]
+                                  ON ([school].[code] = [program].[school_code])
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program)) |
+            -+----------------------------+-
+             |                   4.444444 |
+                                    (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE([program].[count], 0) AS DECIMAL(38)))
+             FROM [ad].[school]
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS [count],
+                                          [program].[school_code]
+                                   FROM [ad].[program]
+                                   GROUP BY [program].[school_code]) AS [program]
+                                  ON ([school].[code] = [program].[school_code])
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program)) |
+            -+----------------------------+-
+             |                   4.444444 |
+                                    (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE([program].[count], 0) AS DECIMAL(38)))
+             FROM [ad].[school]
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS [count],
+                                          [program].[school_code]
+                                   FROM [ad].[program]
+                                   GROUP BY [program].[school_code]) AS [program]
+                                  ON ([school].[code] = [program].[school_code])
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /count(program)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | count(program) |
+            -+----------------+-
+             |             40 |
+                        (1 row)
+
+             ----
+             /count(program)
+             SELECT COUNT(1)
+             FROM [ad].[program]
+        - uri: /count(student)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'student':
+                /count(student)
+                       ^^^^^^^
+        - uri: /school{code, count(department)}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | school                   |
+             +--------------------------+
+             | code | count(department) |
+            -+------+-------------------+-
+             | art  |                 2 |
+             | bus  |                 3 |
+             | edu  |                 2 |
+             | eng  |                 4 |
+             | la   |                 5 |
+             | mus  |                 4 |
+             | ns   |                 4 |
+             | ph   |                 0 |
+             | sc   |                 0 |
+                                 (9 rows)
+
+             ----
+             /school{code,count(department)}
+             SELECT [school].[code],
+                    COALESCE([department].[count], 0)
+             FROM [ad].[school]
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS [count],
+                                          [department].[school_code]
+                                   FROM [ad].[department]
+                                   GROUP BY [department].[school_code]) AS [department]
+                                  ON ([school].[code] = [department].[school_code])
+             ORDER BY 1 ASC
+        - uri: /student{name, count(enrollment)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /student{name, count(enrollment)}
+                                     ^^^^^^^^^^
+        - uri: /max(confidential.pay_grade)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | max(confidential.pay_grade) |
+            -+-----------------------------+-
+             |                           8 |
+                                     (1 row)
+
+             ----
+             /max(confidential.pay_grade)
+             SELECT MAX([confidential].[pay_grade])
+             FROM [id].[confidential]
+        - uri: /confidential.ssn
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'ssn':
+                /confidential.ssn
+                              ^^^
+        - uri: /student{name, dob, school.code, program.code, exists(instructor)}.limit(1)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student                                                       |
+             +---------------------------------------------------------------+
+             | name         | dob        | code | code  | exists(instructor) |
+            -+--------------+------------+------+-------+--------------------+-
+             | Adan Rodgers | 1986-08-15 | bus  | uecon | false              |
+                                                                       (1 row)
+
+             ----
+             /student{name,dob,school.code,program.code,exists(instructor)}.limit(1)
+             SELECT TOP 1
+                    [student].[name],
+                    [student].[dob],
+                    [school].[code],
+                    [program].[code],
+                    (CASE WHEN EXISTS(SELECT 1
+                                      FROM [id].[instructor]
+                                      WHERE ([student].[name] = [instructor].[full_name])
+                                            AND (1 <> 0)) THEN 1 ELSE 0 END)
+             FROM [ed].[student]
+                  INNER JOIN [ad].[school]
+                             ON ([student].[school_code] = [school].[code])
+                  INNER JOIN [ad].[program]
+                             ON (([student].[school_code] = [program].[school_code]) AND ([student].[program_code] = [program].[code]))
+             ORDER BY 1 ASC, 2 ASC
+        - uri: /count(c14n)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | count(c14n) |
+            -+-------------+-
+             |         103 |
+                     (1 row)
+
+             ----
+             /count(c14n)
+             SELECT COUNT(1)
+             FROM [rd].[classification]
+        - uri: /campus{campus, count(school)}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             |        | campus        |
+             |        +---------------+
+             | campus | count(school) |
+            -+--------+---------------+-
+             | north  |             1 |
+             | old    |             4 |
+             | south  |             2 |
+                               (3 rows)
+
+             ----
+             /campus{campus,count(school)}
+             SELECT [school].[campus],
+                    COUNT(1)
+             FROM [ad].[school]
+             WHERE ([school].[campus] IS NOT NULL)
+             GROUP BY [school].[campus]
+             ORDER BY 1 ASC
+        - uri: /classification
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'classification':
+                /classification
+                 ^^^^^^^^^^^^^^
+        - uri: /program?exists(includes)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | program                                         |
+             +-------------------------------------------------+
+             | code  | title                          | degree |
+            -+-------+--------------------------------+--------+-
+             | gecon | Master of Arts in Economics    | ma     |
+             | gbe   | M.S. in Bioengineering         | ms     |
+             | gee   | M.S. in Electrical Engineering | ms     |
+             | gme   | M.S. in Mechanical Engineering | ms     |
+             | gengl | Master of Arts in English      | ma     |
+             | gmth  | Masters of Science in          | bs     |
+             :       : Mathematics                    :        :
+             | pmth  | Doctorate of Science in        | ph     |
+             :       : Mathematics                    :        :
+                                                        (7 rows)
+
+             ----
+             /program?exists(includes)
+             SELECT [program].[code],
+                    [program].[title],
+                    [program].[degree]
+             FROM [ad].[program]
+             WHERE EXISTS(SELECT 1
+                          FROM [ad].[program] AS [program_1]
+                          WHERE ([program].[school_code] = [program_1].[school_code])
+                                AND ([program].[code] = [program_1].[part_of_code])
+                                AND (1 <> 0))
+             ORDER BY [program].[school_code] ASC, 1 ASC
+        - uri: /program?exists(program_via_part_of)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'program_via_part_of':
+                /program?exists(program_via_part_of)
+                                ^^^^^^^^^^^^^^^^^^^
+        - uri: /student?count(class)>55
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student                                            |
+             +----------------------------------------------------+
+             | full_name        | gender | dob        | avg_grade |
+            -+------------------+--------+------------+-----------+-
+             | Michael Castillo | m      | 1984-04-04 |  2.385185 |
+             | Marsha Mason     | f      | 1986-05-20 |  2.447169 |
+                                                           (2 rows)
+
+             ----
+             /student?count(class)>55
+             SELECT [student].[name],
+                    [student].[gender],
+                    [student].[dob],
+                    [enrollment].[avg]
+             FROM [ed].[student]
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS [count],
+                                          [enrollment].[student_id]
+                                   FROM [ed].[enrollment]
+                                        INNER JOIN [cd].[class]
+                                                   ON ([enrollment].[class_seq] = [class].[class_seq])
+                                   GROUP BY [enrollment].[student_id]) AS [class]
+                                  ON ([student].[id] = [class].[student_id])
+                  LEFT OUTER JOIN (SELECT AVG([enrollment].[grade]) AS [avg],
+                                          [enrollment].[student_id]
+                                   FROM [ed].[enrollment]
+                                   GROUP BY [enrollment].[student_id]) AS [enrollment]
+                                  ON ([student].[id] = [enrollment].[student_id])
+             WHERE (COALESCE([class].[count], 0) > 55)
+             ORDER BY [student].[id] ASC
+        - uri: /student.name
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'name':
+                /student.name
+                         ^^^^
+        - uri: /student{name}?count(class)>55
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student          |
+             +------------------+
+             | name             |
+            -+------------------+-
+             | Michael Castillo |
+             | Marsha Mason     |
+                         (2 rows)
+
+             ----
+             /student{name}?count(class)>55
+             SELECT [student].[name]
+             FROM [ed].[student]
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS [count],
+                                          [enrollment].[student_id]
+                                   FROM [ed].[enrollment]
+                                        INNER JOIN [cd].[class]
+                                                   ON ([enrollment].[class_seq] = [class].[class_seq])
+                                   GROUP BY [enrollment].[student_id]) AS [class]
+                                  ON ([student].[id] = [class].[student_id])
+             WHERE (COALESCE([class].[count], 0) > 55)
+             ORDER BY [student].[id] ASC
+        - uri: /enrollment
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /enrollment
+                 ^^^^^^^^^^
+        - uri: /student.enrollment
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /student.enrollment
+                         ^^^^^^^^^^
+        - uri: /student.id
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'id':
+                /student.id
+                         ^^

test/output/mysql.yaml

             ((\"column\".\"table_name\" = \"field\".\"table_name\") AND (\"column\".\"name\"
             = \"field\".\"name\"))\n ORDER BY \"table\".\"name\" ASC, \"field\".\"sort\"
             ASC, 1 ASC, 2 ASC\n"
+      - id: tweak.override
+        tests:
+        - ctl: [ext, tweak.override]
+          stdout: |+
+            TWEAK.OVERRIDE - adjust database metadata
+
+            This addon provides several ways to adjust database metadata.
+            It allows you to restrict access to specific tables and columns,
+            specify additional database constraints, change the names of
+            tables, columns and links, and define calculated attributes.
+
+            Parameter `included-tables` is a list of tables allowed to be used
+            by HTSQL.  Any table not in this list is completely hidden from
+            the HTSQL processor.  Each table in the list must have the form
+            `<table>` or `<schema>.<table>` and may include `*` symbol to
+            indicate any number of characters.
+
+            Parameter `excluded-tables` is a list of tables which are not
+            allowed to be used by HTSQL.  When both `included-tables` and
+            `excluded-tables` are specified, the only tables available
+            are those which are in the `included-tables` list, but not in
+            the `excluded-tables` list.
+
+            Parameter `included-columns` is a list of columns allowed to be
+            used by HTSQL.  Any column not in this list is completely hidden
+            from the HTSQL processor.  The column must have the form
+            `<column>`, `<table>.<column>` or `<schema>.<table>.<column>`
+            and may include `*` symbol to indicate any number of characters.
+
+            Parameter `excluded-columns` is a list of columns not allowed to
+            be used by HTSQL.
+
+            Parameter `not-nulls` is a list of columns with a `NOT NULL`
+            constraint.
+
+            Parameter `unique-keys` is a list of `UNIQUE` and `PRIMARY KEY`
+            constraints.  Each constraint definition must have a form:
+            `<table>(<column>, ...)`, optionally followed by `!` symbol.
+            The `!` symbol indicates a `PRIMARY KEY` constraint.
+
+            Parameter `foreign-keys` is a list of `FOREIGN KEY` constraints.
+            Each constraint definition must have the form:
+            `<table>(<column>,...) -> <table>(<column>,...)`,
+
+            Parameter `class-labels` is a mapping of labels to class
+            definitions.  Each class definition is either a table name
+            or an HTSQL expression wrapped in parentheses.
+
+            Parameter `field-labels` is a mapping of qualified labels to
+            field definitions.  Each field definition is either a column name,
+            a comma-separated list of `FOREIGN KEY` and reverse `FOREIGN KEY`
+            constraints, or an HTSQL expression.
+
+            Parameter `field-orders` is a mapping of table labels to lists
+            of fields to be displayed when an explicit selector is not
+            provided.
+
+            Parameter `unlabeled-tables` is a list of tables hidden from
+            the user.  The tables could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameter `unlabeled-columns` is a list of columns hidden from
+            the user.  The columns could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameters:
+              included-tables=TABLES   : permitted tables
+              excluded-tables=TABLES   : forbidden tables
+              included-columns=COLUMNS : permitted columns
+              excluded-columns=COLUMNS : forbidden columns
+              not-nulls=COLUMNS        : `NOT NULL` constraints
+              unique-keys=KEYS         : `UNIQUE` and `PRIMARY KEY` constraints
+              foreign-keys=KEYS        : `FOREIGN KEY` constraints`
+              class-labels=LABELS      : labels for tables and calculations
+              field-labels=LABELS      : labels for table fields
+              field-orders=LABELS      : default table fields
+              unlabeled-tables=TABLES  : ignored tables
+              unlabeled-columns=COLUMNS : ignored columns
+
+          exit: 0
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program))       |
+            -+----------------------------------+-
+             | 4.444444444444444444444444444444 |
+                                          (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE(`program`.`count`, 0) AS DECIMAL(65,30)))
+             FROM `school`
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS `count`,
+                                          `program`.`school_code`
+                                   FROM `program`
+                                   GROUP BY 2) AS `program`
+                                  ON (`school`.`code` = `program`.`school_code`)
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program))       |
+            -+----------------------------------+-
+             | 4.444444444444444444444444444444 |
+                                          (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE(`program`.`count`, 0) AS DECIMAL(65,30)))
+             FROM `school`
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS `count`,
+                                          `program`.`school_code`
+                                   FROM `program`
+                                   GROUP BY 2) AS `program`
+                                  ON (`school`.`code` = `program`.`school_code`)
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program))       |
+            -+----------------------------------+-
+             | 4.444444444444444444444444444444 |
+                                          (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE(`program`.`count`, 0) AS DECIMAL(65,30)))
+             FROM `school`
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS `count`,
+                                          `program`.`school_code`
+                                   FROM `program`
+                                   GROUP BY 2) AS `program`
+                                  ON (`school`.`code` = `program`.`school_code`)
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /school{code, count(department)}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | school                   |
+             +--------------------------+
+             | code | count(department) |
+            -+------+-------------------+-
+             | art  |                 2 |
+             | bus  |                 3 |
+             | edu  |                 2 |
+             | eng  |                 4 |
+             | la   |                 5 |
+             | mus  |                 4 |
+             | ns   |                 4 |
+             | ph   |                 0 |
+             | sc   |                 0 |
+                                 (9 rows)
+
+             ----
+             /school{code,count(department)}
+             SELECT `school`.`code`,
+                    COALESCE(`department`.`count`, 0)
+             FROM `school`
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS `count`,
+                                          `department`.`school_code`
+                                   FROM `department`
+                                   GROUP BY 2) AS `department`
+                                  ON (`school`.`code` = `department`.`school_code`)
+             ORDER BY 1 ASC
+        - uri: /student{name, count(enrollment)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /student{name, count(enrollment)}
+                                     ^^^^^^^^^^
+        - uri: /max(confidential.pay_grade)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | max(confidential.pay_grade) |
+            -+-----------------------------+-
+             |                           8 |
+                                     (1 row)
+
+             ----
+             /max(confidential.pay_grade)
+             SELECT MAX(`confidential`.`pay_grade`)
+             FROM `confidential`
+        - uri: /confidential.ssn
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'ssn':
+                /confidential.ssn
+                              ^^^
+        - uri: /student{name, dob, school.code, program.code, exists(instructor)}.limit(1)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student                                                       |
+             +---------------------------------------------------------------+
+             | name         | dob        | code | code  | exists(instructor) |
+            -+--------------+------------+------+-------+--------------------+-
+             | Adan Rodgers | 1986-08-15 | bus  | uecon | false              |
+                                                                       (1 row)
+
+             ----
+             /student{name,dob,school.code,program.code,exists(instructor)}.limit(1)
+             SELECT `student`.`name`,
+                    `student`.`dob`,
+                    `school`.`code`,
+                    `program`.`code`,
+                    EXISTS(SELECT TRUE
+                           FROM `instructor`
+                           WHERE (`student`.`name` = `instructor`.`full_name`))
+             FROM `student`
+                  INNER JOIN `school`
+                             ON (`student`.`school_code` = `school`.`code`)
+                  INNER JOIN `program`
+                             ON ((`student`.`school_code` = `program`.`school_code`) AND (`student`.`program_code` = `program`.`code`))
+             ORDER BY 1 ASC, 2 ASC
+             LIMIT 1
+        - uri: /count(c14n)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | count(c14n) |
+            -+-------------+-
+             |         103 |
+                     (1 row)
+
+             ----
+             /count(c14n)
+             SELECT COUNT(TRUE)
+             FROM `classification`
+        - uri: /campus{campus, count(school)}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             |        | campus        |
+             |        +---------------+
+             | campus | count(school) |
+            -+--------+---------------+-
+             | north  |             1 |
+             | old    |             4 |
+             | south  |             2 |
+                               (3 rows)
+
+             ----
+             /campus{campus,count(school)}
+             SELECT `school`.`campus`,
+                    COUNT(TRUE)
+             FROM `school`
+             WHERE (`school`.`campus` IS NOT NULL)
+             GROUP BY 1
+             ORDER BY 1 ASC
+        - uri: /classification
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'classification':
+                /classification
+                 ^^^^^^^^^^^^^^
+        - uri: /program?exists(includes)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | program                                         |
+             +-------------------------------------------------+
+             | code  | title                          | degree |
+            -+-------+--------------------------------+--------+-
+             | gecon | Master of Arts in Economics    | ma     |
+             | gbe   | M.S. in Bioengineering         | ms     |
+             | gee   | M.S. in Electrical Engineering | ms     |
+             | gme   | M.S. in Mechanical Engineering | ms     |
+             | gengl | Master of Arts in English      | ma     |
+             | gmth  | Masters of Science in          | bs     |
+             :       : Mathematics                    :        :
+             | pmth  | Doctorate of Science in        | ph     |
+             :       : Mathematics                    :        :
+                                                        (7 rows)
+
+             ----
+             /program?exists(includes)
+             SELECT `program`.`code`,
+                    `program`.`title`,
+                    `program`.`degree`
+             FROM `program`
+             WHERE EXISTS(SELECT TRUE
+                          FROM `program` AS `program_1`
+                          WHERE (`program`.`school_code` = `program_1`.`school_code`)
+                                AND (`program`.`code` = `program_1`.`part_of_code`))
+             ORDER BY `program`.`school_code` ASC, 1 ASC
+        - uri: /program?exists(program_via_part_of)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'program_via_part_of':
+                /program?exists(program_via_part_of)
+                                ^^^^^^^^^^^^^^^^^^^
+        - uri: /student?count(class)>55
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student                                            |
+             +----------------------------------------------------+
+             | full_name        | gender | dob        | avg_grade |
+            -+------------------+--------+------------+-----------+-
+             | Michael Castillo | m      | 1984-04-04 |  2.385185 |
+             | Marsha Mason     | f      | 1986-05-20 |  2.447170 |
+                                                           (2 rows)
+
+             ----
+             /student?count(class)>55
+             SELECT `student`.`name`,
+                    `student`.`gender`,
+                    `student`.`dob`,
+                    `enrollment`.`avg`
+             FROM `student`
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS `count`,
+                                          `enrollment`.`student_id`
+                                   FROM `enrollment`
+                                        INNER JOIN `class`
+                                                   ON (`enrollment`.`class_seq` = `class`.`class_seq`)
+                                   GROUP BY 2) AS `class`
+                                  ON (`student`.`id` = `class`.`student_id`)
+                  LEFT OUTER JOIN (SELECT AVG(`enrollment`.`grade`) AS `avg`,
+                                          `enrollment`.`student_id`
+                                   FROM `enrollment`
+                                   GROUP BY 2) AS `enrollment`
+                                  ON (`student`.`id` = `enrollment`.`student_id`)
+             WHERE (COALESCE(`class`.`count`, 0) > 55)
+             ORDER BY `student`.`id` ASC
+        - uri: /student.name
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'name':
+                /student.name
+                         ^^^^
+        - uri: /student{name}?count(class)>55
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student          |
+             +------------------+
+             | name             |
+            -+------------------+-
+             | Michael Castillo |
+             | Marsha Mason     |
+                         (2 rows)
+
+             ----
+             /student{name}?count(class)>55
+             SELECT `student`.`name`
+             FROM `student`
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS `count`,
+                                          `enrollment`.`student_id`
+                                   FROM `enrollment`
+                                        INNER JOIN `class`
+                                                   ON (`enrollment`.`class_seq` = `class`.`class_seq`)
+                                   GROUP BY 2) AS `class`
+                                  ON (`student`.`id` = `class`.`student_id`)
+             WHERE (COALESCE(`class`.`count`, 0) > 55)
+             ORDER BY `student`.`id` ASC
+        - uri: /enrollment
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /enrollment
+                 ^^^^^^^^^^
+        - uri: /student.enrollment
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /student.enrollment
+                         ^^^^^^^^^^
+        - uri: /student.id
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'id':
+                /student.id
+                         ^^

test/output/oracle.yaml

             ((\"column\".\"table_name\" = \"field\".\"table_name\") AND (\"column\".\"name\"
             = \"field\".\"name\"))\n ORDER BY \"table\".\"name\" ASC, \"field\".\"sort\"
             ASC, 1 ASC, 2 ASC\n"
+      - id: tweak.override
+        tests:
+        - ctl: [ext, tweak.override]
+          stdout: |+
+            TWEAK.OVERRIDE - adjust database metadata
+
+            This addon provides several ways to adjust database metadata.
+            It allows you to restrict access to specific tables and columns,
+            specify additional database constraints, change the names of
+            tables, columns and links, and define calculated attributes.
+
+            Parameter `included-tables` is a list of tables allowed to be used
+            by HTSQL.  Any table not in this list is completely hidden from
+            the HTSQL processor.  Each table in the list must have the form
+            `<table>` or `<schema>.<table>` and may include `*` symbol to
+            indicate any number of characters.
+
+            Parameter `excluded-tables` is a list of tables which are not
+            allowed to be used by HTSQL.  When both `included-tables` and
+            `excluded-tables` are specified, the only tables available
+            are those which are in the `included-tables` list, but not in
+            the `excluded-tables` list.
+
+            Parameter `included-columns` is a list of columns allowed to be
+            used by HTSQL.  Any column not in this list is completely hidden
+            from the HTSQL processor.  The column must have the form
+            `<column>`, `<table>.<column>` or `<schema>.<table>.<column>`
+            and may include `*` symbol to indicate any number of characters.
+
+            Parameter `excluded-columns` is a list of columns not allowed to
+            be used by HTSQL.
+
+            Parameter `not-nulls` is a list of columns with a `NOT NULL`
+            constraint.
+
+            Parameter `unique-keys` is a list of `UNIQUE` and `PRIMARY KEY`
+            constraints.  Each constraint definition must have a form:
+            `<table>(<column>, ...)`, optionally followed by `!` symbol.
+            The `!` symbol indicates a `PRIMARY KEY` constraint.
+
+            Parameter `foreign-keys` is a list of `FOREIGN KEY` constraints.
+            Each constraint definition must have the form:
+            `<table>(<column>,...) -> <table>(<column>,...)`,
+
+            Parameter `class-labels` is a mapping of labels to class
+            definitions.  Each class definition is either a table name
+            or an HTSQL expression wrapped in parentheses.
+
+            Parameter `field-labels` is a mapping of qualified labels to
+            field definitions.  Each field definition is either a column name,
+            a comma-separated list of `FOREIGN KEY` and reverse `FOREIGN KEY`
+            constraints, or an HTSQL expression.
+
+            Parameter `field-orders` is a mapping of table labels to lists
+            of fields to be displayed when an explicit selector is not
+            provided.
+
+            Parameter `unlabeled-tables` is a list of tables hidden from
+            the user.  The tables could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameter `unlabeled-columns` is a list of columns hidden from
+            the user.  The columns could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameters:
+              included-tables=TABLES   : permitted tables
+              excluded-tables=TABLES   : forbidden tables
+              included-columns=COLUMNS : permitted columns
+              excluded-columns=COLUMNS : forbidden columns
+              not-nulls=COLUMNS        : `NOT NULL` constraints
+              unique-keys=KEYS         : `UNIQUE` and `PRIMARY KEY` constraints
+              foreign-keys=KEYS        : `FOREIGN KEY` constraints`
+              class-labels=LABELS      : labels for tables and calculations
+              field-labels=LABELS      : labels for table fields
+              field-orders=LABELS      : default table fields
+              unlabeled-tables=TABLES  : ignored tables
+              unlabeled-columns=COLUMNS : ignored columns
+
+          exit: 0
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program))               |
+            -+------------------------------------------+-
+             | 4.44444444444444444444444444444444444444 |
+                                                  (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE("PROGRAM"."count", 0) AS NUMBER))
+             FROM "SCHOOL"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "PROGRAM"."SCHOOL_CODE"
+                                   FROM "PROGRAM"
+                                   GROUP BY "PROGRAM"."SCHOOL_CODE") "PROGRAM"
+                                  ON ("SCHOOL"."CODE" = "PROGRAM"."SCHOOL_CODE")
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program))               |
+            -+------------------------------------------+-
+             | 4.44444444444444444444444444444444444444 |
+                                                  (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE("PROGRAM"."count", 0) AS NUMBER))
+             FROM "SCHOOL"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "PROGRAM"."SCHOOL_CODE"
+                                   FROM "PROGRAM"
+                                   GROUP BY "PROGRAM"."SCHOOL_CODE") "PROGRAM"
+                                  ON ("SCHOOL"."CODE" = "PROGRAM"."SCHOOL_CODE")
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program))               |
+            -+------------------------------------------+-
+             | 4.44444444444444444444444444444444444444 |
+                                                  (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE("PROGRAM"."count", 0) AS NUMBER))
+             FROM "SCHOOL"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "PROGRAM"."SCHOOL_CODE"
+                                   FROM "PROGRAM"
+                                   GROUP BY "PROGRAM"."SCHOOL_CODE") "PROGRAM"
+                                  ON ("SCHOOL"."CODE" = "PROGRAM"."SCHOOL_CODE")
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /school{code, count(department)}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | school                   |
+             +--------------------------+
+             | code | count(department) |
+            -+------+-------------------+-
+             | art  |                 2 |
+             | bus  |                 3 |
+             | edu  |                 2 |
+             | eng  |                 4 |
+             | la   |                 5 |
+             | mus  |                 4 |
+             | ns   |                 4 |
+             | ph   |                 0 |
+             | sc   |                 0 |
+                                 (9 rows)
+
+             ----
+             /school{code,count(department)}
+             SELECT "SCHOOL"."CODE",
+                    COALESCE("DEPARTMENT"."count", 0)
+             FROM "SCHOOL"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "DEPARTMENT"."SCHOOL_CODE"
+                                   FROM "DEPARTMENT"
+                                   GROUP BY "DEPARTMENT"."SCHOOL_CODE") "DEPARTMENT"
+                                  ON ("SCHOOL"."CODE" = "DEPARTMENT"."SCHOOL_CODE")
+             ORDER BY 1 ASC
+        - uri: /student{name, count(enrollment)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /student{name, count(enrollment)}
+                                     ^^^^^^^^^^
+        - uri: /max(confidential.pay_grade)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | max(confidential.pay_grade) |
+            -+-----------------------------+-
+             |                           8 |
+                                     (1 row)
+
+             ----
+             /max(confidential.pay_grade)
+             SELECT MAX("CONFIDENTIAL"."PAY_GRADE")
+             FROM "CONFIDENTIAL"
+        - uri: /confidential.ssn
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'ssn':
+                /confidential.ssn
+                              ^^^
+        - uri: /student{name, dob, school.code, program.code, exists(instructor)}.limit(1)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student                                                       |
+             +---------------------------------------------------------------+
+             | name         | dob        | code | code  | exists(instructor) |
+            -+--------------+------------+------+-------+--------------------+-
+             | Adan Rodgers | 1986-08-15 | bus  | uecon | false              |
+                                                                       (1 row)
+
+             ----
+             /student{name,dob,school.code,program.code,exists(instructor)}.limit(1)
+             SELECT "STUDENT"."NAME",
+                    "STUDENT"."DOB",
+                    "SCHOOL"."CODE",
+                    "PROGRAM"."CODE",
+                    (CASE WHEN EXISTS(SELECT 1
+                                      FROM "INSTRUCTOR"
+                                      WHERE ("STUDENT"."NAME" = "INSTRUCTOR"."FULL_NAME")
+                                            AND (1 <> 0)) THEN 1 ELSE 0 END)
+             FROM (SELECT "STUDENT"."NAME",
+                          "STUDENT"."DOB",
+                          "STUDENT"."SCHOOL_CODE",
+                          "STUDENT"."PROGRAM_CODE"
+                   FROM (SELECT "STUDENT"."NAME",
+                                "STUDENT"."DOB",
+                                "STUDENT"."SCHOOL_CODE",
+                                "STUDENT"."PROGRAM_CODE"
+                         FROM "STUDENT"
+                         ORDER BY 1 ASC, 2 ASC) "STUDENT"
+                   WHERE (ROWNUM < 2)) "STUDENT"
+                  INNER JOIN "SCHOOL"
+                             ON ("STUDENT"."SCHOOL_CODE" = "SCHOOL"."CODE")
+                  INNER JOIN "PROGRAM"
+                             ON (("STUDENT"."SCHOOL_CODE" = "PROGRAM"."SCHOOL_CODE") AND ("STUDENT"."PROGRAM_CODE" = "PROGRAM"."CODE"))
+             ORDER BY 1 ASC, 2 ASC
+        - uri: /count(c14n)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | count(c14n) |
+            -+-------------+-
+             |         103 |
+                     (1 row)
+
+             ----
+             /count(c14n)
+             SELECT COUNT(1)
+             FROM "CLASSIFICATION"
+        - uri: /campus{campus, count(school)}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             |        | campus        |
+             |        +---------------+
+             | campus | count(school) |
+            -+--------+---------------+-
+             | north  |             1 |
+             | old    |             4 |
+             | south  |             2 |
+                               (3 rows)
+
+             ----
+             /campus{campus,count(school)}
+             SELECT "SCHOOL"."CAMPUS",
+                    COUNT(1)
+             FROM "SCHOOL"
+             WHERE ("SCHOOL"."CAMPUS" IS NOT NULL)
+             GROUP BY "SCHOOL"."CAMPUS"
+             ORDER BY 1 ASC NULLS FIRST
+        - uri: /classification
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'classification':
+                /classification
+                 ^^^^^^^^^^^^^^
+        - uri: /program?exists(includes)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | program                                         |
+             +-------------------------------------------------+
+             | code  | title                          | degree |
+            -+-------+--------------------------------+--------+-
+             | gecon | Master of Arts in Economics    | ma     |
+             | gbe   | M.S. in Bioengineering         | ms     |
+             | gee   | M.S. in Electrical Engineering | ms     |
+             | gme   | M.S. in Mechanical Engineering | ms     |
+             | gengl | Master of Arts in English      | ma     |
+             | gmth  | Masters of Science in          | bs     |
+             :       : Mathematics                    :        :
+             | pmth  | Doctorate of Science in        | ph     |
+             :       : Mathematics                    :        :
+                                                        (7 rows)
+
+             ----
+             /program?exists(includes)
+             SELECT "PROGRAM"."CODE",
+                    "PROGRAM"."TITLE",
+                    "PROGRAM"."DEGREE"
+             FROM "PROGRAM"
+             WHERE EXISTS(SELECT 1
+                          FROM "PROGRAM" "PROGRAM_1"
+                          WHERE ("PROGRAM"."SCHOOL_CODE" = "PROGRAM_1"."SCHOOL_CODE")
+                                AND ("PROGRAM"."CODE" = "PROGRAM_1"."PART_OF_CODE")
+                                AND (1 <> 0))
+             ORDER BY "PROGRAM"."SCHOOL_CODE" ASC, 1 ASC
+        - uri: /program?exists(program_via_part_of)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'program_via_part_of':
+                /program?exists(program_via_part_of)
+                                ^^^^^^^^^^^^^^^^^^^
+        - uri: /student?count(class)>55
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student                                                                           |
+             +-----------------------------------------------------------------------------------+
+             | full_name        | gender | dob        | avg_grade                                |
+            -+------------------+--------+------------+------------------------------------------+-
+             | Michael Castillo | m      | 1984-04-04 | 2.38518518518518518518518518518518518519 |
+             | Marsha Mason     | f      | 1986-05-20 | 2.44716981132075471698113207547169811321 |
+                                                                                          (2 rows)
+
+             ----
+             /student?count(class)>55
+             SELECT "STUDENT"."NAME",
+                    "STUDENT"."GENDER",
+                    "STUDENT"."DOB",
+                    "ENROLLMENT"."avg"
+             FROM "STUDENT"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "ENROLLMENT"."STUDENT_ID"
+                                   FROM "ENROLLMENT"
+                                        INNER JOIN "CLASS"
+                                                   ON ("ENROLLMENT"."CLASS_SEQ" = "CLASS"."CLASS_SEQ")
+                                   GROUP BY "ENROLLMENT"."STUDENT_ID") "CLASS"
+                                  ON ("STUDENT"."ID" = "CLASS"."STUDENT_ID")
+                  LEFT OUTER JOIN (SELECT AVG("ENROLLMENT"."GRADE") AS "avg",
+                                          "ENROLLMENT"."STUDENT_ID"
+                                   FROM "ENROLLMENT"
+                                   GROUP BY "ENROLLMENT"."STUDENT_ID") "ENROLLMENT"
+                                  ON ("STUDENT"."ID" = "ENROLLMENT"."STUDENT_ID")
+             WHERE (COALESCE("CLASS"."count", 0) > 55)
+             ORDER BY "STUDENT"."ID" ASC
+        - uri: /student.name
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'name':
+                /student.name
+                         ^^^^
+        - uri: /student{name}?count(class)>55
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student          |
+             +------------------+
+             | name             |
+            -+------------------+-
+             | Michael Castillo |
+             | Marsha Mason     |
+                         (2 rows)
+
+             ----
+             /student{name}?count(class)>55
+             SELECT "STUDENT"."NAME"
+             FROM "STUDENT"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "ENROLLMENT"."STUDENT_ID"
+                                   FROM "ENROLLMENT"
+                                        INNER JOIN "CLASS"
+                                                   ON ("ENROLLMENT"."CLASS_SEQ" = "CLASS"."CLASS_SEQ")
+                                   GROUP BY "ENROLLMENT"."STUDENT_ID") "CLASS"
+                                  ON ("STUDENT"."ID" = "CLASS"."STUDENT_ID")
+             WHERE (COALESCE("CLASS"."count", 0) > 55)
+             ORDER BY "STUDENT"."ID" ASC
+        - uri: /enrollment
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /enrollment
+                 ^^^^^^^^^^
+        - uri: /student.enrollment
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /student.enrollment
+                         ^^^^^^^^^^
+        - uri: /student.id
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'id':
+                /student.id
+                         ^^

test/output/pgsql.yaml

             ((\"column\".\"table_name\" = \"field\".\"table_name\") AND (\"column\".\"name\"
             = \"field\".\"name\"))\n ORDER BY \"table\".\"name\" ASC, \"field\".\"sort\"
             ASC, 1 ASC, 2 ASC\n"
+      - id: tweak.override
+        tests:
+        - ctl: [ext, tweak.override]
+          stdout: |+
+            TWEAK.OVERRIDE - adjust database metadata
+
+            This addon provides several ways to adjust database metadata.
+            It allows you to restrict access to specific tables and columns,
+            specify additional database constraints, change the names of
+            tables, columns and links, and define calculated attributes.
+
+            Parameter `included-tables` is a list of tables allowed to be used
+            by HTSQL.  Any table not in this list is completely hidden from
+            the HTSQL processor.  Each table in the list must have the form
+            `<table>` or `<schema>.<table>` and may include `*` symbol to
+            indicate any number of characters.
+
+            Parameter `excluded-tables` is a list of tables which are not
+            allowed to be used by HTSQL.  When both `included-tables` and
+            `excluded-tables` are specified, the only tables available
+            are those which are in the `included-tables` list, but not in
+            the `excluded-tables` list.
+
+            Parameter `included-columns` is a list of columns allowed to be
+            used by HTSQL.  Any column not in this list is completely hidden
+            from the HTSQL processor.  The column must have the form
+            `<column>`, `<table>.<column>` or `<schema>.<table>.<column>`
+            and may include `*` symbol to indicate any number of characters.
+
+            Parameter `excluded-columns` is a list of columns not allowed to
+            be used by HTSQL.
+
+            Parameter `not-nulls` is a list of columns with a `NOT NULL`
+            constraint.
+
+            Parameter `unique-keys` is a list of `UNIQUE` and `PRIMARY KEY`
+            constraints.  Each constraint definition must have a form:
+            `<table>(<column>, ...)`, optionally followed by `!` symbol.
+            The `!` symbol indicates a `PRIMARY KEY` constraint.
+
+            Parameter `foreign-keys` is a list of `FOREIGN KEY` constraints.
+            Each constraint definition must have the form:
+            `<table>(<column>,...) -> <table>(<column>,...)`,
+
+            Parameter `class-labels` is a mapping of labels to class
+            definitions.  Each class definition is either a table name
+            or an HTSQL expression wrapped in parentheses.
+
+            Parameter `field-labels` is a mapping of qualified labels to
+            field definitions.  Each field definition is either a column name,
+            a comma-separated list of `FOREIGN KEY` and reverse `FOREIGN KEY`
+            constraints, or an HTSQL expression.
+
+            Parameter `field-orders` is a mapping of table labels to lists
+            of fields to be displayed when an explicit selector is not
+            provided.
+
+            Parameter `unlabeled-tables` is a list of tables hidden from
+            the user.  The tables could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameter `unlabeled-columns` is a list of columns hidden from
+            the user.  The columns could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameters:
+              included-tables=TABLES   : permitted tables
+              excluded-tables=TABLES   : forbidden tables
+              included-columns=COLUMNS : permitted columns
+              excluded-columns=COLUMNS : forbidden columns
+              not-nulls=COLUMNS        : `NOT NULL` constraints
+              unique-keys=KEYS         : `UNIQUE` and `PRIMARY KEY` constraints
+              foreign-keys=KEYS        : `FOREIGN KEY` constraints`
+              class-labels=LABELS      : labels for tables and calculations
+              field-labels=LABELS      : labels for table fields
+              field-orders=LABELS      : default table fields
+              unlabeled-tables=TABLES  : ignored tables
+              unlabeled-columns=COLUMNS : ignored columns
+
+          exit: 0
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program)) |
+            -+----------------------------+-
+             |         4.4444444444444444 |
+                                    (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE("program"."count", 0) AS NUMERIC))
+             FROM "ad"."school"
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS "count",
+                                          "program"."school_code"
+                                   FROM "ad"."program"
+                                   GROUP BY 2) AS "program"
+                                  ON ("school"."code" = "program"."school_code")
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program)) |
+            -+----------------------------+-
+             |         4.4444444444444444 |
+                                    (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE("program"."count", 0) AS NUMERIC))
+             FROM "ad"."school"
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS "count",
+                                          "program"."school_code"
+                                   FROM "ad"."program"
+                                   GROUP BY 2) AS "program"
+                                  ON ("school"."code" = "program"."school_code")
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program)) |
+            -+----------------------------+-
+             |         4.4444444444444444 |
+                                    (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE("program"."count", 0) AS NUMERIC))
+             FROM "ad"."school"
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS "count",
+                                          "program"."school_code"
+                                   FROM "ad"."program"
+                                   GROUP BY 2) AS "program"
+                                  ON ("school"."code" = "program"."school_code")
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /count(program)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | count(program) |
+            -+----------------+-
+             |             40 |
+                        (1 row)
+
+             ----
+             /count(program)
+             SELECT COUNT(TRUE)
+             FROM "ad"."program"
+        - uri: /count(student)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'student':
+                /count(student)
+                       ^^^^^^^
+        - uri: /school{code, count(department)}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | school                   |
+             +--------------------------+
+             | code | count(department) |
+            -+------+-------------------+-
+             | art  |                 2 |
+             | bus  |                 3 |
+             | edu  |                 2 |
+             | eng  |                 4 |
+             | la   |                 5 |
+             | mus  |                 4 |
+             | ns   |                 4 |
+             | ph   |                 0 |
+             | sc   |                 0 |
+                                 (9 rows)
+
+             ----
+             /school{code,count(department)}
+             SELECT "school"."code",
+                    COALESCE("department"."count", 0)
+             FROM "ad"."school"
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS "count",
+                                          "department"."school_code"
+                                   FROM "ad"."department"
+                                   GROUP BY 2) AS "department"
+                                  ON ("school"."code" = "department"."school_code")
+             ORDER BY 1 ASC
+        - uri: /student{name, count(enrollment)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /student{name, count(enrollment)}
+                                     ^^^^^^^^^^
+        - uri: /max(confidential.pay_grade)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | max(confidential.pay_grade) |
+            -+-----------------------------+-
+             |                           8 |
+                                     (1 row)
+
+             ----
+             /max(confidential.pay_grade)
+             SELECT MAX("confidential"."pay_grade")
+             FROM "id"."confidential"
+        - uri: /confidential.ssn
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'ssn':
+                /confidential.ssn
+                              ^^^
+        - uri: /student{name, dob, school.code, program.code, exists(instructor)}.limit(1)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student                                                       |
+             +---------------------------------------------------------------+
+             | name         | dob        | code | code  | exists(instructor) |
+            -+--------------+------------+------+-------+--------------------+-
+             | Adan Rodgers | 1986-08-15 | bus  | uecon | false              |
+                                                                       (1 row)
+
+             ----
+             /student{name,dob,school.code,program.code,exists(instructor)}.limit(1)
+             SELECT "student"."name",
+                    "student"."dob",
+                    "school"."code",
+                    "program"."code",
+                    EXISTS(SELECT TRUE
+                           FROM "id"."instructor"
+                           WHERE ("student"."name" = "instructor"."full_name"))
+             FROM "ed"."student"
+                  INNER JOIN "ad"."school"
+                             ON ("student"."school_code" = "school"."code")
+                  INNER JOIN "ad"."program"
+                             ON (("student"."school_code" = "program"."school_code") AND ("student"."program_code" = "program"."code"))
+             ORDER BY 1 ASC, 2 ASC
+             LIMIT 1
+        - uri: /count(c14n)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | count(c14n) |
+            -+-------------+-
+             |         103 |
+                     (1 row)
+
+             ----
+             /count(c14n)
+             SELECT COUNT(TRUE)
+             FROM "rd"."classification"
+        - uri: /campus{campus, count(school)}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             |        | campus        |
+             |        +---------------+
+             | campus | count(school) |
+            -+--------+---------------+-
+             | north  |             1 |
+             | old    |             4 |
+             | south  |             2 |
+                               (3 rows)
+
+             ----
+             /campus{campus,count(school)}
+             SELECT "school"."campus",
+                    COUNT(TRUE)
+             FROM "ad"."school"
+             WHERE ("school"."campus" IS NOT NULL)
+             GROUP BY 1
+             ORDER BY 1 ASC NULLS FIRST
+        - uri: /classification
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'classification':
+                /classification
+                 ^^^^^^^^^^^^^^
+        - uri: /program?exists(includes)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | program                                         |
+             +-------------------------------------------------+
+             | code  | title                          | degree |
+            -+-------+--------------------------------+--------+-
+             | gecon | Master of Arts in Economics    | ma     |
+             | gbe   | M.S. in Bioengineering         | ms     |
+             | gee   | M.S. in Electrical Engineering | ms     |
+             | gme   | M.S. in Mechanical Engineering | ms     |
+             | gengl | Master of Arts in English      | ma     |
+             | gmth  | Masters of Science in          | bs     |
+             :       : Mathematics                    :        :
+             | pmth  | Doctorate of Science in        | ph     |
+             :       : Mathematics                    :        :
+                                                        (7 rows)
+
+             ----
+             /program?exists(includes)
+             SELECT "program"."code",
+                    "program"."title",
+                    "program"."degree"
+             FROM "ad"."program"
+             WHERE EXISTS(SELECT TRUE
+                          FROM "ad"."program" AS "program_1"
+                          WHERE ("program"."school_code" = "program_1"."school_code")
+                                AND ("program"."code" = "program_1"."part_of_code"))
+             ORDER BY "program"."school_code" ASC, 1 ASC
+        - uri: /program?exists(program_via_part_of)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'program_via_part_of':
+                /program?exists(program_via_part_of)
+                                ^^^^^^^^^^^^^^^^^^^
+        - uri: /student?count(class)>55
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student                                                     |
+             +-------------------------------------------------------------+
+             | full_name        | gender | dob        | avg_grade          |
+            -+------------------+--------+------------+--------------------+-
+             | Michael Castillo | m      | 1984-04-04 | 2.3851851851851852 |
+             | Marsha Mason     | f      | 1986-05-20 | 2.4471698113207547 |
+                                                                    (2 rows)
+
+             ----
+             /student?count(class)>55
+             SELECT "student"."name",
+                    "student"."gender",
+                    "student"."dob",
+                    "enrollment"."avg"
+             FROM "ed"."student"
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS "count",
+                                          "enrollment"."student_id"
+                                   FROM "ed"."enrollment"
+                                        INNER JOIN "cd"."class"
+                                                   ON ("enrollment"."class_seq" = "class"."class_seq")
+                                   GROUP BY 2) AS "class"
+                                  ON ("student"."id" = "class"."student_id")
+                  LEFT OUTER JOIN (SELECT AVG("enrollment"."grade") AS "avg",
+                                          "enrollment"."student_id"
+                                   FROM "ed"."enrollment"
+                                   GROUP BY 2) AS "enrollment"
+                                  ON ("student"."id" = "enrollment"."student_id")
+             WHERE (COALESCE("class"."count", 0) > 55)
+             ORDER BY "student"."id" ASC
+        - uri: /student.name
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'name':
+                /student.name
+                         ^^^^
+        - uri: /student{name}?count(class)>55
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student          |
+             +------------------+
+             | name             |
+            -+------------------+-
+             | Michael Castillo |
+             | Marsha Mason     |
+                         (2 rows)
+
+             ----
+             /student{name}?count(class)>55
+             SELECT "student"."name"
+             FROM "ed"."student"
+                  LEFT OUTER JOIN (SELECT COUNT(TRUE) AS "count",
+                                          "enrollment"."student_id"
+                                   FROM "ed"."enrollment"
+                                        INNER JOIN "cd"."class"
+                                                   ON ("enrollment"."class_seq" = "class"."class_seq")
+                                   GROUP BY 2) AS "class"
+                                  ON ("student"."id" = "class"."student_id")
+             WHERE (COALESCE("class"."count", 0) > 55)
+             ORDER BY "student"."id" ASC
+        - uri: /enrollment
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /enrollment
+                 ^^^^^^^^^^
+        - uri: /student.enrollment
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /student.enrollment
+                         ^^^^^^^^^^
+        - uri: /student.id
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'id':
+                /student.id
+                         ^^

test/output/sqlite.yaml

             \     INNER JOIN \"field\"\n                 ON ((\"column\".\"table_name\"
             = \"field\".\"table_name\") AND (\"column\".\"name\" = \"field\".\"name\"))\n
             ORDER BY \"table\".\"name\" ASC, \"field\".\"sort\" ASC, 1 ASC, 2 ASC\n"
+      - id: tweak.override
+        tests:
+        - ctl: [ext, tweak.override]
+          stdout: |+
+            TWEAK.OVERRIDE - adjust database metadata
+
+            This addon provides several ways to adjust database metadata.
+            It allows you to restrict access to specific tables and columns,
+            specify additional database constraints, change the names of
+            tables, columns and links, and define calculated attributes.
+
+            Parameter `included-tables` is a list of tables allowed to be used
+            by HTSQL.  Any table not in this list is completely hidden from
+            the HTSQL processor.  Each table in the list must have the form
+            `<table>` or `<schema>.<table>` and may include `*` symbol to
+            indicate any number of characters.
+
+            Parameter `excluded-tables` is a list of tables which are not
+            allowed to be used by HTSQL.  When both `included-tables` and
+            `excluded-tables` are specified, the only tables available
+            are those which are in the `included-tables` list, but not in
+            the `excluded-tables` list.
+
+            Parameter `included-columns` is a list of columns allowed to be
+            used by HTSQL.  Any column not in this list is completely hidden
+            from the HTSQL processor.  The column must have the form
+            `<column>`, `<table>.<column>` or `<schema>.<table>.<column>`
+            and may include `*` symbol to indicate any number of characters.
+
+            Parameter `excluded-columns` is a list of columns not allowed to
+            be used by HTSQL.
+
+            Parameter `not-nulls` is a list of columns with a `NOT NULL`
+            constraint.
+
+            Parameter `unique-keys` is a list of `UNIQUE` and `PRIMARY KEY`
+            constraints.  Each constraint definition must have a form:
+            `<table>(<column>, ...)`, optionally followed by `!` symbol.
+            The `!` symbol indicates a `PRIMARY KEY` constraint.
+
+            Parameter `foreign-keys` is a list of `FOREIGN KEY` constraints.
+            Each constraint definition must have the form:
+            `<table>(<column>,...) -> <table>(<column>,...)`,
+
+            Parameter `class-labels` is a mapping of labels to class
+            definitions.  Each class definition is either a table name
+            or an HTSQL expression wrapped in parentheses.
+
+            Parameter `field-labels` is a mapping of qualified labels to
+            field definitions.  Each field definition is either a column name,
+            a comma-separated list of `FOREIGN KEY` and reverse `FOREIGN KEY`
+            constraints, or an HTSQL expression.
+
+            Parameter `field-orders` is a mapping of table labels to lists
+            of fields to be displayed when an explicit selector is not
+            provided.
+
+            Parameter `unlabeled-tables` is a list of tables hidden from
+            the user.  The tables could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameter `unlabeled-columns` is a list of columns hidden from
+            the user.  The columns could still be used in SQL generated by
+            the HTSQL translator.
+
+            Parameters:
+              included-tables=TABLES   : permitted tables
+              excluded-tables=TABLES   : forbidden tables
+              included-columns=COLUMNS : permitted columns
+              excluded-columns=COLUMNS : forbidden columns
+              not-nulls=COLUMNS        : `NOT NULL` constraints
+              unique-keys=KEYS         : `UNIQUE` and `PRIMARY KEY` constraints
+              foreign-keys=KEYS        : `FOREIGN KEY` constraints`
+              class-labels=LABELS      : labels for tables and calculations
+              field-labels=LABELS      : labels for table fields
+              field-orders=LABELS      : default table fields
+              unlabeled-tables=TABLES  : ignored tables
+              unlabeled-columns=COLUMNS : ignored columns
+
+          exit: 0
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program)) |
+            -+----------------------------+-
+             |              4.44444444444 |
+                                    (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE("program"."count", 0) AS REAL))
+             FROM "school"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "program"."school_code"
+                                   FROM "program"
+                                   GROUP BY 2) AS "program"
+                                  ON ("school"."code" = "program"."school_code")
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program)) |
+            -+----------------------------+-
+             |              4.44444444444 |
+                                    (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE("program"."count", 0) AS REAL))
+             FROM "school"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "program"."school_code"
+                                   FROM "program"
+                                   GROUP BY 2) AS "program"
+                                  ON ("school"."code" = "program"."school_code")
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /avg(school.count(program))
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | avg(school.count(program)) |
+            -+----------------------------+-
+             |              4.44444444444 |
+                                    (1 row)
+
+             ----
+             /avg(school.count(program))
+             SELECT AVG(CAST(COALESCE("program"."count", 0) AS REAL))
+             FROM "school"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "program"."school_code"
+                                   FROM "program"
+                                   GROUP BY 2) AS "program"
+                                  ON ("school"."code" = "program"."school_code")
+        - uri: /avg(school.count(department))
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'department':
+                /avg(school.count(department))
+                                  ^^^^^^^^^^
+        - uri: /school{code, count(department)}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | school                   |
+             +--------------------------+
+             | code | count(department) |
+            -+------+-------------------+-
+             | art  |                 2 |
+             | bus  |                 3 |
+             | edu  |                 2 |
+             | eng  |                 4 |
+             | la   |                 5 |
+             | mus  |                 4 |
+             | ns   |                 4 |
+             | ph   |                 0 |
+             | sc   |                 0 |
+                                 (9 rows)
+
+             ----
+             /school{code,count(department)}
+             SELECT "school"."code",
+                    COALESCE("department"."count", 0)
+             FROM "school"
+                  LEFT OUTER JOIN (SELECT COUNT(1) AS "count",
+                                          "department"."school_code"
+                                   FROM "department"
+                                   GROUP BY 2) AS "department"
+                                  ON ("school"."code" = "department"."school_code")
+             ORDER BY 1 ASC
+        - uri: /student{name, count(enrollment)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'enrollment':
+                /student{name, count(enrollment)}
+                                     ^^^^^^^^^^
+        - uri: /max(confidential.pay_grade)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | max(confidential.pay_grade) |
+            -+-----------------------------+-
+             |                           8 |
+                                     (1 row)
+
+             ----
+             /max(confidential.pay_grade)
+             SELECT MAX("confidential"."pay_grade")
+             FROM "confidential"
+        - uri: /confidential.ssn
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: unknown attribute 'ssn':
+                /confidential.ssn
+                              ^^^
+        - uri: /student{name, dob, school.code, program.code, exists(instructor)}.limit(1)
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | student                                                       |
+             +---------------------------------------------------------------+
+             | name         | dob        | code | code  | exists(instructor) |
+            -+--------------+------------+------+-------+--------------------+-
+             | Adan Rodgers | 1986-08-15 | bus  | uecon | false              |
+                                                                       (1 row)
+
+             ----
+             /student{name,dob,school.code,program.code,exists(instructor)}.limit(1)
+             SELECT "student"."name",
</