Zh      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgportable experimentalalistair@abayley.orghi;Analogous to peekCString. Converts UTF8 CString to String. >Analogous to peekCStringLen. Converts UTF8 CString to String. + The resulting String will end either when len bytes / have been converted, or when a NULL is found. 7Analogous to newCString. Creates UTF8 encoded CString. jAnalogous to newCStringLen. > The length returned is in bytes (encoding units), not chars. 8Analogous to withCString. Creates UTF8 encoded CString. Analogous to withCStringLen. > The length returned is in bytes (encoding units), not chars. <Convert a String that was marshalled from a CString without B any decoder applied. This might be useful if the client encoding - is unknown, and the user code must convert. > We assume that the UTF8 CString was marshalled as if Latin-1 ( i.e. all chars are in the range 0-255. kBConvert a Haskell String into a UTF8 String, where each UTF8 byte G is represented by its Char equivalent i.e. only chars 0-255 are used. F The resulting String can be marshalled to CString directly i.e. with  a Latin-1 encoding. l%Convert Unicode characters to UTF-8. Convert UTF-8 to Unicode. mCConvert UTF-8 to Unicode, from a null-terminated C array of bytes. ) This function is useful, in addition to   above,  because it doesn't create an intermediate [Word8] list. n$The bytes parameter should be len-1 A i.e. if the CString has length 2, then you should pass bytes=1.  That'6s because we add bytes to the Ptr p to get the offset 9 for each byte; byte 1 is at p+0, byte 2 is at p+1, etc. o      non-portable experimental$oleg@pobox.com, alistair@abayley.org Like System.IO.print1, except that Strings are not escaped or quoted. LConvenience for making UTCTimes. Assumes the time given is already UTC time  i.e. there's no timezone adjustment. >Assumes CalendarTime is also UTC i.e. ignores ctTZ component.  pqrstu   Dnon-portable (uses ExistentialQuantification and DeriveDataTypeable) experimentallibraries@haskell.orgLvwxyz{|}~ !"#$%& !"#$%& !"#$%& !"!"#$%& non-portable experimental$oleg@pobox.com, alistair@abayley.org3'.The class DBBind is not used by the end-user. D It is used to tie up low-level database access and the enumerator. I A database-specific library must provide a set of instances for DBBind. $ The latter are the dual of DBType. (:This is really just a wrapper that lets us write lists of  heterogenous bind values e.g. [bindP "string", bindP (0::Int), ...] )<The binding object (bo) below is very abstract, on purpose. @ It may be |IO a|, it may be String, it may be a function, etc. 8 The binding object can hold the result of marshalling, * or bo can hold the current counter, etc. 1 Different databases do things very differently: 8 compare PostgreSQL and the Stub (which models Oracle). *+LThis type is not visible to the end user (cf. ConnectA). It forms a private  `communication channel'- between Database.Enumerator and a back end. Why don'&t we make a user-visible class with a prepare method? B Because it means to standardize the preparation method signature A across all databases. Some databases need more parameters, some H fewer. There may be several statement preparation functions within one > database. So, instead of standardizing the signature of the > preparation function, we standardize on the _result_ of that G function. To be more precise, we standardize on the properties of the C result: whatever it is, the eventual prepared statement should be  suitable to be passed to . ,-A 'buffer': means a column buffer: a data structure that points to a < block of memory allocated for the values of one particular E column. Since a query normally fetches a row of several columns, we H typically deal with a list of column buffers. Although the column data H are typed (e.g., Integer, CalendarDate, etc), column buffers hide that F type. Think of the column buffer as Dynamics. The class DBType below D describes marshalling functions, to fetch a typed value out of the  'untyped' columnBuffer. Different DBMS'0s (that is, different session objects) have, in F general, columnBuffers of different types: the type of Column Buffer  is specific to a database. < So, ISession (m) uniquely determines the buffer type (b)?? 7 Or, actually, a query uniquely determines the buffer. .The class DBType is not used by the end-user. D It is used to tie up low-level database access and the enumerator. I A database-specific library must provide a set of instances for DBType. .<The class IQuery describes the class of query objects. Each D database (that is, each Session object) has its own Query object. E We may assume that a Query object includes (at least, conceptually) C a (pointer to) a Session object, so a Query object determines the  Session object. ; A back-end provides an instance (or instances) of IQuery. D The end user never seens the IQuery class (let alone its methods). 3Can a session have several types of query objects?  Let'@s assume that it can: but a statement plus the session uniquely  determine the query, HNote that we explicitly use IO monad because we will have to explicitly  do FFI. /00( defines the API for query objects i.e.  which types can be queries. 1229 is not a query: command deletes or updates rows, creates/drops $ tables, or changes database state.  A returns the number of affected rows (or 0 if DDL i.e. not DML). 3The 34 class describes a database session to a particular > DBMS. Oracle has its own Session object, SQLite has its own G session object (which maintains the connection handle to the database J engine and other related stuff). Session objects for different databases L normally have different types -- yet they all belong to the class ISession & so we can do generic operations like commit, execDDL, etc. # in a database-independent manner. 9Session objects per se are created by database connection/login functions.  The class 3- is thus an interface between low-level (and B database-specific) code and the Enumerator, database-independent  code.  The 3< class is NOT visible to the end user -- neither the class,  nor any of its methods. The 37 class describes the mapping from connection object to F the session object. The connection object is created by the end user J (and this is how the end user tells which particular back end he wants). B The session object is not accessible by the end user in any way. 7 Even the type of the session object should be hidden! 45>Thrown by cursor functions if you try to fetch after the end. 6Ithe iteratee function used for queries accepts both nullable (Maybe) and L non-nullable types. If the query itself returns a null in a column where a ( non-nullable type was specified, we can',t handle it, so DBUnexpectedNull is thrown. 78DBMS error message. 9:;<=>?FA wrapper around the action to open the database. That wrapper is not A exported to the end user. The only reason for the wrapper is to 8 guarantee that the only thing to do with the result of  "Database.Enumerator.Sqlite.connect function is to pass it out  directly to Database.Enumerator.withSession. @Afor alternative spellers BCDEFThrow a DBException. It's just a type-specific Control.Exception.throwDyn. 2Used by instances of DBType to throw an exception $ when a null (Nothing) is returned. J Will work for any type, as you pass the fetch action in the fetcher arg. 3'()*+,-./0123456789:;<=>?@ABCDEF3'(()*+,-.//01234876556789:;<=>?@EDCBAABCDEF non-portable experimental$oleg@pobox.com, alistair@abayley.org/$A DBCursor is an IORef-mutable-pair  (a, Maybe f), where a is the result-set so far,  and fO is an IO action that fetches and returns the next row (when applied to True), / or closes the cursor (when applied to False).  If Maybe f is Nothing), then the result-set has been exhausted . (or the iteratee function terminated early), ) and the cursor has already been closed. AThe class QueryIteratee is not for the end user. It provides the B interface between the low- and the middle-layers of Takusen. The ; middle-layer - enumerator - is database-independent then. GHIJKLMM and L give us some type sugar. 6 Without them, the types of iteratee functions become  quite unwieldy. NCatch &Database.InteralEnumerator.DBExceptions thrown in the K  monad. O)This simple handler reports the error to stdout and swallows it  i.e. it doesn' t propagate. P1This handler reports the error and propagates it ) (usually to force the program to halt). QASame as reportRethrow, but you can prefix some text to the error = (perhaps to indicate which part of your program raised it). R A show for &Database.InteralEnumerator.DBExceptions. S7If you want to trap a specific error number, use this.  It passes anything else up. T Analogous to S&, but ignores specific errors instead  (propagates anything else). UCTypeable constraint is to prevent the leakage of Session and other  marked objects. V"Persistent database connections. G This issue has been brought up by Shanky Surana. The following design  is inspired by that exchange. POn one hand, implementing persistent connections is easy. One may say we should A have added them long time ago, to match HSQL, HDBC, and similar ? database interfaces. Alas, implementing persistent connection E safely is another matter. The simplest design is like the following  < withContinuedSession :: (Typeable a, IE.ISession sess) => < IE.ConnectA sess -> (forall mark. DBM mark sess a) ->  IO (a, IE.ConnectA sess) 4 withContinuedSession (IE.ConnectA connecta) m = do  conn <- connecta $ r <- runReaderT (unDBM m) conn  return (r,(return conn)) Cso that the connection object is returned as the result and can be E used again with withContinuedSession or withSession. The problem is ( that nothing prevents us from writing:  > (r1,conn) <- withContinuedSession (connect "...") query1 * r2 <- withSession conn query2 * r3 <- withSession conn query3 BThat is, we store the suspended connection and then use it twice. A But the first withSession closes the connection. So, the second E withSession gets an invalid session object. Invalid in a sense that E even memory may be deallocated, so there is no telling what happens A next. Also, as we can see, it is difficult to handle errors and @ automatically dispose of the connections if the fatal error is  encountered. >All these problems are present in other interfaces... In the C case of a suspended connection, the problem is how to enforce the  linear1 access to a variable. It can be enforced, via a 6 state-changing monad. The implementation below makes C the non-linear use of a suspended connection a run-time checkable A condition. It will be generic and safe - fatal errors close the H connection, an attempt to use a closed connection raises an error, and 1 we cannot reuse a connection. We have to write:  3 (r1, conn1) <- withContinuedSession conn ... 3 (r2, conn2) <- withContinuedSession conn1 ... 3 (r3, conn3) <- withContinuedSession conn2 ... Detc. If we reuse a suspended connection or use a closed connection, ; we get a run-time (exception). That is of course not very ; satisfactory - and yet better than a segmentation fault. WXYZDDL operations don'0t manipulate data, so we return no information. 5 If there is a problem, an exception will be raised. [%Returns the number of rows affected. \2Allows arbitrary actions to be run the DBM monad. = The back-end developer must supply instances of EnvInquiry,  which is hidden away in Database.InternalEnumerator.  An example of this is *Database.Sqlite.Enumerator.LastInsertRowid. ]2Prepare a statement and run a DBM action over it. 2 This gives us the ability to re-use a statement, B for example by passing different bind values for each execution. DThe Typeable constraint is to prevent the leakage of marked things. H The type of bound statements should not be exported (and should not be ( in Typeable) so the bound statement can't leak either. 1preparation action to create prepared statement; % this action is usually created by  prepareQuery/Command +DBM action that takes a prepared statement ^IApplies a prepared statement to bind variables to get a bound statement, ) which is passed to the provided action. H Note that by the time it is passed to the action, the query or command  has usually been executed. 4 A bound statement would normally be an instance of  %Database.InternalEnumerator.Statement, so it can be passed to  _ = in order to process the result-set, and also an instance of  #Database.InternalEnumerator.Command, so that we can write 7 re-usable DML statements (inserts, updates, deletes). DThe Typeable constraint is to prevent the leakage of marked things. H The type of bound statements should not be exported (and should not be ( in Typeable) so the bound statement can't leak either. 4prepared statement created by withPreparedStatement  bind values #action to run over bound statement _The left-fold interface. query iteratee function  seed value ` cursorIsEOF'<s return value tells you if there are any more rows or not.  If you call b when there are no more rows,  a DBNoData exception is thrown. 2 Cursors are automatically closed and freed when:  the iteratee returns Left a $ the query result-set is exhausted. To make life easier, we' ve created a c function, 5 which will clean up if an error (exception) occurs,  or the code exits early. 7 You can nest them to get interleaving, if you desire: ) withCursor query1 iter1 [] $ \c1 -> do + withCursor query2 iter2 [] $ \c2 -> do  r1 <- cursorCurrent c1  r2 <- cursorCurrent c2  ...  return something aDReturns the results fetched so far, processed by iteratee function. bMAdvance the cursor. Returns the cursor. The return value is usually ignored. cDEnsures cursor resource is properly tidied up in exceptional cases. - Propagates exceptions after closing cursor. G The Typeable constraint is to prevent cursors and other marked values + (like cursor computations) from escaping. query iteratee function  seed value action taking cursor parameter d7Perform an action as a transaction: commit afterwards, 8 unless there was an exception, in which case rollback. e*Useful utility function, for SQL weenies. nullable value 4value to substitute if first parameter is null i.e. Data.Maybe.Nothing f!Another useful utility function. I Use this to return a value from an iteratee function (the one passed to  _). E Note that you should probably nearly always use the strict version. gTA strict version. This is recommended unless you have a specific need for laziness, 1 as the lazy version will gobble stack and heap. H If you have a large result-set (in the order of 10-100K rows or more), 4 it is likely to exhaust the standard 1M GHC stack.  Whether or not f eats memory depends on what x does:  if it'7s a delayed computation then it almost certainly will. - This includes consing elements onto a list, 5 and arithmetic operations (counting, summing, etc). KThis instance of the class implements the starting and continuation cases. 3This instance of the class is the terminating case 9 i.e. where the iteratee function has one argument left. 3 The argument is applied, and the result returned. A'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgAK3?UVXYWd@EDCBAZ[\48765ROPQNSTF<=9;:,]^021+*)'(./_-MLIJGH`abc>efg!GHHIJJKLMNOPQRSTUVWXYZ[\]^_`abcdefg      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNNOOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}{~{~{~{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{023a\]^FP Takusen-0.8.6Foreign.C.UTF8 Database.UtilControl.Exception.MonadIODatabase.EnumeratorControl.Exception.ExtensibleDatabase.InternalEnumeratorpeekUTF8StringpeekUTF8StringLen newUTF8StringwithUTF8StringwithUTF8StringLenfromUTF8String toUTF8String lengthUTF8toUTF8fromUTF8MyShowshow_print_ mkUTCTime mkCalTimeint64ToDatePartsdatePartsToInt64calTimeToInt64int64ToCalTimeint64ToUTCTimewordsBy skipNonMatch positionspgDatetimetoUTCTimepgDatetimetoCalTimepgDatetimeToPartsutcTimeToIsoStringutcTimeToPGDatetimeutcTimeToIsoDatetimeutcTimeToOdbcDatetimecalTimeToPGDatetimeprintArrayContents CaughtMonadIOgcatch gcatchJustgtrygtryJustgbracketgfinallyDBBindbindPBindA IPrepared PreparationA PreparedStmtDBTypeIQuery currentRowNum Statement EnvInquiryCommandISession DBExceptionDBNoDataDBUnexpectedNullDBFatalDBErrorSqlStateSqlStateSubClass SqlStateClassColNumRowNumPositionConnectAIsolationLevel Serializable SerialisableRepeatableRead ReadCommittedReadUncommittedthrowDB RefCursor NextResultSetDBMIterAct IterResultcatchDBbasicDBExceptionReporter reportRethrowreportRethrowMsgformatDBException catchDBError ignoreDBError withSessionwithContinuedSessionbeginTransactioncommitrollbackexecDDLexecDMLinquirewithPreparedStatementwithBoundStatementdoQuery cursorIsEOF cursorCurrent cursorNext withCursorwithTransactionifNullresultresult' nullCCharnullBytenewUTF8StringLen charToWord8 word8ToChar fromUTF8Ptr0 fromUTF8Ptr readUTF8CharutcTimeToInt64zeroPadsubstrscanWordisoDatetimeToUTCTime myIsInfixOfbaseGHC.BaseassertControl.ExceptioncatchesHandlerControl.Exception.BasebracketOnErrorbracket_finallybracket onExceptiontryJusttry mapException handleJusthandle catchJustcatchPatternMatchFail RecSelError RecConError RecUpdError NoMethodErrorNonTerminationNestedAtomically GHC.Conc.SyncthrowToGHC.IO.ExceptionioErrorBlockedIndefinitelyOnMVarBlockedIndefinitelyOnSTMDeadlockAssertionFailed StackOverflow HeapOverflow ThreadKilled UserInterruptAsyncExceptionIndexOutOfBoundsUndefinedElementArrayExceptionGHC.IOevaluateuninterruptibleMaskuninterruptibleMask_maskmask_getMaskingStateunblockblockthrowIOUnmaskedMaskedInterruptibleMaskedUninterruptible MaskingState IOException GHC.Exceptionthrow SomeException fromException toException Exception ErrorCallOverflow UnderflowLossOfPrecision DivideByZeroDenormalArithExceptionbindRun destroyStmtallocBufferForfetchCol fetchOneRow freeBuffer destroyQuery makeQueryexecuteCommand disconnect throwIfDBNullDBCursor CFoldLeftSelfCollEnumerator QueryIteratee iterApply allocBuffersexecutePreparation$fQueryIterateemq(->)seedb$fQueryIterateemq(->)seedb0