úÎй†z     None+;N ÆTemplatePG currenly only supports a handful of types. It also doesn't distinguish between numeric types with different ranges. More types are the most likely feature of future TemplatePG releases.boolintegerfloat text/varchar&timestamptz (timestamp with time zone)date (day without time)%interval (a time interval), send-onlyfConvert a type OID from PostgreSQL's catalog to a TemplatePG representation. To get a list of types:  SELECT typname, oid FROM pg_typeº Note that I have assumed, but not tested, that type OIDs for these basic types are consistent across installations. If not, I'm going to have to switch to using the text descriptionsÿ=This is PostgreSQL's canonical timestamp format. Time conversions are complicated a bit because PostgreSQL doesn't support timezones with minute parts, and Haskell only supports timezones with minutes parts. We'll need to truncate and pad timestamp strings accordingly. This means with minute parts will not work. ÊConvert a Haskell value to a string of the given PostgreSQL type. Or, more accurately, given a PostgreSQL type, create a function for converting compatible Haskell values into a string of that type. )pgTypeToString :: PGType -> (? -> String) àConvert a string from PostgreSQL of the given type into an appropriate Haskell value. Or, more accurately, given a PostgreSQL type, create a function for converting a string of that type into a compatible Haskell value. )pgStringToType :: PGType -> (String -> ?)ÿMake a string safe for interpolation (escape single-quotes). This relies on standard_conforming_strings = on in postgresql.conf. I'm not 100% sure that this makes all strings safe for execution. I don't know if it's possible to inject SQL with strange (possibly Unicode) characters.PostgreSQL type OID     None+;N +PGException is thrown upon encountering an [ with severity of ERROR, FATAL, or PANIC. It holds the SQLSTATE and message of the error. ^PGMessage represents a PostgreSQL protocol message that we'll either send or receive. See  Phttp://www.postgresql.org/docs/current/interactive/protocol-message-formats.html.!‡CommandComplete is bare for now, although it could be made to contain the number of rows affected by statements in a later version."ÌEach DataRow (result of a query) is a list of ByteStrings (or just Nothing for null values, to distinguish them from emtpy strings). The ByteStrings can then be converted to the appropriate type by  .#WDescribe a SQL query/statement. The SQL string can contain parameters ($1, $2, etc.).(An ErrorResponse contains the severity, SQLSTATE#, and message of an error. See  Mhttp://www.postgresql.org/docs/current/interactive/protocol-error-fields.html.$¾A ParameterDescription describes the type of a given SQL query/statement parameter ($1, $2, etc.). Unfortunately, PostgreSQL does not give us nullability information for the parameter.%*Parse SQL Destination (prepared statement)&©A RowDescription contains the name, type, table OID, and column number of the resulting columns(s) of a query. The column number is useful for inferring nullability.'RSimpleQuery takes a simple SQL string. Parameters ($1, $2, etc.) aren't allowed.(iDetermine whether or not to print debug output based on the value of the TPG_DEBUG environment variable. Connect to a PostgreSQL server.\Disconnect from a PostgreSQL server. Note that this currently doesn't send a close message.)ÐConvert a string to a NULL-terminated UTF-8 string. The PostgreSQL protocol transmits most strings in this format. I haven't yet found a function for doing this without requiring manual memory management.*qGiven a message, build the over-the-wire representation of it. Note that we send fewer messages than we receive.+-Get the type and size of an incoming message.,Parse an incoming message.-)Send a message to PostgreSQL (low-level)..iReceive the next message from PostgreSQL (low-level). Note that this will block until it gets a message./#Wait for a message of a given type.ÿ Describe a SQL statement/query. A statement description consists of 0 or more parameter descriptions (a PostgreSQL type) and zero or more result field descriptions (for queries) (consist of the name of the field, the type of the field, and a nullability indicator).;A simple query is one which requires sending only a single 'Ÿ message to the PostgreSQL server. The query is sent as a single string; you cannot bind parameters. Note that queries can return 0 results (an empty list).VWhile not strictly necessary, this can make code a little bit clearer. It executes a ' but doesn't look for results.0`All PostgreSQL messages have a common header: an identifying character and a 32-bit size field.' 12!"#34567$8%9:&';<( the host to connect tothe port to connect onthe database to connect tothe username to connect asthe password to connect with5a handle to communicate with the PostgreSQL server ona handle from  )=*+, the type of the message to parse-./rA list of message identifiers, the first of which found while reading messages from PostgreSQL will be returned. SQL string_a list of parameter types, and a list of result field names, types, and nullability indicators. SQL string?A list of result rows, which themselves are a list of fields. SQL string0>    12!"#34567$8%9:&';<( )=*+,-./0>None+;N\Grab a PostgreSQL connection for compile time. We do so through the environment variables: TPG_DB, TPG_HOST, TPG_PORT, TPG_USER, and TPG_PASS. Only TPG_DB is required.?ÓThis is where most of the magic happens. This doesn't result in a PostgreSQL prepared statement, it just creates one to do type inference. This returns a prepared SQL string with all values (as an expression)@3"weave" 2 lists of equal length into a single list.AQ"weave" a list of SQL fragements an Haskell expressions into a single SQL string. AqueryTuples :: String -> (Handle -> IO [(column1, column2, ...)])EQuery a PostgreSQL server and return the results as a list of tuples.Example (where h is a handle from  ):9@$(queryTuples "SELECT usesysid, usename FROM pg_user") h$> IO [(Maybe String, Maybe Integer)]@ FqueryTuple :: String -> (Handle -> IO (Maybe (column1, column2, ...)))„Convenience function to query a PostgreSQL server and return the first result as a tuple. If the query produces no results, return B.Example (where h is a handle from  ):@let sysid = 10::Integer;P$(queryTuple "SELECT usesysid, usename FROM pg_user WHERE usesysid = {sysid}") h*> IO (Maybe (Maybe String, Maybe Integer))@ &execute :: String -> (Handle -> IO ())EConvenience function to execute a statement on the PostgreSQL server.Example (where h is a handle from  ):@let rolename = "BOfH"($(execute "CREATE ROLE {rolename}") h @‰Run a sequence of IO actions (presumably SQL statements) wrapped in a transaction. Unfortunately you're restricted to using this in the C" Monad for now due to the use of D. I'm debating adding a  MonadPeelIO version.Roll back a transaction.9Ignore duplicate key errors. This is also limited to the C Monad.ENGiven a result description, create a function to convert a result to a tuple.FiGiven a raw PostgreSQL result and a result field type, convert the appropriate field to a Haskell value.GLike  , but deal with possible NULLs. If the boolean argument is HI, that means that we know that the value is not nullable and we can use I" to keep the code simple. If it's JA, then we don't know if the value is nullable and must return a K value in case it is.LRGiven a SQL string return a list of SQL parts and expression parts. For example: C"SELECT * FROM table WHERE id = {someID} AND age > {baseAge * 1.5}" becomes: ](["SELECT * FROM table WHERE id = ", " AND age > "], ["someID", "baseAge * 1.5"])MParameters are enclosed in {}B and can be any Haskell expression supported by haskell-src-meta.?a SQL string, with-a prepared SQL string and result descriptions@A SQL fragmentsHaskell expressionsNEresult description?A function for converting a row of the given result descriptionF8the name of the variable containing the result list (of K O)the result field type and indexGnullability indicatorLPQRM?@ANEFGLPQRMNone+;N  S      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKFLMNOPIJQFRSIJTFGUVWXYZ[\]^_templ_CfWDifrzgxxJFtkSnX7qJtDatabase.TemplatePG.TypesDatabase.TemplatePG.ProtocolDatabase.TemplatePG.SQLDatabase.TemplatePGPGType PGBoolean PGIntegerPGRealPGText PGTimestampTZPGDate PGInterval pgTypeFromOIDpgTypeToStringpgStringToType PGException pgConnect pgDisconnectdescribeStatementexecuteSimpleQueryexecuteSimpleStatement thConnection queryTuples queryTupleexecutewithTransactionrollback insertIgnorepgTimestampTZFormat escapeString readIntegralreadReal showIntegralshowReal ErrorResponse PGMessageCommandCompleteDataRowDescribeParameterDescriptionParseRowDescription SimpleQuerydebugpgStringputMessageBodygetMessageHeadergetMessageBodypgSend pgReceive pgWaitFor$fBinaryPGMessageAuthenticationBackendKeyDataEmptyQueryResponseExecuteFlushNoDataNoticeResponseParameterStatus ParseComplete ReadyForQueryUnknownMessageprotocolVersion pgMessageID$fExceptionPGException prepareSQLweave weaveStringbaseGHC.BaseNothingghc-prim GHC.TypesIOControl.Exception.Base onException convertRow convertColumnpgStringToType'False Data.MaybefromJustTrueMaybeparseSql sqlParameter maybeHeadbytes_6elQVSg5cWdFrvRnfxTUrHData.ByteString.Lazy.Internal ByteStringevery2nd sqlStatementsqlText