diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changelog
+
+## 0.1.0
+
+* Initial version.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2019, Elliot Cameron
+Copyright (c) 2022, ruby0b
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Taylor Hedberg nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,35 @@
+# `sqlite-simple-interpolate`
+
+Write natural SQL statements in Haskell using QuasiQuoters!
+
+```haskell
+{-# LANGUAGE QuasiQuotes #-}
+module Main where
+
+import Data.Char (toLower)
+import qualified Database.SQLite.Simple as SQL
+import Database.SQLite.Simple.QQ.Interpolated
+import Control.Exception (bracket)
+
+(&) = flip ($)
+infixl 1 &
+
+main :: IO ()
+main = bracket (SQL.open ":memory:") SQL.close $ \conn -> do
+  conn & [iexecute|CREATE TABLE people (name TEXT, age INTEGER)|]
+  conn & [iexecute|INSERT INTO people VALUES ("clive", 40)|]
+  -- you can always use 'isql' directly but you'll have to use uncurry:
+  (uncurry $ SQL.execute conn) [isql|INSERT INTO people VALUES ("clive", 32)|]
+
+  ageSum <- conn & [ifold|SELECT age FROM people|] 0 (\acc (SQL.Only x) -> pure (acc + x))
+  print ageSum
+
+  let limit = 1
+  ages <- conn & [iquery|SELECT age FROM people WHERE name = ${map toLower "CLIVE"} LIMIT ${limit}|]
+  print (ages :: [SQL.Only Int])
+```
+
+## Acknowledgements
+This library is a fork of [`postgresql-simple-interpolate`](https://github.com/3noch/postgresql-simple-interpolate), adapted for use with `sqlite-simple`.
+
+The original itself is basically just a copy of the [`here` package](https://github.com/tmhedberg/here) by Taylor M. Hedberg with slight modifications!
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/sqlite-simple-interpolate.cabal b/sqlite-simple-interpolate.cabal
new file mode 100644
--- /dev/null
+++ b/sqlite-simple-interpolate.cabal
@@ -0,0 +1,41 @@
+cabal-version:      3.0
+name:               sqlite-simple-interpolate
+version:            0.1
+synopsis:           Interpolated SQLite queries via quasiquotation
+description:
+  This package provides Quasiquoters for writing SQLite queries with inline interpolation of values.
+  The values are interpolated using toField from sqlite-simple.
+  See the README for more details.
+
+license:            BSD-3-Clause
+license-file:       LICENSE
+author:             ruby0b
+maintainer:         ruby0b
+copyright:          ©2022 ruby0b ©2019 Elliot Cameron
+homepage:           https://github.com/ruby0b/sqlite-simple-interpolate
+category:           Database
+tested-with:        GHC ==9.0.2
+extra-source-files:
+  CHANGELOG.md
+  README.md
+
+extra-doc-files:    README.md
+
+source-repository head
+  type:     git
+  location: git://github.com/ruby0b/sqlite-simple-interpolate.git
+
+library
+  hs-source-dirs:   src
+  exposed-modules:  Database.SQLite.Simple.QQ.Interpolated
+  other-modules:    Database.SQLite.Simple.QQ.Interpolated.Parser
+  build-depends:
+    , base              >=4.5  && <5
+    , haskell-src-meta  >=0.6  && <0.9
+    , mtl               >=2.1  && <2.3
+    , parsec            ^>=3.1
+    , sqlite-simple     >=0.1
+    , template-haskell  >=2.17 && <2.19
+
+  ghc-options:      -Wall
+  default-language: Haskell2010
diff --git a/src/Database/SQLite/Simple/QQ/Interpolated.hs b/src/Database/SQLite/Simple/QQ/Interpolated.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/SQLite/Simple/QQ/Interpolated.hs
@@ -0,0 +1,93 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+-- | Interpolated SQL queries
+module Database.SQLite.Simple.QQ.Interpolated
+  ( isql
+  , quoteInterpolatedSql
+  , iquery
+  , iexecute
+  , ifold
+  ) where
+
+import Language.Haskell.TH (Exp, Q, appE, listE, sigE, tupE, varE)
+import Language.Haskell.TH.Quote (QuasiQuoter (..))
+import Database.SQLite.Simple.ToField (toField)
+import Database.SQLite.Simple.QQ (sql)
+import Text.Parsec (ParseError)
+import Database.SQLite.Simple
+
+import Database.SQLite.Simple.QQ.Interpolated.Parser (StringPart (..), parseInterpolated)
+
+-- | Quote a SQL statement with embedded antiquoted expressions.
+--
+-- The result of the quasiquoter is a tuple, containing the statement string and a list
+-- of parameters. For example:
+--
+-- @[isql|SELECT field FROM table WHERE name = ${map toLower "ELLIOT"} LIMIT ${10}|]@
+--
+-- produces
+--
+-- @("SELECT field FROM table WHERE name = ? LIMIT ?", [toField ((map toLower) "ELLIOT"), toField 10])@
+--
+-- How the parser works:
+--
+-- Any expression occurring between @${@ and @}@ will be replaced with a @?@
+-- and passed as a query parameter.
+--
+-- Characters preceded by a backslash are treated literally. This enables the
+-- inclusion of the literal substring @${@ within your quoted text by writing
+-- it as @\\${@. The literal sequence @\\${@ may be written as @\\\\${@.
+--
+-- Note: This quasiquoter is a wrapper around 'Database.SQLite.Simple.QQ.sql'.
+--
+-- This quasiquoter only works in expression contexts and will throw an error
+-- at compile time if used in any other context.
+isql :: QuasiQuoter
+isql = QuasiQuoter
+  { quoteExp = quoteInterpolatedSql
+  , quotePat = error "isql quasiquoter does not support usage in patterns"
+  , quoteType = error "isql quasiquoter does not support usage in types"
+  , quoteDec = error "isql quasiquoter does not support usage in declarations"
+  }
+
+combineParts :: [StringPart] -> (String, [Q Exp])
+combineParts = foldr step ("", [])
+  where
+    step subExpr (s, exprs) = case subExpr of
+      Lit str -> (str <> s, exprs)
+      Esc c -> (c : s, exprs)
+      Anti e -> ('?' : s, e : exprs)
+
+applySql :: [StringPart] -> Q Exp
+applySql parts =
+  let
+    (s', exps) = combineParts parts
+  in
+  tupE [quoteExp sql s', sigE (listE $ map (appE (varE 'toField)) exps) [t| [SQLData] |]]
+
+-- | The internal parser used by 'isql'.
+quoteInterpolatedSql :: String -> Q Exp
+quoteInterpolatedSql s = either (handleError s) applySql (parseInterpolated s)
+
+handleError :: String -> ParseError -> Q Exp
+handleError expStr parseError = error $ mconcat
+  [ "Failed to parse interpolated expression in string: "
+  , expStr
+  , "\n"
+  , show parseError
+  ]
+
+-- | Invokes 'query' with arguments provided by 'isql'.
+-- The result is of type '(Connection -> IO [r])'.
+iquery :: QuasiQuoter
+iquery = isql { quoteExp = appE [| \(q, qs) c -> query c q qs |] . quoteInterpolatedSql }
+
+-- | Invokes 'execute' with arguments provided by 'isql'
+-- The result is of type '(Connection -> IO ())'.
+iexecute :: QuasiQuoter
+iexecute = isql { quoteExp = appE [| \(q, qs) c -> execute c q qs |] . quoteInterpolatedSql }
+
+-- | Invokes 'fold' with arguments provided by 'isql'.
+-- The result is of type 'a -> (a -> row -> IO a) -> Connection -> IO a'.
+ifold :: QuasiQuoter
+ifold = isql { quoteExp = appE [| \(q, qs) acc f c -> fold c q qs acc f |] . quoteInterpolatedSql }
diff --git a/src/Database/SQLite/Simple/QQ/Interpolated/Parser.hs b/src/Database/SQLite/Simple/QQ/Interpolated/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/SQLite/Simple/QQ/Interpolated/Parser.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+-- | Parsers for antiquoted Haskell expressions inside strings.
+--
+-- This module was largely copied from
+-- https://github.com/tmhedberg/here/blob/8a616b358bcc16bd215a78a8f6192ad9df8224b6/src/Data/String/Here/Interpolated.hs
+module Database.SQLite.Simple.QQ.Interpolated.Parser where
+
+import Data.Char (isDigit, isLetter)
+import Data.Functor (($>))
+import Control.Monad (unless)
+import Control.Monad.State (evalStateT, get, modify)
+import Control.Monad.Trans (lift)
+import Language.Haskell.Meta (parseExp)
+import Language.Haskell.TH (Exp, Q)
+import Text.Parsec ( ParseError, anyChar, between, char, eof, incSourceColumn
+                   , getInput, lookAhead, manyTill, noneOf, parse, setInput, statePos
+                   , string, try, updateParserState, (<|>)
+                   )
+import Text.Parsec.String (Parser)
+
+data StringPart = Lit String | Esc Char | Anti (Q Exp)
+
+data HsChompState = HsChompState { quoteState :: QuoteState
+                                 , braceCt :: Int
+                                 , consumed :: String
+                                 , prevCharWasIdentChar :: Bool
+                                 }
+
+data QuoteState = None | Single EscapeState | Double EscapeState deriving (Eq, Ord, Show)
+
+data EscapeState = Escaped | Unescaped deriving (Bounded, Enum, Eq, Ord, Show)
+
+parseInterpolated :: String -> Either ParseError [StringPart]
+parseInterpolated = parse pInterp ""
+
+pInterp :: Parser [StringPart]
+pInterp = manyTill pStringPart eof
+
+pStringPart :: Parser StringPart
+pStringPart = pAnti <|> pEsc <|> pLit
+
+pAnti :: Parser StringPart
+pAnti = Anti <$> between (try pAntiOpen) pAntiClose pAntiExpr
+
+pAntiOpen :: Parser String
+pAntiOpen = string "${"
+
+pAntiClose :: Parser String
+pAntiClose = string "}"
+
+pAntiExpr :: Parser (Q Exp)
+pAntiExpr = pUntilUnbalancedCloseBrace >>= either fail (pure . pure) . parseExp
+
+pUntilUnbalancedCloseBrace :: Parser String
+pUntilUnbalancedCloseBrace = evalStateT go $ HsChompState None 0 "" False
+  where
+    go = do
+      c <- lift anyChar
+      modify $ \st@HsChompState {consumed} -> st {consumed = c:consumed}
+      HsChompState{..} <- get
+      let next = setIdentifierCharState c *> go
+      case quoteState of
+        None -> case c of
+          '{' -> incBraceCt 1 *> next
+          '}' | braceCt > 0 -> incBraceCt (-1) *> next
+              | otherwise -> stepBack $> reverse (tail consumed)
+          '\'' -> unless prevCharWasIdentChar (setQuoteState $ Single Unescaped)
+               *> next
+          '"' -> setQuoteState (Double Unescaped) *> next
+          _ -> next
+        Single Unescaped -> do case c of '\\' -> setQuoteState (Single Escaped)
+                                         '\'' -> setQuoteState None
+                                         _ -> pure ()
+                               next
+        Single Escaped -> setQuoteState (Single Unescaped) *> next
+        Double Unescaped -> do case c of '\\' -> setQuoteState (Double Escaped)
+                                         '"' -> setQuoteState None
+                                         _ -> pure ()
+                               next
+        Double Escaped -> setQuoteState (Double Unescaped) *> next
+    stepBack = lift $
+      updateParserState
+        (\s -> s {statePos = incSourceColumn (statePos s) (-1)})
+        *> getInput
+        >>= setInput . ('}':)
+    incBraceCt n = modify $ \st@HsChompState {braceCt} ->
+      st {braceCt = braceCt + n}
+    setQuoteState qs = modify $ \st -> st {quoteState = qs}
+    setIdentifierCharState c = modify $ \st ->
+      st
+        {prevCharWasIdentChar = or [isLetter c, isDigit c, c == '_', c == '\'']}
+
+pEsc :: Parser StringPart
+pEsc = Esc <$> (char '\\' *> anyChar)
+
+pLit :: Parser StringPart
+pLit = fmap Lit $
+  try (litCharTil $ try $ lookAhead pAntiOpen <|> lookAhead (string "\\"))
+    <|> litCharTil eof
+  where litCharTil = manyTill $ noneOf ['\\']
