graphql-engine-1.0.0: GraphQL API over Postgres
Safe HaskellNone
LanguageHaskell2010

Hasura.Backends.Postgres.SQL.RenameIdentifiers

Description

Postgres SQL Rename Identifiers

  1. Prefix table names with underscores to avoid issues where column names and tables conflict. This can happen because we give columns and tables the name root for some reason, and that can trip up row_to_json. See https://github.com/PostgREST/postgrest/issues/993#issuecomment-340377813. An alternative solution would be to not create a TableAlias with the name root, but that seemed a bit complicated for me to do at the time.
  2. Bypass the Postgres limitation of truncating identifiers to 63 characters long by prepending they identifier's md5 hash when they are longer than 63 characters.

We do both operations in the same traversal for performance reasons, but a simpler implementation of (1) would be transformBi prefixHash from the uniplate or the generic-plate package.

See Postgres docs: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS

Synopsis

API

renameIdentifiers :: Select -> Select Source #

Prefix table names with undescores and rename long identifiers.

renameIdentifiersSelectWith :: SelectWithG Select -> SelectWithG Select Source #

prefix table names with undescores and rename long identifiers.

Prefix long identifiers

We are traversing the query transform Identifiers in a query that are longer than 63 characters by prefixing them with their md5 hash.

This works because:

  1. Database table references and column references also use Identifier, but they cannot be more than 63 characters long, so we will never transform those cases.
  2. The md5 hash is a 32 characters long deterministic representation of the identifier, so even when truncated by postgres, it will always be enough to identify the identifiers.
  3. It is possible in theory for to identifiers to produce the same hash, but extremely unlikely and I don't think we'll ever run into such a case.

Note that we could in theory replace the identifier with a hash, but we prefix the hash instead for our benefit as developers - if we need to read the query at some point we can look at the rest of the identifier ignoring the hash for a readable representation.

prefixHash :: Identifier -> Identifier Source #

Prefix md5 hash if identifier length is over 63 characters. We assume (rightly) that identifiers with names longer than 63 characters are not database table columns, and are made by us using aliases, so we should be free to rename them.

Prefix table names with underscore

 

API

Data types

newtype TableNames Source #

The tables in scope

Constructors

TableNames 

Fields

Utilities

mkPrefixedName :: Identifier -> Identifier Source #

attach a prefix to an identifier

addAliasAndPrefixHash :: TableAlias -> MyState TableAlias Source #

Add the alias to the set and return a prefixed alias.

getTableNameAndPrefixHash :: Identifier -> MyState Identifier Source #

Search for the identifier in the table names set and return a prefixed identifier if found, or the original identifier if not found in the set.

restoringTables :: MyState a -> MyState a Source #

Run an action that might change the tables names set and discard the changes made to the set.

Algorithm

uSelectWith :: SelectWithG Select -> MyState (SelectWithG Select) Source #

We run the algorithm on each CTE separately and discard the table names set, then we run the algorithm on the main select and return that result (with the table names found in scope).

uSelect :: Select -> MyState Select Source #

We go in order of each component in the select, starting with the from and CTE clauses (as those introduce new table names to scope). We return a transformed Select (with the table names).

uFromExp :: FromExp -> MyState FromExp Source #

Transform every from_item. Potentially introduces a new alias.

uFromItem :: FromItem -> MyState FromItem Source #

Transform a single from_item. Potentially introduces a new alias.

uFunctionExp :: FunctionExp -> MyState FunctionExp Source #

Transform a function call expression.

uFunctionArgs :: FunctionArgs -> MyState FunctionArgs Source #

Transform function call arguments.

uFunctionAlias :: FunctionAlias -> MyState FunctionAlias Source #

Transform a function call alias.

uJoinExp :: JoinExpr -> MyState JoinExpr Source #

Transform join expressions. Potentially introduces a new alias.

uJoinCond :: JoinCond -> MyState JoinCond Source #

Transform Join condition. ON join condition might contain references to table names and aliases.

uBoolExp :: BoolExp -> MyState BoolExp Source #

Transform boolean expression.

The boolean expression structure does not contain a table name currently, So we look for SQLExps and transform those, as those may contain table names and aliases.

We discard table names that might be introduced here because we don't use them outside of the boolean expression.

uSqlExp :: SQLExp -> MyState SQLExp Source #

Transform a SQL expression. We look for table names and aliases and rename them if needed. SQL expressions do not introduce new table aliases, so we discard the new aliases that might be generated here.

uOrderBy :: OrderByExp -> MyState OrderByExp Source #

Transform order by clauses. Since order by does not introduce new aliases we can discard the new names that might be added, this is already done by uSqlExp though.

prefixHashTableAlias :: TableAlias -> TableAlias Source #

Prefix a table alias with a hash if needed.

prefixHashColumnAlias :: ColumnAlias -> ColumnAlias Source #

Prefix a column alias with a hash if needed.