mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
11a454c2d6
This commit applies ormolu to the whole Haskell code base by running `make format`. For in-flight branches, simply merging changes from `main` will result in merge conflicts. To avoid this, update your branch using the following instructions. Replace `<format-commit>` by the hash of *this* commit. $ git checkout my-feature-branch $ git merge <format-commit>^ # and resolve conflicts normally $ make format $ git commit -a -m "reformat with ormolu" $ git merge -s ours post-ormolu https://github.com/hasura/graphql-engine-mono/pull/2404 GitOrigin-RevId: 75049f5c12f430c615eafb4c6b8e83e371e01c8e
113 lines
5.2 KiB
Haskell
113 lines
5.2 KiB
Haskell
-- | Add metadata tags as comments to SQL queries.
|
|
module Data.SqlCommenter
|
|
( sqlCommenterGoogle,
|
|
sqlCommenterStandard,
|
|
Attribute,
|
|
)
|
|
where
|
|
|
|
import Data.List.NonEmpty qualified as NE
|
|
import Data.Text.Extended (commaSeparated)
|
|
import Hasura.Prelude
|
|
import Hasura.QueryTags
|
|
import Network.URI.Encode qualified as URI
|
|
|
|
-- | query-tags format as defined in the Spec <https://google.github.io/sqlcommenter/spec/#sql-commenter>
|
|
sqlCommenterGoogle :: QueryTagsAttributes -> QueryTagsComment
|
|
sqlCommenterGoogle qtAttributes
|
|
| null attributes = emptyQueryTagsComment
|
|
| otherwise = QueryTagsComment $ createSQLComment $ generateCommentTags (NE.fromList attributes)
|
|
where
|
|
createSQLComment comment = " /* " <> comment <> " */"
|
|
attributes = _unQueryTagsAttributes qtAttributes
|
|
|
|
-- | Default 'Query Tags' format in the Hasura GraphQL Engine
|
|
-- Creates simple 'key=value' pairs of query tags. No sorting, No URL encoding.
|
|
-- If the format of query tags is not mentioned in the metadata then, query-tags
|
|
-- are formatted using hte below format
|
|
sqlCommenterStandard :: QueryTagsAttributes -> QueryTagsComment
|
|
sqlCommenterStandard qtAttributes
|
|
| null attributes = emptyQueryTagsComment
|
|
| otherwise = QueryTagsComment $ createSQLComment $ generateComment (NE.fromList attributes)
|
|
where
|
|
generateComment attr = commaSeparated [k <> "=" <> v | (k, v) <- NE.toList attr]
|
|
createSQLComment comment = " /* " <> comment <> " */"
|
|
attributes = _unQueryTagsAttributes qtAttributes
|
|
|
|
-- | Top-level algorithm to generate the string comment from list of
|
|
-- 'Attribute's
|
|
-- Spec <https://google.github.io/sqlcommenter/spec/#sql-commenter>
|
|
-- See Note [Ambiguous SQLCommenterGoogle Specification]
|
|
generateCommentTags :: NE.NonEmpty Attribute -> Text
|
|
generateCommentTags attributes =
|
|
let -- 1. URL encode the key,value pairs. What the spec calls serialization.
|
|
-- https://google.github.io/sqlcommenter/spec/#key-serialization-algorithm
|
|
-- https://google.github.io/sqlcommenter/spec/#value-serialization-algorithm
|
|
encoded = NE.map urlEncodePair attributes
|
|
-- 2. Sort the pairs. Spec: https://google.github.io/sqlcommenter/spec/#sorting
|
|
-- Note the 'sort' from 'Data.List' works by sorting the first element in
|
|
-- the pair. And sorting on 'Text' is lexicographic.
|
|
sorted = NE.sort encoded
|
|
-- 3. Finally, serialize the pairs into a CSV. What the spec calls concatenation
|
|
-- https://google.github.io/sqlcommenter/spec/#concatenation
|
|
serialized = serializePairs sorted
|
|
in serialized
|
|
where
|
|
urlEncodePair (k, v) = (URI.encodeText k, URI.encodeText v)
|
|
serializePairs pairs = commaSeparated [k <> "='" <> v <> "'" | (k, v) <- NE.toList pairs]
|
|
|
|
-- | NOTE: [Ambiguous SQLCommenterGoogle Specification]
|
|
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
-- The specification is ambiguos/unclear about the following two steps:
|
|
--
|
|
-- 1. [Comment Escaping](https://google.github.io/sqlcommenter/spec/#comment-escaping)
|
|
-- ~~~~~~~~~~~~~~~~~~~~
|
|
-- The Spec states that:
|
|
--
|
|
-- > If a comment already exists within a SQL statement, we MUST NOT mutate that statement.
|
|
--
|
|
-- Oddly, the implementation of above rule/statement is not uniform among it's various language
|
|
-- libraries. That along with the fact that the above statement is clear for the scenarios when
|
|
-- we have properly formed comment, but does not state anything about what to do when the comments
|
|
-- are malformed is why it was decided to skip this step.
|
|
--
|
|
-- I have created a issue regarding this at https://github.com/google/sqlcommenter/issues/57
|
|
--
|
|
-- When discussed the above problem with Tiru, it was decided that since in our case we do not have
|
|
-- any other sql comments in the prepared statement, this step won't affect us thus it's okay to
|
|
-- skip this.
|
|
--
|
|
-- 2. [Escaping Meta Characters](https://google.github.io/sqlcommenter/spec/#meta-characters)
|
|
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
-- The Spec states that:
|
|
--
|
|
-- > Meta characters such as `'` should be escaped with a slash `\`.
|
|
--
|
|
-- And the algorithm in brief is:
|
|
-- 1. URL encode the Key/Value
|
|
-- 2. Escape the meta-characters withing the raw value; a single quote `'` becomes `\'`
|
|
--
|
|
-- From the above statement, it could be understood that the meta-characters in context of
|
|
-- sqlcommenter is `'`. And 'Network.URI.Encode.encodeText' is capable of encoding `'`
|
|
-- without any need of escaping.
|
|
--
|
|
-- ```
|
|
-- Prelude Network.URI.Encode> encodeText"'"
|
|
-- "%27"
|
|
--
|
|
-- Prelude Network.URI.Encode> encodeText "\'"
|
|
-- "%27"
|
|
-- ```
|
|
--
|
|
-- During the investigation, we also found that the reference implementation of the sqlcommenter
|
|
-- in other languages did not even do this step:
|
|
-- 1. https://github.com/google/sqlcommenter/blob/master/python/sqlcommenter-python/google/cloud/sqlcommenter/__init__.py#L29
|
|
-- 2. https://github.com/google/sqlcommenter/blob/master/java/sqlcommenter-java/src/main/java/com/google/cloud/sqlcommenter/threadlocalstorage/State.java#L179
|
|
--
|
|
-- The above can be summarized as:
|
|
-- 1. The meta-characters in the context of sqlcommenter is `'`
|
|
-- 2. Reference implementation of sqlcommenter does not include the step
|
|
-- 3. 'Network.URI.Encode.encodeText' can encode `'` without any need of escaping
|
|
--
|
|
-- Thus, on the basis of the above three facts we decided to ignore the 'Escape Meta Characters' step.
|