@@ -27,6 +27,7 @@ type ExtraTableInfo struct {
2727 PkOrdinals []int
2828 Replicated bool
2929 Sequence string
30+ Checks []sql.CheckDefinition
3031}
3132
3233type ColumnInfo struct {
@@ -37,6 +38,7 @@ type ColumnInfo struct {
3738 ColumnDefault stdsql.NullString
3839 Comment stdsql.NullString
3940}
41+
4042type IndexedTable struct {
4143 * Table
4244 Lookup sql.IndexLookup
@@ -54,6 +56,8 @@ var _ sql.TruncateableTable = (*Table)(nil)
5456var _ sql.ReplaceableTable = (* Table )(nil )
5557var _ sql.CommentedTable = (* Table )(nil )
5658var _ sql.AutoIncrementTable = (* Table )(nil )
59+ var _ sql.CheckTable = (* Table )(nil )
60+ var _ sql.CheckAlterableTable = (* Table )(nil )
5761
5862func NewTable (name string , db * Database ) * Table {
5963 return & Table {
@@ -707,6 +711,9 @@ func (t *Table) PreciseMatch() bool {
707711
708712// Comment implements sql.CommentedTable.
709713func (t * Table ) Comment () string {
714+ t .mu .RLock ()
715+ defer t .mu .RUnlock ()
716+
710717 return t .comment .Text
711718}
712719
@@ -761,10 +768,16 @@ func (t *IndexedTable) LookupPartitions(ctx *sql.Context, lookup sql.IndexLookup
761768
762769// PeekNextAutoIncrementValue implements sql.AutoIncrementTable.
763770func (t * Table ) PeekNextAutoIncrementValue (ctx * sql.Context ) (uint64 , error ) {
771+ t .mu .RLock ()
772+ defer t .mu .RUnlock ()
773+
764774 if t .comment .Meta .Sequence == "" {
765775 return 0 , sql .ErrNoAutoIncrementCol
766776 }
777+ return t .getNextAutoIncrementValue (ctx )
778+ }
767779
780+ func (t * Table ) getNextAutoIncrementValue (ctx * sql.Context ) (uint64 , error ) {
768781 // For PeekNextAutoIncrementValue, we want to see what the next value would be
769782 // without actually incrementing. We can do this by getting currval + 1.
770783 var val uint64
@@ -788,12 +801,20 @@ func (t *Table) PeekNextAutoIncrementValue(ctx *sql.Context) (uint64, error) {
788801}
789802
790803// GetNextAutoIncrementValue implements sql.AutoIncrementTable.
791- func (t * Table ) GetNextAutoIncrementValue (ctx * sql.Context , insertVal interface {}) (uint64 , error ) {
804+ func (t * Table ) GetNextAutoIncrementValue (ctx * sql.Context , insertVal any ) (uint64 , error ) {
805+ t .mu .Lock ()
806+ defer t .mu .Unlock ()
807+
792808 if t .comment .Meta .Sequence == "" {
793809 return 0 , sql .ErrNoAutoIncrementCol
794810 }
795811
796- // If insertVal is provided and greater than current sequence value, update sequence
812+ nextVal , err := t .getNextAutoIncrementValue (ctx )
813+ if err != nil {
814+ return 0 , err
815+ }
816+
817+ // If insertVal is provided and greater than the next sequence value, update sequence
797818 if insertVal != nil {
798819 var start uint64
799820 switch v := insertVal .(type ) {
@@ -804,7 +825,7 @@ func (t *Table) GetNextAutoIncrementValue(ctx *sql.Context, insertVal interface{
804825 start = uint64 (v )
805826 }
806827 }
807- if start > 0 {
828+ if start > 0 && start > nextVal {
808829 err := t .setAutoIncrementValue (ctx , start )
809830 if err != nil {
810831 return 0 , err
@@ -815,7 +836,7 @@ func (t *Table) GetNextAutoIncrementValue(ctx *sql.Context, insertVal interface{
815836
816837 // Get next value from sequence
817838 var val uint64
818- err : = adapter .QueryRowCatalog (ctx , `SELECT nextval('` + t .comment .Meta .Sequence + `')` ).Scan (& val )
839+ err = adapter .QueryRowCatalog (ctx , `SELECT nextval('` + t .comment .Meta .Sequence + `')` ).Scan (& val )
819840 if err != nil {
820841 return 0 , ErrDuckDB .New (err )
821842 }
@@ -885,14 +906,12 @@ func (t *Table) setAutoIncrementValue(ctx *sql.Context, value uint64) error {
885906 // }
886907
887908 // Update the table comment with the new sequence name
888- tableInfo := t .comment .Meta
889- tableInfo .Sequence = fullSequenceName
890- comment := NewCommentWithMeta (t .comment .Text , tableInfo )
891- if _ , err = adapter .Exec (ctx , `COMMENT ON TABLE ` + FullTableName (t .db .catalog , t .db .name , t .name )+ ` IS '` + comment .Encode ()+ `'` ); err != nil {
892- return ErrDuckDB .New (err )
909+ if err = t .updateExtraTableInfo (ctx , func (info * ExtraTableInfo ) {
910+ info .Sequence = fullSequenceName
911+ }); err != nil {
912+ return err
893913 }
894914
895- t .comment .Meta .Sequence = fullSequenceName
896915 return t .withSchema (ctx )
897916}
898917
@@ -910,6 +929,62 @@ func (s *autoIncrementSetter) Close(ctx *sql.Context) error {
910929}
911930
912931func (s * autoIncrementSetter ) AcquireAutoIncrementLock (ctx * sql.Context ) (func (), error ) {
913- // DuckDB handles sequence synchronization internally
914- return func () {}, nil
932+ s .t .mu .Lock ()
933+ return s .t .mu .Unlock , nil
934+ }
935+
936+ func (t * Table ) updateExtraTableInfo (ctx * sql.Context , updater func (* ExtraTableInfo )) error {
937+ tableInfo := t .comment .Meta
938+ updater (& tableInfo )
939+ comment := NewCommentWithMeta (t .comment .Text , tableInfo )
940+ _ , err := adapter .Exec (ctx , `COMMENT ON TABLE ` + FullTableName (t .db .catalog , t .db .name , t .name )+ ` IS '` + comment .Encode ()+ `'` )
941+ if err != nil {
942+ return ErrDuckDB .New (err )
943+ }
944+ t .comment .Meta = tableInfo // Update the in-memory metadata
945+ return nil
946+ }
947+
948+ // CheckConstraints implements sql.CheckTable.
949+ func (t * Table ) GetChecks (ctx * sql.Context ) ([]sql.CheckDefinition , error ) {
950+ t .mu .RLock ()
951+ defer t .mu .RUnlock ()
952+
953+ return t .comment .Meta .Checks , nil
954+ }
955+
956+ // AddCheck implements sql.CheckAlterableTable.
957+ func (t * Table ) CreateCheck (ctx * sql.Context , check * sql.CheckDefinition ) error {
958+ t .mu .Lock ()
959+ defer t .mu .Unlock ()
960+
961+ // TODO(fan): Implement this once DuckDB supports modifying check constraints.
962+ // https://duckdb.org/docs/sql/statements/alter_table.html#add--drop-constraint
963+ // https://github.com/duckdb/duckdb/issues/57
964+ // Just record the check constraint for now.
965+ return t .updateExtraTableInfo (ctx , func (info * ExtraTableInfo ) {
966+ info .Checks = append (info .Checks , * check )
967+ })
968+ }
969+
970+ // DropCheck implements sql.CheckAlterableTable.
971+ func (t * Table ) DropCheck (ctx * sql.Context , checkName string ) error {
972+ t .mu .Lock ()
973+ defer t .mu .Unlock ()
974+
975+ checks := make ([]sql.CheckDefinition , 0 , max (len (t .comment .Meta .Checks )- 1 , 0 ))
976+ found := false
977+ for i , check := range t .comment .Meta .Checks {
978+ if check .Name == checkName {
979+ found = true
980+ continue
981+ }
982+ checks = append (checks , t .comment .Meta .Checks [i ])
983+ }
984+ if ! found {
985+ return sql .ErrUnknownConstraint .New (checkName )
986+ }
987+ return t .updateExtraTableInfo (ctx , func (info * ExtraTableInfo ) {
988+ info .Checks = checks
989+ })
915990}
0 commit comments