beam-duckdb-0.3.1.0: src/Database/Beam/DuckDB/Syntax.hs
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wno-unused-top-binds #-}
module Database.Beam.DuckDB.Syntax
( -- * Command
DuckDBCommandSyntax (..),
-- * Concrete syntaxes
DuckDBSelectSyntax (..),
DuckDBFromSyntax (..),
DuckDBExpressionSyntax (..),
DuckDBInsertSyntax (..),
DuckDBUpdateSyntax (..),
DuckDBDeleteSyntax (..),
DuckDBTableNameSyntax (..),
DuckDBOnConflictSyntax (..),
DuckDBFieldNameSyntax (..),
-- * DDL syntaxes
DuckDBDataTypeSyntax (..),
DuckDBCreateTableSyntax (..),
DuckDBDropTableSyntax (..),
DuckDBAlterTableSyntax (..),
DuckDBAlterTableActionSyntax (..),
DuckDBAlterColumnActionSyntax (..),
DuckDBCreateSchemaSyntax (..),
DuckDBDropSchemaSyntax (..),
DuckDBSchemaNameSyntax (..),
DuckDBColumnSchemaSyntax (..),
DuckDBColumnConstraintDefinitionSyntax (..),
DuckDBColumnConstraintSyntax (..),
DuckDBTableConstraintSyntax (..),
DuckDBMatchTypeSyntax (..),
DuckDBReferentialActionSyntax (..),
DuckDBTableOptionsSyntax (..),
DuckDBIndexOptions (..),
)
where
import Data.Aeson (object, withObject, (.:), (.=))
import Data.Bifunctor (second)
import Data.Coerce (coerce)
import Data.Hashable (Hashable (..))
import Data.Int (Int16, Int32, Int64, Int8)
import qualified Data.List.NonEmpty as NE
import Data.Scientific (Scientific)
import Data.String (fromString)
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Lazy as Lazy
import Data.Time (Day, LocalTime, TimeOfDay, UTCTime)
import Data.Word (Word16, Word32, Word64, Word8)
import Database.Beam.Backend
( HasSqlValueSyntax (..),
IsSql2003EnhancedNumericFunctionsAggregationExpressionSyntax (..),
IsSql2003EnhancedNumericFunctionsExpressionSyntax (..),
IsSql2003ExpressionAdvancedOLAPOperationsSyntax (..),
IsSql2003ExpressionElementaryOLAPOperationsSyntax (..),
IsSql2003ExpressionSyntax (..),
IsSql2003FirstValueAndLastValueExpressionSyntax (..),
IsSql2003LeadAndLagExpressionSyntax (..),
IsSql2003NthValueExpressionSyntax (..),
IsSql2003NtileExpressionSyntax (..),
IsSql2003OrderingElementaryOLAPOperationsSyntax (..),
IsSql2003WindowFrameBoundSyntax (..),
IsSql2003WindowFrameBoundsSyntax (..),
IsSql2003WindowFrameSyntax (..),
IsSql92AggregationExpressionSyntax (..),
IsSql92AggregationSetQuantifierSyntax (..),
IsSql92DataTypeSyntax (..),
IsSql92DeleteSyntax (..),
IsSql92ExpressionSyntax (..),
IsSql92ExtractFieldSyntax (..),
IsSql92FieldNameSyntax (..),
IsSql92FromOuterJoinSyntax (..),
IsSql92FromSyntax (..),
IsSql92GroupingSyntax (..),
IsSql92InsertSyntax (..),
IsSql92InsertValuesSyntax (..),
IsSql92OrderingSyntax (..),
IsSql92ProjectionSyntax (..),
IsSql92QuantifierSyntax (..),
IsSql92SchemaNameSyntax (..),
IsSql92SelectSyntax (..),
IsSql92SelectTableSyntax (..),
IsSql92Syntax (..),
IsSql92TableNameSyntax (..),
IsSql92TableSourceSyntax (..),
IsSql92UpdateSyntax (..),
IsSql99AggregationExpressionSyntax (..),
IsSql99CommonTableExpressionSelectSyntax (..),
IsSql99CommonTableExpressionSyntax (..),
IsSql99ConcatExpressionSyntax (..),
IsSql99DataTypeSyntax (..),
IsSql99ExpressionSyntax (..),
IsSql99FunctionExpressionSyntax (..),
IsSql99RecursiveCommonTableExpressionSelectSyntax (..),
SqlNull (..),
)
import Database.Beam.Backend.SQL
( IsSql2003BinaryAndVarBinaryDataTypeSyntax (..),
IsSql2008BigIntDataTypeSyntax (..),
Sql92DisplaySyntax (..),
)
import Database.Beam.DuckDB.Syntax.Builder (DuckDBSyntax, commas, duckDBRenderSyntaxScript, emit, emitChar, emitIntegral, emitRealFloat, emitScientific, emitValue, parens, quotedIdentifier, sepBy, spaces)
import Database.Beam.Migrate.Checks (HasDataTypeCreatedCheck (..))
import Database.Beam.Migrate.SQL
( IsSql92AlterColumnActionSyntax (..),
IsSql92AlterTableActionSyntax (..),
IsSql92AlterTableSyntax (..),
IsSql92ColumnConstraintDefinitionSyntax (..),
IsSql92ColumnConstraintSyntax (..),
IsSql92ColumnSchemaSyntax (..),
IsSql92CreateDropIndexSyntax (..),
IsSql92CreateSchemaSyntax (..),
IsSql92CreateTableSyntax (..),
IsSql92DdlCommandSyntax (..),
IsSql92DdlSchemaCommandSyntax (..),
IsSql92DropSchemaSyntax (..),
IsSql92DropTableSyntax (..),
IsSql92MatchTypeSyntax (..),
IsSql92ReferentialActionSyntax (..),
IsSql92TableConstraintSyntax (..),
IsSql92UniqueIndexSyntax (..),
Sql92SerializableConstraintDefinitionSyntax (..),
Sql92SerializableDataTypeSyntax (..),
)
import Database.Beam.Migrate.SQL.Builder (ConstraintAttributeTiming (..), SqlConstraintAttributesBuilder (..), sqlConstraintAttributesSerialized)
import Database.Beam.Migrate.SQL.SQL92 (ForeignKeyAction (..))
import Database.Beam.Migrate.Serialization
( BeamSerializedConstraint,
BeamSerializedConstraintDefinition,
BeamSerializedDataType (..),
BeamSerializedExpression (..),
BeamSerializedMatchType,
BeamSerializedReferentialAction,
fromBeamSerializedConstraintDefinition,
fromBeamSerializedDataType,
)
import Database.DuckDB.Simple (Null (Null))
newtype DuckDBCommandSyntax = DuckDBCommandSyntax {fromDuckDBSyntax :: DuckDBSyntax}
--
instance IsSql92Syntax DuckDBCommandSyntax where
type Sql92SelectSyntax DuckDBCommandSyntax = DuckDBSelectSyntax
type Sql92InsertSyntax DuckDBCommandSyntax = DuckDBInsertSyntax
type Sql92UpdateSyntax DuckDBCommandSyntax = DuckDBUpdateSyntax
type Sql92DeleteSyntax DuckDBCommandSyntax = DuckDBDeleteSyntax
selectCmd = DuckDBCommandSyntax . fromDuckDBSelect
insertCmd = DuckDBCommandSyntax . fromDuckDBInsert
updateCmd = DuckDBCommandSyntax . fromDuckDBUpdate
deleteCmd = DuckDBCommandSyntax . fromDuckDBDelete
instance IsSql92DdlCommandSyntax DuckDBCommandSyntax where
type Sql92DdlCommandCreateTableSyntax DuckDBCommandSyntax = DuckDBCreateTableSyntax
type Sql92DdlCommandDropTableSyntax DuckDBCommandSyntax = DuckDBDropTableSyntax
type Sql92DdlCommandAlterTableSyntax DuckDBCommandSyntax = DuckDBAlterTableSyntax
createTableCmd = DuckDBCommandSyntax . fromDuckDBCreateTable
dropTableCmd = DuckDBCommandSyntax . fromDuckDBDropTable
alterTableCmd = DuckDBCommandSyntax . fromDuckDBAlterTable
instance IsSql92DdlSchemaCommandSyntax DuckDBCommandSyntax where
type Sql92DdlCommandCreateSchemaSyntax DuckDBCommandSyntax = DuckDBCreateSchemaSyntax
type Sql92DdlCommandDropSchemaSyntax DuckDBCommandSyntax = DuckDBDropSchemaSyntax
createSchemaCmd = DuckDBCommandSyntax . fromDuckDBCreateSchema
dropSchemaCmd = DuckDBCommandSyntax . fromDuckDBDropSchema
newtype DuckDBTableNameSyntax = DuckDBTableNameSyntax {fromDuckDBTableName :: DuckDBSyntax}
-- | DuckDB @SELECT@ syntax
newtype DuckDBSelectSyntax = DuckDBSelectSyntax {fromDuckDBSelect :: DuckDBSyntax}
newtype DuckDBSelectTableSyntax = DuckDBSelectTableSyntax {fromDuckDBSelectTable :: DuckDBSyntax}
newtype DuckDBOrderingSyntax = DuckDBOrderingSyntax {fromDuckDBOrdering :: DuckDBSyntax}
newtype DuckDBExpressionSyntax = DuckDBExpressionSyntax {fromDuckDBExpression :: DuckDBSyntax} deriving (Eq)
newtype DuckDBTableSourceSyntax = DuckDBTableSourceSyntax {fromDuckDBTableSource :: DuckDBSyntax}
newtype DuckDBProjectionSyntax = DuckDBProjectionSyntax {fromDuckDBProjection :: DuckDBSyntax}
newtype DuckDBFromSyntax = DuckDBFromSyntax {fromDuckDBFrom :: DuckDBSyntax}
newtype DuckDBValueSyntax = DuckDBValueSyntax {fromDuckDBValue :: DuckDBSyntax}
newtype DuckDBFieldNameSyntax = DuckDBFieldNameSyntax {fromDuckDBFieldName :: DuckDBSyntax}
newtype DuckDBComparisonQuantifierSyntax = DuckDBComparisonQuantifierSyntax {fromDuckDBComparisonQuantifier :: DuckDBSyntax}
data DuckDBDataTypeSyntax = DuckDBDataTypeSyntax
{ duckDBDataType :: DuckDBSyntax,
duckDBDataTypeSerialized :: BeamSerializedDataType
}
deriving (Show)
-- DDL syntax newtypes used by 'IsSql92DdlCommandSyntax' and friends.
newtype DuckDBCreateTableSyntax = DuckDBCreateTableSyntax {fromDuckDBCreateTable :: DuckDBSyntax}
data DuckDBTableOptionsSyntax = DuckDBTableOptionsSyntax DuckDBSyntax DuckDBSyntax
newtype DuckDBDropTableSyntax = DuckDBDropTableSyntax {fromDuckDBDropTable :: DuckDBSyntax}
newtype DuckDBAlterTableSyntax = DuckDBAlterTableSyntax {fromDuckDBAlterTable :: DuckDBSyntax}
newtype DuckDBAlterTableActionSyntax = DuckDBAlterTableActionSyntax {fromDuckDBAlterTableAction :: DuckDBSyntax}
newtype DuckDBAlterColumnActionSyntax = DuckDBAlterColumnActionSyntax {fromDuckDBAlterColumnAction :: DuckDBSyntax}
newtype DuckDBCreateSchemaSyntax = DuckDBCreateSchemaSyntax {fromDuckDBCreateSchema :: DuckDBSyntax}
newtype DuckDBDropSchemaSyntax = DuckDBDropSchemaSyntax {fromDuckDBDropSchema :: DuckDBSyntax}
newtype DuckDBSchemaNameSyntax = DuckDBSchemaNameSyntax {fromDuckDBSchemaName :: DuckDBSyntax}
newtype DuckDBColumnSchemaSyntax = DuckDBColumnSchemaSyntax {fromDuckDBColumnSchema :: DuckDBSyntax}
deriving (Show, Eq)
data DuckDBColumnConstraintDefinitionSyntax = DuckDBColumnConstraintDefinitionSyntax
{ fromDuckDBColumnConstraintDefinition :: DuckDBSyntax,
duckDBColumnConstraintDefinitionSerialized :: BeamSerializedConstraintDefinition
}
deriving (Show)
data DuckDBColumnConstraintSyntax = DuckDBColumnConstraintSyntax
{ fromDuckDBColumnConstraint :: DuckDBSyntax,
duckDBColumnConstraintSerialized :: BeamSerializedConstraint
}
newtype DuckDBTableConstraintSyntax = DuckDBTableConstraintSyntax {fromDuckDBTableConstraint :: DuckDBSyntax}
data DuckDBMatchTypeSyntax = DuckDBMatchTypeSyntax
{ fromDuckDBMatchType :: DuckDBSyntax,
duckDBMatchTypeSerialized :: BeamSerializedMatchType
}
data DuckDBReferentialActionSyntax = DuckDBReferentialActionSyntax
{ fromDuckDBReferentialAction :: DuckDBSyntax,
duckDBReferentialActionSerialized :: BeamSerializedReferentialAction
}
newtype DuckDBIndexOptions = DuckDBIndexOptions {duckDBIndexUnique :: Bool}
deriving stock (Show, Eq)
deriving newtype (Hashable)
newtype DuckDBExtractFieldSyntax = DuckDBExtractFieldSyntax {fromDuckDBExtractField :: DuckDBSyntax}
newtype DuckDBGroupingSyntax = DuckDBGroupingSyntax {fromDuckDBGrouping :: DuckDBSyntax}
newtype DuckDBSelectSetQuantifierSyntax = DuckDBSelectSetQuantifierSyntax {fromDuckDBSelectSetQuantifier :: DuckDBSyntax}
newtype DuckDBAggregationSetQuantifierSyntax = DuckDBAggregationSetQuantifierSyntax {fromDuckDBAggregationSetQuantifier :: DuckDBSyntax}
newtype DuckDBCommonTableExpressionSyntax = DuckDBCommonTableExpressionSyntax {fromDuckDBCommonTableExpressionSyntax :: DuckDBSyntax}
newtype DuckDBWindowFrameSyntax = DuckDBWindowFrameSyntax {fromDuckDBWindowFrame :: DuckDBSyntax}
newtype DuckDBWindowFrameBoundsSyntax = DuckDBWindowFrameBoundsSyntax {fromDuckDBWindowFrameBounds :: DuckDBSyntax}
newtype DuckDBWindowFrameBoundSyntax = DuckDBWindowFrameBoundSyntax {fromDuckDBWindowFrameBound :: Text -> DuckDBSyntax}
instance IsSql92AggregationExpressionSyntax DuckDBExpressionSyntax where
type Sql92AggregationSetQuantifierSyntax DuckDBExpressionSyntax = DuckDBAggregationSetQuantifierSyntax
countAllE = DuckDBExpressionSyntax (emit "COUNT(*)")
countE = aggFunc "COUNT"
avgE = aggFunc "AVG"
sumE = aggFunc "SUM"
minE = aggFunc "MIN"
maxE = aggFunc "MAX"
aggFunc :: Text -> Maybe DuckDBAggregationSetQuantifierSyntax -> DuckDBExpressionSyntax -> DuckDBExpressionSyntax
aggFunc fn q e =
DuckDBExpressionSyntax $
emit fn <> emit "(" <> maybe mempty (\qInner -> fromDuckDBAggregationSetQuantifier qInner <> emit " ") q <> fromDuckDBExpression e <> emit ")"
instance IsSql92SelectTableSyntax DuckDBSelectTableSyntax where
type Sql92SelectTableSelectSyntax DuckDBSelectTableSyntax = DuckDBSelectSyntax
type Sql92SelectTableExpressionSyntax DuckDBSelectTableSyntax = DuckDBExpressionSyntax
type Sql92SelectTableProjectionSyntax DuckDBSelectTableSyntax = DuckDBProjectionSyntax
type Sql92SelectTableFromSyntax DuckDBSelectTableSyntax = DuckDBFromSyntax
type Sql92SelectTableGroupingSyntax DuckDBSelectTableSyntax = DuckDBGroupingSyntax
type Sql92SelectTableSetQuantifierSyntax DuckDBSelectTableSyntax = DuckDBAggregationSetQuantifierSyntax
selectTableStmt setQuantifier proj from where_ grouping having =
DuckDBSelectTableSyntax $
mconcat
[ emit "SELECT ",
maybe mempty ((<> emit " ") . fromDuckDBAggregationSetQuantifier) setQuantifier,
fromDuckDBProjection proj,
maybe mempty ((emit " FROM " <>) . fromDuckDBFrom) from,
maybe mempty ((emit " WHERE " <>) . fromDuckDBExpression) where_,
maybe mempty ((emit " GROUP BY " <>) . fromDuckDBGrouping) grouping,
maybe mempty ((emit " HAVING " <>) . fromDuckDBExpression) having
]
unionTables unionAll = tableOp (if unionAll then "UNION ALL" else "UNION")
intersectTables intersectAcc = tableOp (if intersectAcc then "INTERSECT ALL" else "INTERSECT")
exceptTable exceptAll = tableOp (if exceptAll then "EXCEPT ALL" else "EXCEPT")
tableOp :: Text -> DuckDBSelectTableSyntax -> DuckDBSelectTableSyntax -> DuckDBSelectTableSyntax
tableOp op a b =
DuckDBSelectTableSyntax $
fromDuckDBSelectTable a <> spaces (emit op) <> fromDuckDBSelectTable b
instance IsSql92QuantifierSyntax DuckDBComparisonQuantifierSyntax where
quantifyOverAll = DuckDBComparisonQuantifierSyntax (emit "ALL")
quantifyOverAny = DuckDBComparisonQuantifierSyntax (emit "ANY")
instance IsSql92ExtractFieldSyntax DuckDBExtractFieldSyntax where
secondsField = DuckDBExtractFieldSyntax (emit "SECOND")
minutesField = DuckDBExtractFieldSyntax (emit "MINUTE")
hourField = DuckDBExtractFieldSyntax (emit "HOUR")
dayField = DuckDBExtractFieldSyntax (emit "DAY")
weekField = DuckDBExtractFieldSyntax (emit "WEEK")
monthField = DuckDBExtractFieldSyntax (emit "MONTH")
yearField = DuckDBExtractFieldSyntax (emit "YEAR")
instance IsSql92AggregationSetQuantifierSyntax DuckDBAggregationSetQuantifierSyntax where
setQuantifierDistinct = DuckDBAggregationSetQuantifierSyntax $ emit "DISTINCT"
setQuantifierAll = DuckDBAggregationSetQuantifierSyntax $ emit "ALL"
instance IsSql92AggregationSetQuantifierSyntax DuckDBSelectSetQuantifierSyntax where
setQuantifierDistinct = DuckDBSelectSetQuantifierSyntax $ emit "DISTINCT"
setQuantifierAll = DuckDBSelectSetQuantifierSyntax $ emit "ALL"
instance IsSql92GroupingSyntax DuckDBGroupingSyntax where
type Sql92GroupingExpressionSyntax DuckDBGroupingSyntax = DuckDBExpressionSyntax
groupByExpressions es =
DuckDBGroupingSyntax $
commas (map fromDuckDBExpression es)
instance IsSql92TableNameSyntax DuckDBTableNameSyntax where
tableName Nothing t = DuckDBTableNameSyntax (quotedIdentifier t)
tableName (Just s) t = DuckDBTableNameSyntax (quotedIdentifier s <> emit "." <> quotedIdentifier t)
instance IsSql92TableSourceSyntax DuckDBTableSourceSyntax where
type Sql92TableSourceSelectSyntax DuckDBTableSourceSyntax = DuckDBSelectSyntax
type Sql92TableSourceExpressionSyntax DuckDBTableSourceSyntax = DuckDBExpressionSyntax
type Sql92TableSourceTableNameSyntax DuckDBTableSourceSyntax = DuckDBTableNameSyntax
tableNamed = DuckDBTableSourceSyntax . fromDuckDBTableName
tableFromSubSelect s = DuckDBTableSourceSyntax $ emit "(" <> fromDuckDBSelect s <> emit ")"
tableFromValues vss =
DuckDBTableSourceSyntax . parens $
emit "VALUES "
<> commas
( map
(parens . commas . map fromDuckDBExpression)
vss
)
instance IsSql92FromSyntax DuckDBFromSyntax where
type Sql92FromExpressionSyntax DuckDBFromSyntax = DuckDBExpressionSyntax
type Sql92FromTableSourceSyntax DuckDBFromSyntax = DuckDBTableSourceSyntax
fromTable tableSrc Nothing = coerce tableSrc
fromTable tableSrc (Just (nm, colNms)) =
DuckDBFromSyntax $
coerce tableSrc
<> emit " AS "
<> quotedIdentifier nm
<> maybe mempty (parens . commas . map quotedIdentifier) colNms
innerJoin a b Nothing = DuckDBFromSyntax (fromDuckDBFrom a <> emit " CROSS JOIN " <> fromDuckDBFrom b)
innerJoin a b (Just e) = join "INNER JOIN" a b (Just e)
leftJoin = join "LEFT JOIN"
rightJoin = join "RIGHT JOIN"
join :: Text -> DuckDBFromSyntax -> DuckDBFromSyntax -> Maybe DuckDBExpressionSyntax -> DuckDBFromSyntax
join joinType a b Nothing =
DuckDBFromSyntax $
fromDuckDBFrom a <> emit (" " <> joinType <> " ") <> fromDuckDBFrom b <> emit " ON TRUE"
join joinType a b (Just on) =
DuckDBFromSyntax $
fromDuckDBFrom a
<> emit (" " <> joinType <> " ")
<> fromDuckDBFrom b
<> emit " ON "
<> fromDuckDBExpression on
instance IsSql92FromOuterJoinSyntax DuckDBFromSyntax where
outerJoin = join "FULL OUTER JOIN"
instance IsSql92FieldNameSyntax DuckDBFieldNameSyntax where
qualifiedField a b =
DuckDBFieldNameSyntax $
quotedIdentifier a <> emit "." <> quotedIdentifier b
unqualifiedField = DuckDBFieldNameSyntax . quotedIdentifier
instance IsSql92DataTypeSyntax DuckDBDataTypeSyntax where
domainType nm =
DuckDBDataTypeSyntax
(quotedIdentifier nm)
(domainType nm)
charType prec charSet =
DuckDBDataTypeSyntax
(emit "CHAR" <> optPrec prec <> optCharSet charSet)
(charType prec charSet)
varCharType prec charSet =
DuckDBDataTypeSyntax
(emit "VARCHAR" <> optPrec prec <> optCharSet charSet)
(varCharType prec charSet)
nationalCharType prec =
DuckDBDataTypeSyntax
(emit "NATIONAL CHAR" <> optPrec prec)
(nationalCharType prec)
nationalVarCharType prec =
DuckDBDataTypeSyntax
(emit "NATIONAL CHARACTER VARYING" <> optPrec prec)
(nationalVarCharType prec)
bitType prec =
DuckDBDataTypeSyntax
(emit "BIT" <> optPrec prec)
(bitType prec)
varBitType prec =
DuckDBDataTypeSyntax
(emit "BIT VARYING" <> optPrec prec)
(varBitType prec)
numericType prec =
DuckDBDataTypeSyntax
(emit "NUMERIC" <> optNumericPrec prec)
(numericType prec)
decimalType prec =
DuckDBDataTypeSyntax
(emit "DECIMAL" <> optNumericPrec prec)
(decimalType prec)
intType = DuckDBDataTypeSyntax (emit "INT") intType
smallIntType = DuckDBDataTypeSyntax (emit "SMALLINT") smallIntType
floatType prec =
DuckDBDataTypeSyntax
(emit "FLOAT" <> optPrec prec)
(floatType prec)
doubleType = DuckDBDataTypeSyntax (emit "DOUBLE PRECISION") doubleType
realType = DuckDBDataTypeSyntax (emit "REAL") realType
dateType = DuckDBDataTypeSyntax (emit "DATE") dateType
timeType prec withTz =
DuckDBDataTypeSyntax
(emit "TIME" <> optPrec prec <> if withTz then emit " WITH TIME ZONE" else mempty)
(timeType prec withTz)
timestampType prec withTz =
DuckDBDataTypeSyntax
(emit "TIMESTAMP" <> optPrec prec <> if withTz then emit " WITH TIME ZONE" else mempty)
(timestampType prec withTz)
instance IsSql99DataTypeSyntax DuckDBDataTypeSyntax where
characterLargeObjectType = DuckDBDataTypeSyntax {duckDBDataType = emit "TEXT", duckDBDataTypeSerialized = characterLargeObjectType}
binaryLargeObjectType = DuckDBDataTypeSyntax {duckDBDataType = emit "BYTEA", duckDBDataTypeSerialized = binaryLargeObjectType}
booleanType = DuckDBDataTypeSyntax (emit "BOOLEAN") booleanType
arrayType (DuckDBDataTypeSyntax syntax serialized) sz =
DuckDBDataTypeSyntax
(syntax <> emit "[" <> emit (fromString (show sz)) <> emit "]")
(arrayType serialized sz)
rowType fields =
DuckDBDataTypeSyntax
{ duckDBDataType =
-- Note that DuckDB's support for ROW is effectively STRUCT, as far as I understand
emit "STRUCT("
<> sepBy (emit ", ") (map (\(fieldName, DuckDBDataTypeSyntax t _) -> emit fieldName <> emit " " <> t) fields)
<> emitChar ')',
duckDBDataTypeSerialized = rowType (map (second duckDBDataTypeSerialized) fields)
}
instance IsSql2008BigIntDataTypeSyntax DuckDBDataTypeSyntax where
bigIntType = DuckDBDataTypeSyntax (emit "BIGINT") bigIntType
instance IsSql2003BinaryAndVarBinaryDataTypeSyntax DuckDBDataTypeSyntax where
binaryType sz =
DuckDBDataTypeSyntax
(emit "BLOB" <> optPrec sz)
(binaryType sz)
varBinaryType sz =
DuckDBDataTypeSyntax
(emit "BLOB" <> optPrec sz)
(varBinaryType sz)
-- | Two 'DuckDBDataTypeSyntax' values are equal when their rendered SQL is
-- equal. This is good enough for migration purposes, where the serialized
-- JSON form is used as the canonical comparison key by @beam-migrate@.
instance Eq DuckDBDataTypeSyntax where
DuckDBDataTypeSyntax a _ == DuckDBDataTypeSyntax b _ = a == b
instance Hashable DuckDBDataTypeSyntax where
hashWithSalt salt (DuckDBDataTypeSyntax a _) = hashWithSalt salt a
instance Sql92DisplaySyntax DuckDBDataTypeSyntax where
displaySyntax = displaySyntax . duckDBDataType
instance Sql92SerializableDataTypeSyntax DuckDBDataTypeSyntax where
serializeDataType = fromBeamSerializedDataType . duckDBDataTypeSerialized
-- | DuckDB doesn't allow user-defined domain types, so every data type is
-- always available.
instance HasDataTypeCreatedCheck DuckDBDataTypeSyntax where
dataTypeHasBeenCreated _ _ = True
instance Sql92DisplaySyntax DuckDBColumnSchemaSyntax where
displaySyntax = displaySyntax . fromDuckDBColumnSchema
instance Hashable DuckDBColumnSchemaSyntax where
hashWithSalt salt = hashWithSalt salt . fromDuckDBColumnSchema
instance Eq DuckDBColumnConstraintDefinitionSyntax where
DuckDBColumnConstraintDefinitionSyntax a _ == DuckDBColumnConstraintDefinitionSyntax b _ = a == b
instance Hashable DuckDBColumnConstraintDefinitionSyntax where
hashWithSalt salt = hashWithSalt salt . fromDuckDBColumnConstraintDefinition
instance Sql92DisplaySyntax DuckDBColumnConstraintDefinitionSyntax where
displaySyntax = displaySyntax . fromDuckDBColumnConstraintDefinition
instance Sql92SerializableConstraintDefinitionSyntax DuckDBColumnConstraintDefinitionSyntax where
serializeConstraint = fromBeamSerializedConstraintDefinition . duckDBColumnConstraintDefinitionSerialized
instance IsSql92SchemaNameSyntax DuckDBSchemaNameSyntax where
schemaName s = DuckDBSchemaNameSyntax (quotedIdentifier s)
instance IsSql92CreateSchemaSyntax DuckDBCreateSchemaSyntax where
type Sql92CreateSchemaSchemaNameSyntax DuckDBCreateSchemaSyntax = DuckDBSchemaNameSyntax
createSchemaSyntax sNm =
DuckDBCreateSchemaSyntax (emit "CREATE SCHEMA " <> fromDuckDBSchemaName sNm)
instance IsSql92DropSchemaSyntax DuckDBDropSchemaSyntax where
type Sql92DropSchemaSchemaNameSyntax DuckDBDropSchemaSyntax = DuckDBSchemaNameSyntax
dropSchemaSyntax sNm =
DuckDBDropSchemaSyntax (emit "DROP SCHEMA " <> fromDuckDBSchemaName sNm)
instance IsSql92DropTableSyntax DuckDBDropTableSyntax where
type Sql92DropTableTableNameSyntax DuckDBDropTableSyntax = DuckDBTableNameSyntax
dropTableSyntax tblNm =
DuckDBDropTableSyntax (emit "DROP TABLE " <> fromDuckDBTableName tblNm)
instance IsSql92AlterTableSyntax DuckDBAlterTableSyntax where
type Sql92AlterTableAlterTableActionSyntax DuckDBAlterTableSyntax = DuckDBAlterTableActionSyntax
type Sql92AlterTableTableNameSyntax DuckDBAlterTableSyntax = DuckDBTableNameSyntax
alterTableSyntax tblNm action =
DuckDBAlterTableSyntax $
emit "ALTER TABLE "
<> fromDuckDBTableName tblNm
<> emit " "
<> fromDuckDBAlterTableAction action
instance IsSql92AlterTableActionSyntax DuckDBAlterTableActionSyntax where
type Sql92AlterTableAlterColumnActionSyntax DuckDBAlterTableActionSyntax = DuckDBAlterColumnActionSyntax
type Sql92AlterTableColumnSchemaSyntax DuckDBAlterTableActionSyntax = DuckDBColumnSchemaSyntax
alterColumnSyntax colNm action =
DuckDBAlterTableActionSyntax $
emit "ALTER COLUMN "
<> quotedIdentifier colNm
<> emit " "
<> fromDuckDBAlterColumnAction action
addColumnSyntax colNm schema =
DuckDBAlterTableActionSyntax $
emit "ADD COLUMN "
<> quotedIdentifier colNm
<> emit " "
<> fromDuckDBColumnSchema schema
dropColumnSyntax colNm =
DuckDBAlterTableActionSyntax (emit "DROP COLUMN " <> quotedIdentifier colNm)
renameTableToSyntax newNm =
DuckDBAlterTableActionSyntax (emit "RENAME TO " <> quotedIdentifier newNm)
renameColumnToSyntax oldNm newNm =
DuckDBAlterTableActionSyntax $
emit "RENAME COLUMN "
<> quotedIdentifier oldNm
<> emit " TO "
<> quotedIdentifier newNm
instance IsSql92AlterColumnActionSyntax DuckDBAlterColumnActionSyntax where
setNullSyntax = DuckDBAlterColumnActionSyntax (emit "DROP NOT NULL")
setNotNullSyntax = DuckDBAlterColumnActionSyntax (emit "SET NOT NULL")
instance IsSql92CreateTableSyntax DuckDBCreateTableSyntax where
type Sql92CreateTableTableNameSyntax DuckDBCreateTableSyntax = DuckDBTableNameSyntax
type Sql92CreateTableColumnSchemaSyntax DuckDBCreateTableSyntax = DuckDBColumnSchemaSyntax
type Sql92CreateTableTableConstraintSyntax DuckDBCreateTableSyntax = DuckDBTableConstraintSyntax
type Sql92CreateTableOptionsSyntax DuckDBCreateTableSyntax = DuckDBTableOptionsSyntax
createTableSyntax options tblNm fieldTypes constraints =
let (beforeOptions, afterOptions) =
case options of
Nothing -> (emit " ", emit " ")
Just (DuckDBTableOptionsSyntax before after) ->
(emit " " <> before <> emit " ", emit " " <> after <> emit " ")
in DuckDBCreateTableSyntax $
emit "CREATE"
<> beforeOptions
<> emit "TABLE "
<> fromDuckDBTableName tblNm
<> emit " ("
<> sepBy
(emit ", ")
( map (\(nm, ty) -> quotedIdentifier nm <> emit " " <> fromDuckDBColumnSchema ty) fieldTypes
<> map fromDuckDBTableConstraint constraints
)
<> emit ")"
<> afterOptions
duckDBForeignKeyAction :: ForeignKeyAction -> DuckDBSyntax
duckDBForeignKeyAction ForeignKeyActionCascade = emit "CASCADE"
duckDBForeignKeyAction ForeignKeyActionSetNull = emit "SET NULL"
duckDBForeignKeyAction ForeignKeyActionSetDefault = emit "SET DEFAULT"
duckDBForeignKeyAction ForeignKeyActionRestrict = emit "RESTRICT"
duckDBForeignKeyAction ForeignKeyNoAction = emit "NO ACTION"
instance IsSql92TableConstraintSyntax DuckDBTableConstraintSyntax where
primaryKeyConstraintSyntax fieldNames =
DuckDBTableConstraintSyntax $
emit "PRIMARY KEY("
<> sepBy (emit ", ") (map quotedIdentifier (NE.toList fieldNames))
<> emit ")"
foreignKeyConstraintSyntax localCols refTbl refCols onUpdate onDelete =
DuckDBTableConstraintSyntax $
emit "FOREIGN KEY("
<> sepBy (emit ", ") (map quotedIdentifier (NE.toList localCols))
<> emit ") REFERENCES "
<> quotedIdentifier refTbl
<> emit "("
<> sepBy (emit ", ") (map quotedIdentifier (NE.toList refCols))
<> emit ")"
<> emit " ON UPDATE "
<> duckDBForeignKeyAction onUpdate
<> emit " ON DELETE "
<> duckDBForeignKeyAction onDelete
instance IsSql92ColumnSchemaSyntax DuckDBColumnSchemaSyntax where
type Sql92ColumnSchemaColumnTypeSyntax DuckDBColumnSchemaSyntax = DuckDBDataTypeSyntax
type Sql92ColumnSchemaExpressionSyntax DuckDBColumnSchemaSyntax = DuckDBExpressionSyntax
type Sql92ColumnSchemaColumnConstraintDefinitionSyntax DuckDBColumnSchemaSyntax = DuckDBColumnConstraintDefinitionSyntax
columnSchemaSyntax colType defaultClause constraints collation =
DuckDBColumnSchemaSyntax $
duckDBDataType colType
<> maybe mempty (\d -> emit " DEFAULT " <> fromDuckDBExpression d) defaultClause
<> ( case constraints of
[] -> mempty
_ -> foldMap (\c -> emit " " <> fromDuckDBColumnConstraintDefinition c) constraints
)
<> maybe mempty (\nm -> emit " COLLATE " <> quotedIdentifier nm) collation
instance IsSql92MatchTypeSyntax DuckDBMatchTypeSyntax where
fullMatchSyntax = DuckDBMatchTypeSyntax (emit "FULL") fullMatchSyntax
partialMatchSyntax = DuckDBMatchTypeSyntax (emit "PARTIAL") partialMatchSyntax
instance IsSql92ReferentialActionSyntax DuckDBReferentialActionSyntax where
referentialActionCascadeSyntax = DuckDBReferentialActionSyntax (emit "CASCADE") referentialActionCascadeSyntax
referentialActionNoActionSyntax = DuckDBReferentialActionSyntax (emit "NO ACTION") referentialActionNoActionSyntax
referentialActionSetDefaultSyntax = DuckDBReferentialActionSyntax (emit "SET DEFAULT") referentialActionSetDefaultSyntax
referentialActionSetNullSyntax = DuckDBReferentialActionSyntax (emit "SET NULL") referentialActionSetNullSyntax
instance IsSql92ColumnConstraintDefinitionSyntax DuckDBColumnConstraintDefinitionSyntax where
type Sql92ColumnConstraintDefinitionConstraintSyntax DuckDBColumnConstraintDefinitionSyntax = DuckDBColumnConstraintSyntax
type Sql92ColumnConstraintDefinitionAttributesSyntax DuckDBColumnConstraintDefinitionSyntax = SqlConstraintAttributesBuilder
constraintDefinitionSyntax nm constraint attrs =
DuckDBColumnConstraintDefinitionSyntax syntax serialized
where
syntax =
maybe mempty (\n -> emit "CONSTRAINT " <> quotedIdentifier n <> emit " ") nm
<> fromDuckDBColumnConstraint constraint
<> maybe mempty (\a -> emit " " <> duckDBSqlConstraintAttributes a) attrs
serialized =
constraintDefinitionSyntax nm (duckDBColumnConstraintSerialized constraint) (fmap sqlConstraintAttributesSerialized attrs)
-- | Emit a 'SqlConstraintAttributesBuilder' as DuckDB syntax. DuckDB does not
-- support @DEFERRABLE@/@INITIALLY DEFERRED@ at the time of writing, but we
-- still emit the corresponding SQL keywords so that user-supplied constraint
-- attributes are not silently dropped. If DuckDB rejects them, the user will
-- see an obvious error rather than a subtle behavioural difference.
duckDBSqlConstraintAttributes :: SqlConstraintAttributesBuilder -> DuckDBSyntax
duckDBSqlConstraintAttributes (SqlConstraintAttributesBuilder timing deferrable) =
maybe mempty timingBuilder timing <> maybe mempty deferrableBuilder deferrable
where
timingBuilder InitiallyDeferred = emit "INITIALLY DEFERRED"
timingBuilder InitiallyImmediate = emit "INITIALLY IMMEDIATE"
deferrableBuilder False = emit "NOT DEFERRABLE"
deferrableBuilder True = emit "DEFERRABLE"
instance IsSql92ColumnConstraintSyntax DuckDBColumnConstraintSyntax where
type Sql92ColumnConstraintMatchTypeSyntax DuckDBColumnConstraintSyntax = DuckDBMatchTypeSyntax
type Sql92ColumnConstraintReferentialActionSyntax DuckDBColumnConstraintSyntax = DuckDBReferentialActionSyntax
type Sql92ColumnConstraintExpressionSyntax DuckDBColumnConstraintSyntax = DuckDBExpressionSyntax
notNullConstraintSyntax = DuckDBColumnConstraintSyntax (emit "NOT NULL") notNullConstraintSyntax
uniqueColumnConstraintSyntax = DuckDBColumnConstraintSyntax (emit "UNIQUE") uniqueColumnConstraintSyntax
primaryKeyColumnConstraintSyntax = DuckDBColumnConstraintSyntax (emit "PRIMARY KEY") primaryKeyColumnConstraintSyntax
checkColumnConstraintSyntax expr =
DuckDBColumnConstraintSyntax
(emit "CHECK(" <> fromDuckDBExpression expr <> emit ")")
( checkColumnConstraintSyntax
( BeamSerializedExpression
(duckDBRenderSyntaxScript (fromDuckDBExpression expr))
)
)
referencesConstraintSyntax tbl fields matchType onUpdate onDelete =
DuckDBColumnConstraintSyntax syntax serialized
where
syntax =
emit "REFERENCES "
<> quotedIdentifier tbl
<> emit "("
<> sepBy (emit ", ") (map quotedIdentifier fields)
<> emit ")"
<> maybe mempty (\m -> emit " " <> fromDuckDBMatchType m) matchType
<> maybe mempty (\a -> emit " ON UPDATE " <> fromDuckDBReferentialAction a) onUpdate
<> maybe mempty (\a -> emit " ON DELETE " <> fromDuckDBReferentialAction a) onDelete
serialized =
referencesConstraintSyntax
tbl
fields
(fmap duckDBMatchTypeSerialized matchType)
(fmap duckDBReferentialActionSerialized onUpdate)
(fmap duckDBReferentialActionSerialized onDelete)
instance IsSql92CreateDropIndexSyntax DuckDBCommandSyntax where
type Sql92CreateIndexOptionsSyntax DuckDBCommandSyntax = DuckDBIndexOptions
defaultIndexOptions = DuckDBIndexOptions {duckDBIndexUnique = False}
createIndexCmd idxNm tblNm cols opts =
DuckDBCommandSyntax $
emit (if duckDBIndexUnique opts then "CREATE UNIQUE INDEX " else "CREATE INDEX ")
<> quotedIdentifier idxNm
<> emit " ON "
<> fromDuckDBTableName tblNm
<> parens (sepBy (emit ", ") (NE.toList (fmap quotedIdentifier cols)))
dropIndexCmd idxNm =
DuckDBCommandSyntax (emit "DROP INDEX " <> quotedIdentifier idxNm)
serializeIndexOptions opts =
object ["unique" .= duckDBIndexUnique opts]
deserializeIndexOptions =
withObject "DuckDBIndexOptions" $ \v ->
DuckDBIndexOptions <$> v .: "unique"
instance IsSql92UniqueIndexSyntax DuckDBCommandSyntax where
setUniqueIndexOptions u opts = opts {duckDBIndexUnique = u}
indexIsUnique = duckDBIndexUnique
optPrec :: Maybe Word -> DuckDBSyntax
optPrec Nothing = mempty
optPrec (Just x) = emit "(" <> emit (fromString (show x)) <> emit ")"
optCharSet :: Maybe Text -> DuckDBSyntax
optCharSet Nothing = mempty
optCharSet (Just cs) = emit " CHARACTER SET " <> emit cs
optNumericPrec :: Maybe (Word, Maybe Word) -> DuckDBSyntax
optNumericPrec Nothing = mempty
optNumericPrec (Just (prec, Nothing)) = optPrec (Just prec)
optNumericPrec (Just (prec, Just dec)) = emit "(" <> emit (fromString (show prec)) <> emit ", " <> emit (fromString (show dec)) <> emit ")"
instance HasSqlValueSyntax DuckDBValueSyntax Int8 where
sqlValueSyntax i = DuckDBValueSyntax (emitIntegral i)
instance HasSqlValueSyntax DuckDBValueSyntax Int16 where
sqlValueSyntax i = DuckDBValueSyntax (emitIntegral i)
instance HasSqlValueSyntax DuckDBValueSyntax Int32 where
sqlValueSyntax i = DuckDBValueSyntax (emitIntegral i)
instance HasSqlValueSyntax DuckDBValueSyntax Int64 where
sqlValueSyntax i = DuckDBValueSyntax (emitIntegral i)
instance HasSqlValueSyntax DuckDBValueSyntax Word8 where
sqlValueSyntax i = DuckDBValueSyntax (emitIntegral i)
instance HasSqlValueSyntax DuckDBValueSyntax Word16 where
sqlValueSyntax i = DuckDBValueSyntax (emitIntegral i)
instance HasSqlValueSyntax DuckDBValueSyntax Word32 where
sqlValueSyntax i = DuckDBValueSyntax (emitIntegral i)
instance HasSqlValueSyntax DuckDBValueSyntax Word64 where
sqlValueSyntax i = DuckDBValueSyntax (emitIntegral i)
instance HasSqlValueSyntax DuckDBValueSyntax Float where
sqlValueSyntax f = DuckDBValueSyntax (emitRealFloat f)
instance HasSqlValueSyntax DuckDBValueSyntax Double where
sqlValueSyntax f = DuckDBValueSyntax (emitRealFloat f)
instance HasSqlValueSyntax DuckDBValueSyntax Scientific where
sqlValueSyntax f = DuckDBValueSyntax (emitScientific f)
instance HasSqlValueSyntax DuckDBValueSyntax Bool where
sqlValueSyntax = DuckDBValueSyntax . emitValue
instance HasSqlValueSyntax DuckDBValueSyntax SqlNull where
sqlValueSyntax _ = DuckDBValueSyntax (emitValue Null)
instance HasSqlValueSyntax DuckDBValueSyntax String where
sqlValueSyntax = sqlValueSyntax . Text.pack
instance HasSqlValueSyntax DuckDBValueSyntax Text where
sqlValueSyntax = DuckDBValueSyntax . emitValue
instance HasSqlValueSyntax DuckDBValueSyntax Lazy.Text where
sqlValueSyntax = sqlValueSyntax . Lazy.toStrict
instance HasSqlValueSyntax DuckDBValueSyntax Day where
sqlValueSyntax = DuckDBValueSyntax . emitValue
instance HasSqlValueSyntax DuckDBValueSyntax TimeOfDay where
sqlValueSyntax = DuckDBValueSyntax . emitValue
instance HasSqlValueSyntax DuckDBValueSyntax LocalTime where
sqlValueSyntax = DuckDBValueSyntax . emitValue
instance HasSqlValueSyntax DuckDBValueSyntax UTCTime where
sqlValueSyntax = DuckDBValueSyntax . emitValue
instance (HasSqlValueSyntax DuckDBValueSyntax x) => HasSqlValueSyntax DuckDBValueSyntax (Maybe x) where
sqlValueSyntax (Just x) = sqlValueSyntax x
sqlValueSyntax Nothing = sqlValueSyntax SqlNull
instance IsSql92ExpressionSyntax DuckDBExpressionSyntax where
type Sql92ExpressionValueSyntax DuckDBExpressionSyntax = DuckDBValueSyntax
type Sql92ExpressionSelectSyntax DuckDBExpressionSyntax = DuckDBSelectSyntax
type Sql92ExpressionFieldNameSyntax DuckDBExpressionSyntax = DuckDBFieldNameSyntax
type Sql92ExpressionQuantifierSyntax DuckDBExpressionSyntax = DuckDBComparisonQuantifierSyntax
type Sql92ExpressionCastTargetSyntax DuckDBExpressionSyntax = DuckDBDataTypeSyntax
type Sql92ExpressionExtractFieldSyntax DuckDBExpressionSyntax = DuckDBExtractFieldSyntax
addE = binOp "+"
subE = binOp "-"
mulE = binOp "*"
divE = binOp "/"
modE = binOp "%"
orE = binOp "OR"
andE = binOp "AND"
likeE = binOp "LIKE"
overlapsE = binOp "OVERLAPS"
eqE = compOp "="
neqE = compOp "<>"
ltE = compOp "<"
gtE = compOp ">"
leE = compOp "<="
geE = compOp ">="
negateE = unOp "-"
notE = unOp "NOT"
isNotNullE = postFix "IS NOT NULL"
isNullE = postFix "IS NULL"
isTrueE = postFix "IS TRUE"
isFalseE = postFix "IS FALSE"
isNotTrueE = postFix "IS NOT TRUE"
isNotFalseE = postFix "IS NOT FALSE"
isUnknownE = postFix "IS UNKNOWN"
isNotUnknownE = postFix "IS NOT UNKNOWN"
existsE select = DuckDBExpressionSyntax (emit "EXISTS " <> parens (fromDuckDBSelect select))
uniqueE select = DuckDBExpressionSyntax (emit "UNIQUE " <> parens (fromDuckDBSelect select))
betweenE a b c =
DuckDBExpressionSyntax
( parens (fromDuckDBExpression a)
<> emit " BETWEEN "
<> parens (fromDuckDBExpression b)
<> emit " AND "
<> parens (fromDuckDBExpression c)
)
valueE = DuckDBExpressionSyntax . fromDuckDBValue
rowE vs = DuckDBExpressionSyntax (parens (commas (map fromDuckDBExpression vs)))
quantifierListE vs =
DuckDBExpressionSyntax $
emit "(VALUES " <> sepBy (emit ", ") (fmap (parens . coerce) vs) <> emit ")"
fieldE = DuckDBExpressionSyntax . fromDuckDBFieldName
subqueryE = DuckDBExpressionSyntax . parens . fromDuckDBSelect
positionE needle haystack =
DuckDBExpressionSyntax $
emit "POSITION" <> parens (parens (fromDuckDBExpression needle) <> emit " IN " <> parens (fromDuckDBExpression haystack))
nullIfE a b =
DuckDBExpressionSyntax $
emit "NULLIF" <> parens (fromDuckDBExpression a <> emit ", " <> fromDuckDBExpression b)
absE x = DuckDBExpressionSyntax (emit "ABS" <> parens (fromDuckDBExpression x))
bitLengthE x = DuckDBExpressionSyntax (emit "8 * LENGTH" <> parens (emit "CAST" <> parens (parens (fromDuckDBExpression x) <> emit " AS BLOB")))
charLengthE x = DuckDBExpressionSyntax (emit "LENGTH" <> parens (fromDuckDBExpression x))
octetLengthE x = DuckDBExpressionSyntax (emit "LENGTH" <> parens (emit "CAST" <> parens (parens (fromDuckDBExpression x) <> emit " AS BLOB")))
lowerE x = DuckDBExpressionSyntax (emit "LOWER" <> parens (fromDuckDBExpression x))
upperE x = DuckDBExpressionSyntax (emit "UPPER" <> parens (fromDuckDBExpression x))
trimE x = DuckDBExpressionSyntax (emit "TRIM" <> parens (fromDuckDBExpression x))
coalesceE es = DuckDBExpressionSyntax (emit "COALESCE" <> parens (commas (map fromDuckDBExpression es)))
extractE field from = DuckDBExpressionSyntax (emit "EXTRACT(" <> fromDuckDBExtractField field <> emit " FROM (" <> fromDuckDBExpression from <> emit "))")
castE e t = DuckDBExpressionSyntax (emit "CAST" <> parens (parens (fromDuckDBExpression e) <> emit " AS " <> duckDBDataType t))
caseE cases else_ =
DuckDBExpressionSyntax $
emit "CASE "
<> foldMap (\(cond, res) -> emit "WHEN " <> fromDuckDBExpression cond <> emit " THEN " <> fromDuckDBExpression res <> emit " ") cases
<> emit "ELSE "
<> fromDuckDBExpression else_
<> emit " END"
currentTimestampE = DuckDBExpressionSyntax (emit "CURRENT_TIMESTAMP")
defaultE = DuckDBExpressionSyntax (emit "DEFAULT")
inE e es = DuckDBExpressionSyntax (parens (fromDuckDBExpression e) <> emit " IN " <> parens (commas (map fromDuckDBExpression es)))
inSelectE e sel =
DuckDBExpressionSyntax (parens (fromDuckDBExpression e) <> emit " IN " <> parens (fromDuckDBSelect sel))
binOp :: Text -> DuckDBExpressionSyntax -> DuckDBExpressionSyntax -> DuckDBExpressionSyntax
binOp op a b =
DuckDBExpressionSyntax $
parens (fromDuckDBExpression a) <> emit " " <> emit op <> emit " " <> parens (fromDuckDBExpression b)
compOp ::
Text ->
Maybe DuckDBComparisonQuantifierSyntax ->
DuckDBExpressionSyntax ->
DuckDBExpressionSyntax ->
DuckDBExpressionSyntax
compOp op quantifier a b =
DuckDBExpressionSyntax $
parens (fromDuckDBExpression a)
<> emit op
<> maybe mempty (\q -> emit " " <> fromDuckDBComparisonQuantifier q <> emit " ") quantifier
<> parens (fromDuckDBExpression b)
unOp, postFix :: Text -> DuckDBExpressionSyntax -> DuckDBExpressionSyntax
unOp op a =
DuckDBExpressionSyntax (emit op <> parens (fromDuckDBExpression a))
postFix op a =
DuckDBExpressionSyntax (parens (fromDuckDBExpression a) <> emit " " <> emit op)
instance IsSql92ProjectionSyntax DuckDBProjectionSyntax where
type Sql92ProjectionExpressionSyntax DuckDBProjectionSyntax = DuckDBExpressionSyntax
projExprs exprs =
DuckDBProjectionSyntax $
commas
( map
( \(expr, nm) ->
fromDuckDBExpression expr
<> maybe mempty (\nm' -> emit " AS " <> quotedIdentifier nm') nm
)
exprs
)
instance IsSql92OrderingSyntax DuckDBOrderingSyntax where
type Sql92OrderingExpressionSyntax DuckDBOrderingSyntax = DuckDBExpressionSyntax
ascOrdering e = DuckDBOrderingSyntax (fromDuckDBExpression e <> emit " ASC")
descOrdering e = DuckDBOrderingSyntax (fromDuckDBExpression e <> emit " DESC")
instance IsSql2003OrderingElementaryOLAPOperationsSyntax DuckDBOrderingSyntax where
nullsFirstOrdering o = DuckDBOrderingSyntax $ coerce o <> emit " NULLS FIRST"
nullsLastOrdering o = DuckDBOrderingSyntax $ coerce o <> emit " NULLS LAST"
instance IsSql92SelectSyntax DuckDBSelectSyntax where
type Sql92SelectSelectTableSyntax DuckDBSelectSyntax = DuckDBSelectTableSyntax
type Sql92SelectOrderingSyntax DuckDBSelectSyntax = DuckDBOrderingSyntax
selectStmt tbl ordering limit offset =
DuckDBSelectSyntax $
mconcat
[ fromDuckDBSelectTable tbl,
case ordering of
[] -> mempty
ordering' -> emit " ORDER BY " <> commas (map coerce ordering'),
maybe mempty (emit . fromString . (" LIMIT " <>) . show) limit,
maybe mempty (emit . fromString . (" OFFSET " <>) . show) offset
]
-- | DuckDB @ON CONFLICT@ syntax
newtype DuckDBOnConflictSyntax = DuckDBOnConflictSyntax {fromDuckDBOnConflict :: DuckDBSyntax}
-- | SQLite @INSERT@ syntax.
newtype DuckDBInsertSyntax = DuckDBInsertSyntax {fromDuckDBInsert :: DuckDBSyntax}
newtype DuckDBInsertValuesSyntax = DuckDBInsertValuesSyntax {fromDuckDBInsertValues :: DuckDBSyntax}
instance IsSql92InsertSyntax DuckDBInsertSyntax where
type Sql92InsertTableNameSyntax DuckDBInsertSyntax = DuckDBTableNameSyntax
type Sql92InsertValuesSyntax DuckDBInsertSyntax = DuckDBInsertValuesSyntax
insertStmt tblName fields values =
DuckDBInsertSyntax $
emit "INSERT INTO "
<> fromDuckDBTableName tblName
<> emit "("
<> commas (map quotedIdentifier fields)
<> emit ") "
<> fromDuckDBInsertValues values
instance IsSql92InsertValuesSyntax DuckDBInsertValuesSyntax where
type Sql92InsertValuesExpressionSyntax DuckDBInsertValuesSyntax = DuckDBExpressionSyntax
type Sql92InsertValuesSelectSyntax DuckDBInsertValuesSyntax = DuckDBSelectSyntax
insertSqlExpressions es =
DuckDBInsertValuesSyntax $
emit "VALUES "
<> commas
( map
(\values -> emit "(" <> commas (coerce values) <> emit ")")
es
)
insertFromSql (DuckDBSelectSyntax a) = DuckDBInsertValuesSyntax a
-- | DuckDB @UPDATE@ syntax
newtype DuckDBUpdateSyntax = DuckDBUpdateSyntax {fromDuckDBUpdate :: DuckDBSyntax}
instance IsSql92UpdateSyntax DuckDBUpdateSyntax where
type Sql92UpdateFieldNameSyntax DuckDBUpdateSyntax = DuckDBFieldNameSyntax
type Sql92UpdateExpressionSyntax DuckDBUpdateSyntax = DuckDBExpressionSyntax
type Sql92UpdateTableNameSyntax DuckDBUpdateSyntax = DuckDBTableNameSyntax
updateStmt tbl fields where_ =
DuckDBUpdateSyntax $
emit "UPDATE "
<> fromDuckDBTableName tbl
<> ( case fields of
[] -> mempty
fs ->
emit " SET "
<> commas (map (\(field, val) -> fromDuckDBFieldName field <> emit "=" <> fromDuckDBExpression val) fs)
)
<> maybe mempty (\whereInner -> emit " WHERE " <> fromDuckDBExpression whereInner) where_
-- | DuckDB @DELETE@ syntax
newtype DuckDBDeleteSyntax = DuckDBDeleteSyntax {fromDuckDBDelete :: DuckDBSyntax}
instance IsSql92DeleteSyntax DuckDBDeleteSyntax where
type Sql92DeleteExpressionSyntax DuckDBDeleteSyntax = DuckDBExpressionSyntax
type Sql92DeleteTableNameSyntax DuckDBDeleteSyntax = DuckDBTableNameSyntax
deleteStmt tbl alias where_ =
DuckDBDeleteSyntax $
emit "DELETE FROM "
<> fromDuckDBTableName tbl
<> maybe mempty (\alias_ -> emit " AS " <> quotedIdentifier alias_) alias
<> maybe mempty (\whereInner -> emit " WHERE " <> fromDuckDBExpression whereInner) where_
deleteSupportsAlias _ = True
instance IsSql99CommonTableExpressionSelectSyntax DuckDBSelectSyntax where
type Sql99SelectCTESyntax DuckDBSelectSyntax = DuckDBCommonTableExpressionSyntax
withSyntax ctes (DuckDBSelectSyntax select) =
DuckDBSelectSyntax $
emit "WITH "
<> sepBy (emit ", ") (map fromDuckDBCommonTableExpressionSyntax ctes)
<> select
instance IsSql99RecursiveCommonTableExpressionSelectSyntax DuckDBSelectSyntax where
withRecursiveSyntax ctes (DuckDBSelectSyntax select) =
DuckDBSelectSyntax $
emit "WITH RECURSIVE "
<> sepBy (emit ", ") (map fromDuckDBCommonTableExpressionSyntax ctes)
<> select
instance IsSql99CommonTableExpressionSyntax DuckDBCommonTableExpressionSyntax where
type Sql99CTESelectSyntax DuckDBCommonTableExpressionSyntax = DuckDBSelectSyntax
cteSubquerySyntax tbl fields (DuckDBSelectSyntax select) =
DuckDBCommonTableExpressionSyntax $
quotedIdentifier tbl
<> parens (sepBy (emit ",") (map quotedIdentifier fields))
<> emit " AS "
<> parens select
instance IsSql99FunctionExpressionSyntax DuckDBExpressionSyntax where
functionCallE name args =
DuckDBExpressionSyntax $
fromDuckDBExpression name
<> parens (sepBy (emit ", ") (map fromDuckDBExpression args))
functionNameE nm = DuckDBExpressionSyntax (emit nm)
instance IsSql99ExpressionSyntax DuckDBExpressionSyntax where
-- The implementation of 'distinctE' is the same as Postgres', but its
-- use via 'distinct_' creates SQL queries which are not necessarily supported
distinctE select = DuckDBExpressionSyntax (emit "DISTINCT (" <> fromDuckDBSelect select <> emit ")")
-- DuckDB doesn't support 'SIMILAR TO' exactly, but rather
-- provides the 'regexp_matches' function
similarToE left right =
DuckDBExpressionSyntax $
mconcat
[ emit "regexp_matches(",
coerce left,
emit ", ",
coerce right,
emitChar ')'
]
instanceFieldE i nm =
DuckDBExpressionSyntax $
parens (fromDuckDBExpression i) <> emit "." <> quotedIdentifier nm
refFieldE i nm =
DuckDBExpressionSyntax $
parens (fromDuckDBExpression i) <> emit "->" <> quotedIdentifier nm
instance IsSql99ConcatExpressionSyntax DuckDBExpressionSyntax where
concatE [] = valueE (sqlValueSyntax ("" :: Text))
concatE [x] = x
concatE es =
DuckDBExpressionSyntax $
emit "CONCAT"
<> parens
( sepBy
(emit ", ")
-- DuckDB's type inference doesn't work reliably inside of CONCAT. I suspect
-- that this is only for ? parameters for binding.
-- However, to be extra safe, we cast every element inside the `CONCAT` call
-- to a VARCHAR
-- TODO: optimize this to only CAST when the expression is a binding parameter
(map (\e -> emit "CAST" <> parens (coerce e <> emit " AS VARCHAR")) es)
)
instance IsSql99AggregationExpressionSyntax DuckDBExpressionSyntax where
everyE = aggFunc "BOOL_AND" -- DuckDB doesn't implement 'EVERY'
-- The following two functions are modeled the same way
-- as for the Postgres backend
someE = aggFunc "BOOL_OR"
anyE = aggFunc "BOOL_OR"
instance IsSql2003ExpressionSyntax DuckDBExpressionSyntax where
type
Sql2003ExpressionWindowFrameSyntax DuckDBExpressionSyntax =
DuckDBWindowFrameSyntax
overE expr frame =
DuckDBExpressionSyntax $
fromDuckDBExpression expr <> emit " " <> fromDuckDBWindowFrame frame
rowNumberE = DuckDBExpressionSyntax $ emit "ROW_NUMBER()"
instance IsSql2003EnhancedNumericFunctionsExpressionSyntax DuckDBExpressionSyntax where
lnE x = DuckDBExpressionSyntax (emit "LN(" <> fromDuckDBExpression x <> emit ")")
expE x = DuckDBExpressionSyntax (emit "EXP(" <> fromDuckDBExpression x <> emit ")")
sqrtE x = DuckDBExpressionSyntax (emit "SQRT(" <> fromDuckDBExpression x <> emit ")")
ceilE x = DuckDBExpressionSyntax (emit "CEIL(" <> fromDuckDBExpression x <> emit ")")
floorE x = DuckDBExpressionSyntax (emit "FLOOR(" <> fromDuckDBExpression x <> emit ")")
powerE x y = DuckDBExpressionSyntax (emit "POWER(" <> fromDuckDBExpression x <> emit ", " <> fromDuckDBExpression y <> emit ")")
instance IsSql2003ExpressionAdvancedOLAPOperationsSyntax DuckDBExpressionSyntax where
denseRankAggE = DuckDBExpressionSyntax $ emit "DENSE_RANK()"
percentRankAggE = DuckDBExpressionSyntax $ emit "PERCENT_RANK()"
cumeDistAggE = DuckDBExpressionSyntax $ emit "CUME_DIST()"
instance IsSql2003ExpressionElementaryOLAPOperationsSyntax DuckDBExpressionSyntax where
rankAggE = DuckDBExpressionSyntax $ emit "RANK()"
filterAggE agg f =
DuckDBExpressionSyntax $
fromDuckDBExpression agg <> emit " FILTER (WHERE " <> fromDuckDBExpression f <> emit ")"
binAggFunc ::
Text ->
Maybe DuckDBAggregationSetQuantifierSyntax ->
DuckDBExpressionSyntax ->
DuckDBExpressionSyntax ->
DuckDBExpressionSyntax
binAggFunc fn q x y =
DuckDBExpressionSyntax $
emit fn
<> emitChar '('
<> maybe mempty (\inner -> fromDuckDBAggregationSetQuantifier inner <> emitChar ' ') q
<> fromDuckDBExpression x
<> emit ", "
<> fromDuckDBExpression y
<> emitChar ')'
instance IsSql2003EnhancedNumericFunctionsAggregationExpressionSyntax DuckDBExpressionSyntax where
stddevPopE = aggFunc "STDDEV_POP"
stddevSampE = aggFunc "STDDEV_SAMP"
varPopE = aggFunc "VAR_POP"
varSampE = aggFunc "VAR_SAMP"
covarPopE = binAggFunc "COVAR_POP"
covarSampE = binAggFunc "COVAR_SAMP"
corrE = binAggFunc "CORR"
regrSlopeE = binAggFunc "REGR_SLOPE"
regrInterceptE = binAggFunc "REGR_INTERCEPT"
regrCountE = binAggFunc "REGR_COUNT"
regrRSquaredE = binAggFunc "REGR_R2"
regrAvgXE = binAggFunc "REGR_AVGX"
regrAvgYE = binAggFunc "REGR_AVGY"
regrSXXE = binAggFunc "REGR_SXX"
regrSYYE = binAggFunc "REGR_SYY"
regrSXYE = binAggFunc "REGR_SXY"
instance IsSql2003NtileExpressionSyntax DuckDBExpressionSyntax where
ntileE x = DuckDBExpressionSyntax (emit "NTILE(" <> fromDuckDBExpression x <> emit ")")
instance IsSql2003LeadAndLagExpressionSyntax DuckDBExpressionSyntax where
leadE x Nothing Nothing =
DuckDBExpressionSyntax (emit "LEAD(" <> fromDuckDBExpression x <> emit ")")
leadE x (Just n) Nothing =
DuckDBExpressionSyntax (emit "LEAD(" <> fromDuckDBExpression x <> emit ", " <> fromDuckDBExpression n <> emit ")")
leadE x (Just n) (Just def) =
DuckDBExpressionSyntax (emit "LEAD(" <> fromDuckDBExpression x <> emit ", " <> fromDuckDBExpression n <> emit ", " <> fromDuckDBExpression def <> emit ")")
leadE x Nothing (Just def) =
DuckDBExpressionSyntax (emit "LEAD(" <> fromDuckDBExpression x <> emit ", 1, " <> fromDuckDBExpression def <> emit ")")
lagE x Nothing Nothing =
DuckDBExpressionSyntax (emit "LAG(" <> fromDuckDBExpression x <> emit ")")
lagE x (Just n) Nothing =
DuckDBExpressionSyntax (emit "LAG(" <> fromDuckDBExpression x <> emit ", " <> fromDuckDBExpression n <> emit ")")
lagE x (Just n) (Just def) =
DuckDBExpressionSyntax (emit "LAG(" <> fromDuckDBExpression x <> emit ", " <> fromDuckDBExpression n <> emit ", " <> fromDuckDBExpression def <> emit ")")
lagE x Nothing (Just def) =
DuckDBExpressionSyntax (emit "LAG(" <> fromDuckDBExpression x <> emit ", 1, " <> fromDuckDBExpression def <> emit ")")
instance IsSql2003FirstValueAndLastValueExpressionSyntax DuckDBExpressionSyntax where
firstValueE x = DuckDBExpressionSyntax (emit "FIRST_VALUE(" <> fromDuckDBExpression x <> emit ")")
lastValueE x = DuckDBExpressionSyntax (emit "LAST_VALUE(" <> fromDuckDBExpression x <> emit ")")
instance IsSql2003NthValueExpressionSyntax DuckDBExpressionSyntax where
nthValueE x n = DuckDBExpressionSyntax (emit "NTH_VALUE(" <> fromDuckDBExpression x <> emit ", " <> fromDuckDBExpression n <> emit ")")
instance IsSql2003WindowFrameSyntax DuckDBWindowFrameSyntax where
type Sql2003WindowFrameExpressionSyntax DuckDBWindowFrameSyntax = DuckDBExpressionSyntax
type Sql2003WindowFrameOrderingSyntax DuckDBWindowFrameSyntax = DuckDBOrderingSyntax
type Sql2003WindowFrameBoundsSyntax DuckDBWindowFrameSyntax = DuckDBWindowFrameBoundsSyntax
frameSyntax partition_ ordering_ bounds_ =
DuckDBWindowFrameSyntax $
emit "OVER "
<> parens
( maybe mempty (\p -> emit "PARTITION BY " <> sepBy (emit ", ") (map fromDuckDBExpression p)) partition_
<> maybe mempty (\o -> emit " ORDER BY " <> sepBy (emit ", ") (map fromDuckDBOrdering o)) ordering_
<> maybe mempty (\b -> emit " ROWS " <> fromDuckDBWindowFrameBounds b) bounds_
)
instance IsSql2003WindowFrameBoundsSyntax DuckDBWindowFrameBoundsSyntax where
type Sql2003WindowFrameBoundsBoundSyntax DuckDBWindowFrameBoundsSyntax = DuckDBWindowFrameBoundSyntax
fromToBoundSyntax from Nothing =
DuckDBWindowFrameBoundsSyntax (fromDuckDBWindowFrameBound from "PRECEDING")
fromToBoundSyntax from (Just to) =
DuckDBWindowFrameBoundsSyntax $
emit "BETWEEN " <> fromDuckDBWindowFrameBound from "PRECEDING" <> emit " AND " <> fromDuckDBWindowFrameBound to "FOLLOWING"
instance IsSql2003WindowFrameBoundSyntax DuckDBWindowFrameBoundSyntax where
unboundedSyntax = DuckDBWindowFrameBoundSyntax $ \where_ -> emit "UNBOUNDED " <> emit where_
nrowsBoundSyntax 0 = DuckDBWindowFrameBoundSyntax $ \_ -> emit "CURRENT ROW"
nrowsBoundSyntax n = DuckDBWindowFrameBoundSyntax $ \where_ -> emit (fromString (show n)) <> emit " " <> emit where_