/src/Database/HsSqlPpp/Internals/TypeChecking/Ddl/CreateFunction.ag

http://github.com/JakeWheat/hssqlppp · Unknown · 171 lines · 131 code · 40 blank · 0 comment · 0 complexity · 95e35d1b6962ce6bbbdd07202a7ba14b MD5 · raw file

  1. {-
  2. This file contains the ag code for create function statements.
  3. | CreateFunction ann:Annotation
  4. name : String
  5. params : ParamDefList
  6. rettype : TypeName
  7. rep : Replace
  8. lang : Language
  9. body : FnBody
  10. vol : Volatility
  11. DATA FnBody | SqlFnBody ann:Annotation sts : StatementList
  12. | PlpgsqlFnBody ann:Annotation vars:VarDefList sts : StatementList
  13. DATA ParamDef | ParamDef ann:Annotation name:String typ:TypeName
  14. | ParamDefTp ann:Annotation typ:TypeName
  15. DATA VarDef | VarDef ann:Annotation
  16. name : String
  17. typ : TypeName
  18. value : (Maybe Expression)
  19. paramdeflist: need cat, produces lb
  20. vardef needs cat, produces lb
  21. function body: gets cat and new lb
  22. -}
  23. {
  24. data ParamName = NamedParam Int String
  25. | UnnamedParam Int
  26. }
  27. ATTR ParamDef [pos : Int||paramName : ParamName
  28. namedType : {Maybe Type}]
  29. ATTR ParamDefList [pos : Int||params : {[(ParamName, Maybe Type)]}]
  30. -- collect the information to update the local bindings from the parameters
  31. SEM ParamDef
  32. | ParamDef ParamDefTp
  33. lhs.namedType = @typ.namedType
  34. | ParamDef
  35. lhs.paramName = NamedParam @lhs.pos (ncStr @name)
  36. | ParamDefTp
  37. lhs.paramName = UnnamedParam @lhs.pos
  38. SEM ParamDefList
  39. | Nil lhs.params = []
  40. | Cons lhs.params = ((@hd.paramName, @hd.namedType) : @tl.params)
  41. hd.pos = @lhs.pos
  42. tl.pos = @lhs.pos + 1
  43. -- create the new local bindings and pass into the function body
  44. -- just
  45. SEM Statement
  46. | CreateFunction
  47. --add the parameters to the catalog for the contained statements
  48. body.lib = either (const @lhs.lib) id $ do
  49. _ <- lmt @rettype.namedType
  50. lbUpdate @lhs.cat (LBIds ((getTName @name.originalTree) ++ " parameters") (Just (getTName @name.originalTree)) paramsNoPos) @lhs.lib
  51. >>= lbUpdate @lhs.cat (LBIds ((getTName @name.originalTree) ++ " parameters") Nothing paramsPosOnly)
  52. where
  53. paramsPosOnly :: [(String,Type)]
  54. paramsPosOnly = mapMaybe prm @params.params
  55. prm :: (ParamName,Maybe Type) -> Maybe (String,Type)
  56. prm (NamedParam p _,Just t) = Just ("$" ++ show p, t)
  57. prm (UnnamedParam p,Just t) = Just ("$" ++ show p, t)
  58. prm _ = Nothing
  59. paramsNoPos :: [(String,Type)]
  60. paramsNoPos = mapMaybe pnp @params.params
  61. pnp :: (ParamName,Maybe Type) -> Maybe (String,Type)
  62. pnp (NamedParam _ n,Just t) = Just (n,t)
  63. pnp _ = Nothing
  64. params.pos = 1
  65. {-
  66. boilerplate
  67. -}
  68. SEM Statement
  69. | CreateFunction
  70. loc.tpe = Right $ Pseudo Void
  71. loc.catUpdates = either (const []) id $ do
  72. let ps = mapMaybe lpt @params.params
  73. rt <- lmt @rettype.namedType
  74. return [CatCreateFunction FunName
  75. (map toLower (getTName @name.originalTree))
  76. ps
  77. rt
  78. False]
  79. where
  80. lpt (_,Just t) = Just t
  81. lpt _ = Nothing
  82. loc.backTree = CreateFunction @ann
  83. @name.originalTree
  84. @params.annotatedTree
  85. @rettype.annotatedTree
  86. @rep
  87. @lang
  88. @body.annotatedTree
  89. @vol
  90. loc.statementType = Nothing
  91. body.cat = @lhs.inProducedCat
  92. {-
  93. == function prototype
  94. all you do here is type check enough to produce the prototype
  95. information which is added to the catalog, this means the function
  96. name, parameter types, and the return type.
  97. type checking failure is contained so that the function prototype is
  98. produced iff the parameter and return types check ok. Any type errors
  99. in the function body (including the top level variable declarations
  100. don't affect the prototype, and hence callers of the function).
  101. -}
  102. {-
  103. ISSUE:
  104. when writing an sql file, you can put a create function which refers
  105. to a table definition that is given later. As long as the function
  106. isn't called before the table definition is given, this is ok. To
  107. handle this, need to gather the function prototype, but delay checking
  108. the contents until either a) all the other type checking has been
  109. done, or b) the function is needed (list ways this can happen: used in
  110. a view (even then, not needed until view is used), function can be
  111. called directly, or indirectly in another function call, ...)
  112. No thoughts on how to do this - but at some point want to support
  113. 'declarative' sql source code, where the order doesn't matter, and
  114. this code figures out an order to load it into the database which will
  115. get past pgs checks, so hopefully the solution will move towards this
  116. goal also. One additional consideration is that the error message in a
  117. situation like this would be really helpful if it could tell that a
  118. problem like this could be fixed with a reordering, and suggest that
  119. reordering.
  120. New plan: do two passes, type check everything but the bodies of
  121. functions in first pass, then type check bodies of functions in second
  122. pass. Not perfect, but better than current situation. This will be
  123. achieved by using a separate cat attribute which is the same as the cat
  124. value which gets returned from the annotation functions in AstInternal.ag
  125. -}
  126. {-
  127. TODO: using fromRight on it's own for identifier bindings or cat
  128. updates is wrong, if an error is produced then this needs to be added
  129. to an annotation somewhere. Some of the code uses error instead of fromRight
  130. which is even worse.
  131. -}