@@ -890,6 +890,12 @@ extension StmtTypeChecker {
890890 }
891891 }
892892
893+ private mutating func insertColumnsIntoEnv( columns: borrowing Columns ) {
894+ for column in columns {
895+ env. insert ( column. key, ty: column. value)
896+ }
897+ }
898+
893899 mutating func typeCheck( createTable: CreateTableStmtSyntax ) {
894900 if pragmas. contains ( . requireStrictTables)
895901 && !createTable. options. kind. contains ( . strict) {
@@ -910,11 +916,16 @@ extension StmtTypeChecker {
910916 primaryKey: primaryKey ( of: createTable, columns: columns) ,
911917 kind: . normal
912918 )
913- case let . columns( columns) :
914- let columns : Columns = columns. reduce ( into: [ : ] ) {
915- $0 [ $1. value. name. value] = typeFor ( column: $1. value)
919+ case let . columns( columnsDefs) :
920+ var columns : Columns = [ : ]
921+ for (name, def) in columnsDefs {
922+ columns [ name. value] = typeFor (
923+ column: def,
924+ tableColumns: columns
925+ )
916926 }
917927
928+ validateTableConstraints ( of: createTable, columns: columns)
918929 schema [ createTable. name. value] = Table (
919930 name: createTable. name. value,
920931 columns: columns,
@@ -950,7 +961,7 @@ extension StmtTypeChecker {
950961 newColumns [ column. key == oldName. value ? newName. value : column. key] = column. value
951962 }
952963 case let . addColumn( column) :
953- table. columns [ column. name. value] = typeFor ( column: column)
964+ table. columns [ column. name. value] = typeFor ( column: column, tableColumns : table . columns )
954965 case let . dropColumn( column) :
955966 table. columns [ column. value] = nil
956967 }
@@ -987,11 +998,39 @@ extension StmtTypeChecker {
987998 }
988999
9891000 /// Will figure out the final SQL column type from the syntax
990- private mutating func typeFor( column: borrowing ColumnDefSyntax ) -> Type {
991- // Technically you can have a NULL primary key but I don't
992- // think people actually do that...
993- let isNotNullable = column. constraints
994- . contains { $0. isPkConstraint || $0. isNotNullConstraint }
1001+ private mutating func typeFor(
1002+ column: borrowing ColumnDefSyntax ,
1003+ tableColumns: borrowing Columns
1004+ ) -> Type {
1005+ var isNotNullable = false
1006+ for constraint in column. constraints {
1007+ switch constraint. kind {
1008+ case . primaryKey, . notNull:
1009+ // Technically you can have a NULL primary key but I don't
1010+ // think people actually do that...
1011+ isNotNullable = true
1012+ case . check( let expr) :
1013+ inNewEnvironment { typeChecker in
1014+ typeChecker. insertColumnsIntoEnv ( columns: tableColumns)
1015+ _ = typeChecker. typeCheck ( expr)
1016+ }
1017+ case . default( let expr) :
1018+ inNewEnvironment { typeChecker in
1019+ _ = typeChecker. typeCheck ( expr)
1020+ }
1021+ case . foreignKey( let fk) :
1022+ if schema [ fk. foreignTable. value] == nil {
1023+ diagnostics. add ( . tableDoesNotExist( fk. foreignTable) )
1024+ }
1025+ case . generated( let expr, _) :
1026+ inNewEnvironment { typeChecker in
1027+ typeChecker. insertColumnsIntoEnv ( columns: tableColumns)
1028+ _ = typeChecker. typeCheck ( expr)
1029+ }
1030+ case . unique, . collate:
1031+ break
1032+ }
1033+ }
9951034
9961035 // Validate it is an actual SQLite type since SQlite doesnt care.
9971036 if !Type. validTypeNames. contains ( column. type. name. value) {
@@ -1076,6 +1115,41 @@ extension StmtTypeChecker {
10761115 }
10771116 }
10781117
1118+ private mutating func validateTableConstraints(
1119+ of stmt: CreateTableStmtSyntax ,
1120+ columns: Columns
1121+ ) {
1122+ for constraint in stmt. constraints {
1123+ switch constraint. kind {
1124+ case . check( let expr) :
1125+ inNewEnvironment { typeChecker in
1126+ typeChecker. insertColumnsIntoEnv ( columns: columns)
1127+ _ = typeChecker. typeCheck ( expr)
1128+ }
1129+ case . foreignKey( let fkColumns, let fkClause) :
1130+ // Make sure listed columns exist
1131+ for column in fkColumns {
1132+ guard columns [ column. value] == nil else { continue }
1133+ diagnostics. add ( . columnDoesNotExist( column) )
1134+ }
1135+
1136+ // Make sure referenced table exists
1137+ guard let foreignTable = schema [ fkClause. foreignTable. value] else {
1138+ diagnostics. add ( . tableDoesNotExist( fkClause. foreignTable) )
1139+ return
1140+ }
1141+
1142+ // Make sure referenced columns exist
1143+ for column in fkClause. foreignColumns {
1144+ guard foreignTable. columns [ column. value] == nil else { continue }
1145+ diagnostics. add ( . columnDoesNotExist( column) )
1146+ }
1147+ case . primaryKey, . unique:
1148+ break
1149+ }
1150+ }
1151+ }
1152+
10791153 mutating func typeCheck( fts5Table: borrowing CreateVirtualTableStmtSyntax ) {
10801154 var columns : Columns = [ : ]
10811155
0 commit comments