diff --git a/generator/helpers.go b/generator/helpers.go index d180ad2..5fa98fb 100644 --- a/generator/helpers.go +++ b/generator/helpers.go @@ -6,6 +6,19 @@ import ( "github.com/voidclancy/valk/schema" ) +var DEFAULT_FUNCS = map[string]string{ + "autoincrement": "", + "cuid": "generateCUID()", + "cuid(1)": "generateCUID()", + "cuid(2)": "generateCUID2()", + "uuid": "generateUUID()", + "uuid(4)": "generateUUID()", + "uuid(7)": "generateUUID7()", + "ulid": "generateULID()", + "nanoid": "generateNanoID()", + "now": "time.Now()", +} + func capitalize(s string) string { if s == "" { return "" @@ -69,22 +82,12 @@ func hasTimeField(m *schema.Model) bool { return false } func isKnownDefaultFunc(funcName string) bool { - switch funcName { - case "autoincrement", "cuid", "uuid", "now": - return true - } - return false + _, ok := DEFAULT_FUNCS[funcName] + return ok } + func defaultFuncCall(funcName string) string { - switch funcName { - case "cuid": - return "generateCUID()" - case "uuid": - return "generateUUID()" - case "now": - return "time.Now()" - } - return "" + return DEFAULT_FUNCS[funcName] } func hasStringField(m *schema.Model) bool { for _, sf := range m.ScalarFields { diff --git a/generator/templates/header.gotpl b/generator/templates/header.gotpl index db344c6..986ce38 100644 --- a/generator/templates/header.gotpl +++ b/generator/templates/header.gotpl @@ -25,6 +25,7 @@ var _ = time.Time{} var _ = json.RawMessage{} var _ = strings.Join var _ = uuid.New +var _ = uuid.NewV7 var _ = rand.Read var _ = strconv.AppendUint @@ -52,6 +53,66 @@ func generateUUID() string { return uuid.New().String() } +func generateUUID7() string { + id, err := uuid.NewV7() + if err != nil { + return uuid.New().String() + } + return id.String() +} + +func generateCUID2() string { + now := uint64(time.Now().UnixMilli()) + b := make([]byte, 12) + _, _ = rand.Read(b) + buf := make([]byte, 0, 34) + buf = strconv.AppendUint(buf, now, 36) + const chars = "0123456789abcdefghijklmnopqrstuvwxyz" + for _, v := range b { + buf = append(buf, chars[v&0x1f]) + buf = append(buf, chars[(v>>5)&0x1f]) + } + return string(buf) +} + +func generateULID() string { + now := uint64(time.Now().UnixMilli()) + b := make([]byte, 10) + _, _ = rand.Read(b) + bits := make([]byte, 16) + bits[0] = byte(now >> 40) + bits[1] = byte(now >> 32) + bits[2] = byte(now >> 24) + bits[3] = byte(now >> 16) + bits[4] = byte(now >> 8) + bits[5] = byte(now) + copy(bits[6:], b) + + const crockford = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" + var buf [26]byte + for i := 25; i >= 0; i-- { + bitOff := i * 5 + byteIdx := bitOff / 8 + byteOff := uint(bitOff % 8) + val := uint16(bits[byteIdx]) >> byteOff + if byteIdx+1 < 16 { + val |= uint16(bits[byteIdx+1]) << (8 - byteOff) + } + buf[i] = crockford[val&0x1f] + } + return string(buf[:]) +} + +func generateNanoID() string { + const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-" + b := make([]byte, 21) + _, _ = rand.Read(b) + for i := range b { + b[i] = alphabet[b[i]&0x3f] + } + return string(b) +} + // FieldError represents a single validation failure on a specific field. type FieldError struct { Field string diff --git a/generator/templates/model_create.gotpl b/generator/templates/model_create.gotpl index 0eef96b..9afb634 100644 --- a/generator/templates/model_create.gotpl +++ b/generator/templates/model_create.gotpl @@ -81,6 +81,10 @@ func validate{{ .Model.Name }}Create(assignments []FieldAssignment) error { errs.Add("{{ $field.Name }}", v, "range", "value is out of range for TinyInt (-128 to 127)") } {{- end }} + {{- else }} + if _, ok := a.Val.({{ trimPrefix $field.GoType "*" }}); !ok { + errs.Add("{{ $field.Name }}", a.Val, "type", "field {{ $field.Name }} must be of type {{ trimPrefix $field.GoType "*" }}") + } {{- end }} {{- else }} if _, ok := a.Val.({{ trimPrefix $field.GoType "*" }}); !ok { @@ -106,6 +110,12 @@ func validate{{ .Model.Name }}Create(assignments []FieldAssignment) error { errs.Add("{{ $field.Name }}", "", "required", "field {{ $fieldName }} is required") } {{- end }} + {{- else }} + {{- if and (eq $field.Default nil) (not $field.Optional) (not $field.IsArray) }} + if !provided["{{ $col }}"] { + errs.Add("{{ $field.Name }}", nil, "required", "field {{ $fieldName }} is required") + } + {{- end }} {{- end }} {{- end }} diff --git a/integration/schema.prisma b/integration/schema.prisma index 2cf8057..7fab3d1 100644 --- a/integration/schema.prisma +++ b/integration/schema.prisma @@ -74,3 +74,77 @@ model CategoryToPost { @@id([postId, categoryId]) } + +model DefaultsTest { + uuid4 String @id @default(uuid(4)) + uuid7 String @default(uuid(7)) + uuidNoArgs String @default(uuid()) + cuid1 String @default(cuid(1)) + cuid2 String @default(cuid(2)) + cuidNoArgs String @default(cuid()) + ulid String @default(ulid()) + nanoid String @default(nanoid()) + now DateTime @default(now()) +} + +model AllFieldsSoFar { + id Int @id @default(autoincrement()) + + // String + stringReq String + stringOpt String? + stringDefault String @default("default") + stringVarchar String + stringChar String + + // Default function variants + cuidDefault String @default(cuid()) + cuid1Default String @default(cuid(1)) + cuid2Default String @default(cuid(2)) + uuidDefault String @default(uuid()) + uuid4Default String @default(uuid(4)) + uuid7Default String @default(uuid(7)) + ulidDefault String @default(ulid()) + nanoidDefault String @default(nanoid()) + uuidDb String + + // Int + intReq Int + intOpt Int? + intDefault Int @default(42) + smallInt Int + tinyInt Int + + // BigInt + bigIntReq BigInt + bigIntOpt BigInt? + + // Float + floatReq Float + floatOpt Float? + + // Decimal + decimalReq Decimal + decimalOpt Decimal? + decimalPrecise Decimal + + // Boolean + boolReq Boolean + boolOpt Boolean? + boolDefault Boolean @default(false) + + // DateTime + dateTimeReq DateTime + dateTimeOpt DateTime? + dateTimeDefault DateTime @default(now()) + updatedAt DateTime @updatedAt + dateTimeTz DateTime + + // Json + jsonReq Json + jsonOpt Json? + + // Bytes + bytesReq Bytes + bytesOpt Bytes? +} diff --git a/integration/valk/allFieldsSoFar.go b/integration/valk/allFieldsSoFar.go new file mode 100644 index 0000000..c2214fb --- /dev/null +++ b/integration/valk/allFieldsSoFar.go @@ -0,0 +1,1409 @@ +package valk + +import ( + "context" + "encoding/json" + "fmt" + "slices" + "strings" + "time" + "unicode/utf8" +) + +// AllFieldsSoFar represents the database model +type AllFieldsSoFar struct { + Id int32 `db:"id" json:"id"` + StringReq string `db:"stringReq" json:"stringReq"` + StringOpt *string `db:"stringOpt" json:"stringOpt,omitempty"` + StringDefault string `db:"stringDefault" json:"stringDefault"` + StringVarchar string `db:"stringVarchar" json:"stringVarchar"` + StringChar string `db:"stringChar" json:"stringChar"` + CuidDefault string `db:"cuidDefault" json:"cuidDefault"` + Cuid1Default string `db:"cuid1Default" json:"cuid1Default"` + Cuid2Default string `db:"cuid2Default" json:"cuid2Default"` + UuidDefault string `db:"uuidDefault" json:"uuidDefault"` + Uuid4Default string `db:"uuid4Default" json:"uuid4Default"` + Uuid7Default string `db:"uuid7Default" json:"uuid7Default"` + UlidDefault string `db:"ulidDefault" json:"ulidDefault"` + NanoidDefault string `db:"nanoidDefault" json:"nanoidDefault"` + UuidDb string `db:"uuidDb" json:"uuidDb"` + IntReq int32 `db:"intReq" json:"intReq"` + IntOpt *int32 `db:"intOpt" json:"intOpt,omitempty"` + IntDefault int32 `db:"intDefault" json:"intDefault"` + SmallInt int32 `db:"smallInt" json:"smallInt"` + TinyInt int32 `db:"tinyInt" json:"tinyInt"` + BigIntReq int64 `db:"bigIntReq" json:"bigIntReq"` + BigIntOpt *int64 `db:"bigIntOpt" json:"bigIntOpt,omitempty"` + FloatReq float64 `db:"floatReq" json:"floatReq"` + FloatOpt *float64 `db:"floatOpt" json:"floatOpt,omitempty"` + DecimalReq string `db:"decimalReq" json:"decimalReq"` + DecimalOpt *string `db:"decimalOpt" json:"decimalOpt,omitempty"` + DecimalPrecise string `db:"decimalPrecise" json:"decimalPrecise"` + BoolReq bool `db:"boolReq" json:"boolReq"` + BoolOpt *bool `db:"boolOpt" json:"boolOpt,omitempty"` + BoolDefault bool `db:"boolDefault" json:"boolDefault"` + DateTimeReq time.Time `db:"dateTimeReq" json:"dateTimeReq"` + DateTimeOpt *time.Time `db:"dateTimeOpt" json:"dateTimeOpt,omitempty"` + DateTimeDefault time.Time `db:"dateTimeDefault" json:"dateTimeDefault"` + UpdatedAt time.Time `db:"updatedAt" json:"updatedAt"` + DateTimeTz time.Time `db:"dateTimeTz" json:"dateTimeTz"` + JsonReq json.RawMessage `db:"jsonReq" json:"jsonReq"` + JsonOpt *json.RawMessage `db:"jsonOpt" json:"jsonOpt,omitempty"` + BytesReq []byte `db:"bytesReq" json:"bytesReq"` + BytesOpt *[]byte `db:"bytesOpt" json:"bytesOpt,omitempty"` +} + +// AllFieldsSoFarCreate is used for hooks only — the Create API uses FieldAssignment +type AllFieldsSoFarCreate struct { + Id *int32 `json:"id"` + StringReq string `json:"stringReq"` + StringOpt *string `json:"stringOpt"` + StringDefault *string `json:"stringDefault"` + StringVarchar string `json:"stringVarchar"` + StringChar string `json:"stringChar"` + CuidDefault *string `json:"cuidDefault"` + Cuid1Default *string `json:"cuid1Default"` + Cuid2Default *string `json:"cuid2Default"` + UuidDefault *string `json:"uuidDefault"` + Uuid4Default *string `json:"uuid4Default"` + Uuid7Default *string `json:"uuid7Default"` + UlidDefault *string `json:"ulidDefault"` + NanoidDefault *string `json:"nanoidDefault"` + UuidDb string `json:"uuidDb"` + IntReq int32 `json:"intReq"` + IntOpt *int32 `json:"intOpt"` + IntDefault *int32 `json:"intDefault"` + SmallInt int32 `json:"smallInt"` + TinyInt int32 `json:"tinyInt"` + BigIntReq int64 `json:"bigIntReq"` + BigIntOpt *int64 `json:"bigIntOpt"` + FloatReq float64 `json:"floatReq"` + FloatOpt *float64 `json:"floatOpt"` + DecimalReq string `json:"decimalReq"` + DecimalOpt *string `json:"decimalOpt"` + DecimalPrecise string `json:"decimalPrecise"` + BoolReq bool `json:"boolReq"` + BoolOpt *bool `json:"boolOpt"` + BoolDefault *bool `json:"boolDefault"` + DateTimeReq time.Time `json:"dateTimeReq"` + DateTimeOpt *time.Time `json:"dateTimeOpt"` + DateTimeDefault *time.Time `json:"dateTimeDefault"` + UpdatedAt time.Time `json:"updatedAt"` + DateTimeTz time.Time `json:"dateTimeTz"` + JsonReq json.RawMessage `json:"jsonReq"` + JsonOpt *json.RawMessage `json:"jsonOpt"` + BytesReq []byte `json:"bytesReq"` + BytesOpt *[]byte `json:"bytesOpt"` +} + +// AllFieldsSoFarSelect specifies which fields to include +type AllFieldsSoFarSelect struct { + Id bool `json:"id"` + StringReq bool `json:"stringReq"` + StringOpt bool `json:"stringOpt"` + StringDefault bool `json:"stringDefault"` + StringVarchar bool `json:"stringVarchar"` + StringChar bool `json:"stringChar"` + CuidDefault bool `json:"cuidDefault"` + Cuid1Default bool `json:"cuid1Default"` + Cuid2Default bool `json:"cuid2Default"` + UuidDefault bool `json:"uuidDefault"` + Uuid4Default bool `json:"uuid4Default"` + Uuid7Default bool `json:"uuid7Default"` + UlidDefault bool `json:"ulidDefault"` + NanoidDefault bool `json:"nanoidDefault"` + UuidDb bool `json:"uuidDb"` + IntReq bool `json:"intReq"` + IntOpt bool `json:"intOpt"` + IntDefault bool `json:"intDefault"` + SmallInt bool `json:"smallInt"` + TinyInt bool `json:"tinyInt"` + BigIntReq bool `json:"bigIntReq"` + BigIntOpt bool `json:"bigIntOpt"` + FloatReq bool `json:"floatReq"` + FloatOpt bool `json:"floatOpt"` + DecimalReq bool `json:"decimalReq"` + DecimalOpt bool `json:"decimalOpt"` + DecimalPrecise bool `json:"decimalPrecise"` + BoolReq bool `json:"boolReq"` + BoolOpt bool `json:"boolOpt"` + BoolDefault bool `json:"boolDefault"` + DateTimeReq bool `json:"dateTimeReq"` + DateTimeOpt bool `json:"dateTimeOpt"` + DateTimeDefault bool `json:"dateTimeDefault"` + UpdatedAt bool `json:"updatedAt"` + DateTimeTz bool `json:"dateTimeTz"` + JsonReq bool `json:"jsonReq"` + JsonOpt bool `json:"jsonOpt"` + BytesReq bool `json:"bytesReq"` + BytesOpt bool `json:"bytesOpt"` +} + +// AllFieldsSoFarOmit specifies which fields to exclude +type AllFieldsSoFarOmit struct { + Id bool `json:"id"` + StringReq bool `json:"stringReq"` + StringOpt bool `json:"stringOpt"` + StringDefault bool `json:"stringDefault"` + StringVarchar bool `json:"stringVarchar"` + StringChar bool `json:"stringChar"` + CuidDefault bool `json:"cuidDefault"` + Cuid1Default bool `json:"cuid1Default"` + Cuid2Default bool `json:"cuid2Default"` + UuidDefault bool `json:"uuidDefault"` + Uuid4Default bool `json:"uuid4Default"` + Uuid7Default bool `json:"uuid7Default"` + UlidDefault bool `json:"ulidDefault"` + NanoidDefault bool `json:"nanoidDefault"` + UuidDb bool `json:"uuidDb"` + IntReq bool `json:"intReq"` + IntOpt bool `json:"intOpt"` + IntDefault bool `json:"intDefault"` + SmallInt bool `json:"smallInt"` + TinyInt bool `json:"tinyInt"` + BigIntReq bool `json:"bigIntReq"` + BigIntOpt bool `json:"bigIntOpt"` + FloatReq bool `json:"floatReq"` + FloatOpt bool `json:"floatOpt"` + DecimalReq bool `json:"decimalReq"` + DecimalOpt bool `json:"decimalOpt"` + DecimalPrecise bool `json:"decimalPrecise"` + BoolReq bool `json:"boolReq"` + BoolOpt bool `json:"boolOpt"` + BoolDefault bool `json:"boolDefault"` + DateTimeReq bool `json:"dateTimeReq"` + DateTimeOpt bool `json:"dateTimeOpt"` + DateTimeDefault bool `json:"dateTimeDefault"` + UpdatedAt bool `json:"updatedAt"` + DateTimeTz bool `json:"dateTimeTz"` + JsonReq bool `json:"jsonReq"` + JsonOpt bool `json:"jsonOpt"` + BytesReq bool `json:"bytesReq"` + BytesOpt bool `json:"bytesOpt"` +} + +type AllFieldsSoFarDelegate struct { + client *Queries + beforeCreate func(context.Context, *AllFieldsSoFarCreate) error + afterCreate func(context.Context, []*AllFieldsSoFar) error + afterCreateMany func(context.Context, []AllFieldsSoFarCreate, int64) error +} + +func (d *AllFieldsSoFarDelegate) BeforeCreate(hook func(context.Context, *AllFieldsSoFarCreate) error) { + d.beforeCreate = hook +} + +func (d *AllFieldsSoFarDelegate) AfterCreate(hook func(context.Context, []*AllFieldsSoFar) error) { + d.afterCreate = hook +} + +func (d *AllFieldsSoFarDelegate) AfterCreateMany(hook func(context.Context, []AllFieldsSoFarCreate, int64) error) { + d.afterCreateMany = hook +} + +func (m *AllFieldsSoFar) ScanFields(cols []string) []any { + targets := make([]any, len(cols)) + for i, col := range cols { + switch col { + case "id": + targets[i] = &m.Id + case "stringReq": + targets[i] = &m.StringReq + case "stringOpt": + targets[i] = &m.StringOpt + case "stringDefault": + targets[i] = &m.StringDefault + case "stringVarchar": + targets[i] = &m.StringVarchar + case "stringChar": + targets[i] = &m.StringChar + case "cuidDefault": + targets[i] = &m.CuidDefault + case "cuid1Default": + targets[i] = &m.Cuid1Default + case "cuid2Default": + targets[i] = &m.Cuid2Default + case "uuidDefault": + targets[i] = &m.UuidDefault + case "uuid4Default": + targets[i] = &m.Uuid4Default + case "uuid7Default": + targets[i] = &m.Uuid7Default + case "ulidDefault": + targets[i] = &m.UlidDefault + case "nanoidDefault": + targets[i] = &m.NanoidDefault + case "uuidDb": + targets[i] = &m.UuidDb + case "intReq": + targets[i] = &m.IntReq + case "intOpt": + targets[i] = &m.IntOpt + case "intDefault": + targets[i] = &m.IntDefault + case "smallInt": + targets[i] = &m.SmallInt + case "tinyInt": + targets[i] = &m.TinyInt + case "bigIntReq": + targets[i] = &m.BigIntReq + case "bigIntOpt": + targets[i] = &m.BigIntOpt + case "floatReq": + targets[i] = &m.FloatReq + case "floatOpt": + targets[i] = &m.FloatOpt + case "decimalReq": + targets[i] = &m.DecimalReq + case "decimalOpt": + targets[i] = &m.DecimalOpt + case "decimalPrecise": + targets[i] = &m.DecimalPrecise + case "boolReq": + targets[i] = &m.BoolReq + case "boolOpt": + targets[i] = &m.BoolOpt + case "boolDefault": + targets[i] = &m.BoolDefault + case "dateTimeReq": + targets[i] = &m.DateTimeReq + case "dateTimeOpt": + targets[i] = &m.DateTimeOpt + case "dateTimeDefault": + targets[i] = &m.DateTimeDefault + case "updatedAt": + targets[i] = &m.UpdatedAt + case "dateTimeTz": + targets[i] = &m.DateTimeTz + case "jsonReq": + targets[i] = &m.JsonReq + case "jsonOpt": + targets[i] = &m.JsonOpt + case "bytesReq": + targets[i] = &m.BytesReq + case "bytesOpt": + targets[i] = &m.BytesOpt + } + } + return targets +} + +var allFieldsSoFarDefaultCols = []string{ + "id", + "stringReq", + "stringOpt", + "stringDefault", + "stringVarchar", + "stringChar", + "cuidDefault", + "cuid1Default", + "cuid2Default", + "uuidDefault", + "uuid4Default", + "uuid7Default", + "ulidDefault", + "nanoidDefault", + "uuidDb", + "intReq", + "intOpt", + "intDefault", + "smallInt", + "tinyInt", + "bigIntReq", + "bigIntOpt", + "floatReq", + "floatOpt", + "decimalReq", + "decimalOpt", + "decimalPrecise", + "boolReq", + "boolOpt", + "boolDefault", + "dateTimeReq", + "dateTimeOpt", + "dateTimeDefault", + "updatedAt", + "dateTimeTz", + "jsonReq", + "jsonOpt", + "bytesReq", + "bytesOpt", +} + +func (q *Queries) selectAllFieldsSoFarCols(selects *AllFieldsSoFarSelect, omits *AllFieldsSoFarOmit, forceCols ...string) []string { + if selects == nil && omits == nil && len(forceCols) == 0 { + return allFieldsSoFarDefaultCols + } + + anySelected := selects != nil && (selects.Id || selects.StringReq || selects.StringOpt || selects.StringDefault || selects.StringVarchar || selects.StringChar || selects.CuidDefault || selects.Cuid1Default || selects.Cuid2Default || selects.UuidDefault || selects.Uuid4Default || selects.Uuid7Default || selects.UlidDefault || selects.NanoidDefault || selects.UuidDb || selects.IntReq || selects.IntOpt || selects.IntDefault || selects.SmallInt || selects.TinyInt || selects.BigIntReq || selects.BigIntOpt || selects.FloatReq || selects.FloatOpt || selects.DecimalReq || selects.DecimalOpt || selects.DecimalPrecise || selects.BoolReq || selects.BoolOpt || selects.BoolDefault || selects.DateTimeReq || selects.DateTimeOpt || selects.DateTimeDefault || selects.UpdatedAt || selects.DateTimeTz || selects.JsonReq || selects.JsonOpt || selects.BytesReq || selects.BytesOpt) + + specs := []colSpec{ + {"id", selects != nil && selects.Id, omits != nil && omits.Id, selects != nil && selects.hasAnyRelation()}, + {"stringReq", selects != nil && selects.StringReq, omits != nil && omits.StringReq, false}, + {"stringOpt", selects != nil && selects.StringOpt, omits != nil && omits.StringOpt, false}, + {"stringDefault", selects != nil && selects.StringDefault, omits != nil && omits.StringDefault, false}, + {"stringVarchar", selects != nil && selects.StringVarchar, omits != nil && omits.StringVarchar, false}, + {"stringChar", selects != nil && selects.StringChar, omits != nil && omits.StringChar, false}, + {"cuidDefault", selects != nil && selects.CuidDefault, omits != nil && omits.CuidDefault, false}, + {"cuid1Default", selects != nil && selects.Cuid1Default, omits != nil && omits.Cuid1Default, false}, + {"cuid2Default", selects != nil && selects.Cuid2Default, omits != nil && omits.Cuid2Default, false}, + {"uuidDefault", selects != nil && selects.UuidDefault, omits != nil && omits.UuidDefault, false}, + {"uuid4Default", selects != nil && selects.Uuid4Default, omits != nil && omits.Uuid4Default, false}, + {"uuid7Default", selects != nil && selects.Uuid7Default, omits != nil && omits.Uuid7Default, false}, + {"ulidDefault", selects != nil && selects.UlidDefault, omits != nil && omits.UlidDefault, false}, + {"nanoidDefault", selects != nil && selects.NanoidDefault, omits != nil && omits.NanoidDefault, false}, + {"uuidDb", selects != nil && selects.UuidDb, omits != nil && omits.UuidDb, false}, + {"intReq", selects != nil && selects.IntReq, omits != nil && omits.IntReq, false}, + {"intOpt", selects != nil && selects.IntOpt, omits != nil && omits.IntOpt, false}, + {"intDefault", selects != nil && selects.IntDefault, omits != nil && omits.IntDefault, false}, + {"smallInt", selects != nil && selects.SmallInt, omits != nil && omits.SmallInt, false}, + {"tinyInt", selects != nil && selects.TinyInt, omits != nil && omits.TinyInt, false}, + {"bigIntReq", selects != nil && selects.BigIntReq, omits != nil && omits.BigIntReq, false}, + {"bigIntOpt", selects != nil && selects.BigIntOpt, omits != nil && omits.BigIntOpt, false}, + {"floatReq", selects != nil && selects.FloatReq, omits != nil && omits.FloatReq, false}, + {"floatOpt", selects != nil && selects.FloatOpt, omits != nil && omits.FloatOpt, false}, + {"decimalReq", selects != nil && selects.DecimalReq, omits != nil && omits.DecimalReq, false}, + {"decimalOpt", selects != nil && selects.DecimalOpt, omits != nil && omits.DecimalOpt, false}, + {"decimalPrecise", selects != nil && selects.DecimalPrecise, omits != nil && omits.DecimalPrecise, false}, + {"boolReq", selects != nil && selects.BoolReq, omits != nil && omits.BoolReq, false}, + {"boolOpt", selects != nil && selects.BoolOpt, omits != nil && omits.BoolOpt, false}, + {"boolDefault", selects != nil && selects.BoolDefault, omits != nil && omits.BoolDefault, false}, + {"dateTimeReq", selects != nil && selects.DateTimeReq, omits != nil && omits.DateTimeReq, false}, + {"dateTimeOpt", selects != nil && selects.DateTimeOpt, omits != nil && omits.DateTimeOpt, false}, + {"dateTimeDefault", selects != nil && selects.DateTimeDefault, omits != nil && omits.DateTimeDefault, false}, + {"updatedAt", selects != nil && selects.UpdatedAt, omits != nil && omits.UpdatedAt, false}, + {"dateTimeTz", selects != nil && selects.DateTimeTz, omits != nil && omits.DateTimeTz, false}, + {"jsonReq", selects != nil && selects.JsonReq, omits != nil && omits.JsonReq, false}, + {"jsonOpt", selects != nil && selects.JsonOpt, omits != nil && omits.JsonOpt, false}, + {"bytesReq", selects != nil && selects.BytesReq, omits != nil && omits.BytesReq, false}, + {"bytesOpt", selects != nil && selects.BytesOpt, omits != nil && omits.BytesOpt, false}, + } + + cols := computeCols(specs, selects != nil, anySelected) + + for _, f := range forceCols { + if !slices.Contains(cols, f) { + cols = append(cols, f) + } + } + + return cols +} + +var AllFieldsSoFarColOrder = []string{ + "id", + "stringReq", + "stringOpt", + "stringDefault", + "stringVarchar", + "stringChar", + "cuidDefault", + "cuid1Default", + "cuid2Default", + "uuidDefault", + "uuid4Default", + "uuid7Default", + "ulidDefault", + "nanoidDefault", + "uuidDb", + "intReq", + "intOpt", + "intDefault", + "smallInt", + "tinyInt", + "bigIntReq", + "bigIntOpt", + "floatReq", + "floatOpt", + "decimalReq", + "decimalOpt", + "decimalPrecise", + "boolReq", + "boolOpt", + "boolDefault", + "dateTimeReq", + "dateTimeOpt", + "dateTimeDefault", + "updatedAt", + "dateTimeTz", + "jsonReq", + "jsonOpt", + "bytesReq", + "bytesOpt", +} + +func (s *AllFieldsSoFarSelect) hasAnyRelation() bool { + if s == nil { + return false + } + return false +} + +func (d *AllFieldsSoFarDelegate) Create(assignments ...FieldAssignment) *CreateBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit] { + return &CreateBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit]{ + client: d.client, + assignments: assignments, + execFunc: d.client.executeAllFieldsSoFarCreate, + } +} + +func validateAllFieldsSoFarCreate(assignments []FieldAssignment) error { + errs := &ValidationError{} + + provided := make(map[string]bool) + for _, a := range assignments { + provided[a.Col] = true + switch a.Col { + case "id": + if _, ok := a.Val.(int32); !ok { + errs.Add("id", a.Val, "type", "field id must be of type int32") + } + case "stringReq": + if v, ok := a.Val.(string); ok { + if v == "" { + errs.Add("stringReq", v, "required", "field stringReq is required") + } + if strings.Contains(v, "\x00") { + errs.Add("stringReq", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("stringReq", v, "safety", "string must be valid UTF-8") + } + } + case "stringOpt": + if _, ok := a.Val.(string); !ok { + errs.Add("stringOpt", a.Val, "type", "field stringOpt must be of type string") + } + case "stringDefault": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("stringDefault", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("stringDefault", v, "safety", "string must be valid UTF-8") + } + } + case "stringVarchar": + if v, ok := a.Val.(string); ok { + if v == "" { + errs.Add("stringVarchar", v, "required", "field stringVarchar is required") + } + if strings.Contains(v, "\x00") { + errs.Add("stringVarchar", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("stringVarchar", v, "safety", "string must be valid UTF-8") + } + } + case "stringChar": + if v, ok := a.Val.(string); ok { + if v == "" { + errs.Add("stringChar", v, "required", "field stringChar is required") + } + if strings.Contains(v, "\x00") { + errs.Add("stringChar", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("stringChar", v, "safety", "string must be valid UTF-8") + } + } + case "cuidDefault": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("cuidDefault", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("cuidDefault", v, "safety", "string must be valid UTF-8") + } + } + case "cuid1Default": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("cuid1Default", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("cuid1Default", v, "safety", "string must be valid UTF-8") + } + } + case "cuid2Default": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("cuid2Default", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("cuid2Default", v, "safety", "string must be valid UTF-8") + } + } + case "uuidDefault": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("uuidDefault", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("uuidDefault", v, "safety", "string must be valid UTF-8") + } + } + case "uuid4Default": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("uuid4Default", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("uuid4Default", v, "safety", "string must be valid UTF-8") + } + } + case "uuid7Default": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("uuid7Default", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("uuid7Default", v, "safety", "string must be valid UTF-8") + } + } + case "ulidDefault": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("ulidDefault", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("ulidDefault", v, "safety", "string must be valid UTF-8") + } + } + case "nanoidDefault": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("nanoidDefault", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("nanoidDefault", v, "safety", "string must be valid UTF-8") + } + } + case "uuidDb": + if v, ok := a.Val.(string); ok { + if v == "" { + errs.Add("uuidDb", v, "required", "field uuidDb is required") + } + if strings.Contains(v, "\x00") { + errs.Add("uuidDb", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("uuidDb", v, "safety", "string must be valid UTF-8") + } + } + case "intReq": + if _, ok := a.Val.(int32); !ok { + errs.Add("intReq", a.Val, "type", "field intReq must be of type int32") + } + case "intOpt": + if _, ok := a.Val.(int32); !ok { + errs.Add("intOpt", a.Val, "type", "field intOpt must be of type int32") + } + case "intDefault": + if _, ok := a.Val.(int32); !ok { + errs.Add("intDefault", a.Val, "type", "field intDefault must be of type int32") + } + case "smallInt": + if _, ok := a.Val.(int32); !ok { + errs.Add("smallInt", a.Val, "type", "field smallInt must be of type int32") + } + case "tinyInt": + if _, ok := a.Val.(int32); !ok { + errs.Add("tinyInt", a.Val, "type", "field tinyInt must be of type int32") + } + case "bigIntReq": + if _, ok := a.Val.(int64); !ok { + errs.Add("bigIntReq", a.Val, "type", "field bigIntReq must be of type int64") + } + case "bigIntOpt": + if _, ok := a.Val.(int64); !ok { + errs.Add("bigIntOpt", a.Val, "type", "field bigIntOpt must be of type int64") + } + case "floatReq": + if _, ok := a.Val.(float64); !ok { + errs.Add("floatReq", a.Val, "type", "field floatReq must be of type float64") + } + case "floatOpt": + if _, ok := a.Val.(float64); !ok { + errs.Add("floatOpt", a.Val, "type", "field floatOpt must be of type float64") + } + case "decimalReq": + if v, ok := a.Val.(string); ok { + if v == "" { + errs.Add("decimalReq", v, "required", "field decimalReq is required") + } + if strings.Contains(v, "\x00") { + errs.Add("decimalReq", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("decimalReq", v, "safety", "string must be valid UTF-8") + } + } + case "decimalOpt": + if _, ok := a.Val.(string); !ok { + errs.Add("decimalOpt", a.Val, "type", "field decimalOpt must be of type string") + } + case "decimalPrecise": + if v, ok := a.Val.(string); ok { + if v == "" { + errs.Add("decimalPrecise", v, "required", "field decimalPrecise is required") + } + if strings.Contains(v, "\x00") { + errs.Add("decimalPrecise", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("decimalPrecise", v, "safety", "string must be valid UTF-8") + } + } + case "boolReq": + if _, ok := a.Val.(bool); !ok { + errs.Add("boolReq", a.Val, "type", "field boolReq must be of type bool") + } + case "boolOpt": + if _, ok := a.Val.(bool); !ok { + errs.Add("boolOpt", a.Val, "type", "field boolOpt must be of type bool") + } + case "boolDefault": + if _, ok := a.Val.(bool); !ok { + errs.Add("boolDefault", a.Val, "type", "field boolDefault must be of type bool") + } + case "dateTimeReq": + if _, ok := a.Val.(time.Time); !ok { + errs.Add("dateTimeReq", a.Val, "type", "field dateTimeReq must be of type time.Time") + } + case "dateTimeOpt": + if _, ok := a.Val.(time.Time); !ok { + errs.Add("dateTimeOpt", a.Val, "type", "field dateTimeOpt must be of type time.Time") + } + case "dateTimeDefault": + if _, ok := a.Val.(time.Time); !ok { + errs.Add("dateTimeDefault", a.Val, "type", "field dateTimeDefault must be of type time.Time") + } + case "updatedAt": + if _, ok := a.Val.(time.Time); !ok { + errs.Add("updatedAt", a.Val, "type", "field updatedAt must be of type time.Time") + } + case "dateTimeTz": + if _, ok := a.Val.(time.Time); !ok { + errs.Add("dateTimeTz", a.Val, "type", "field dateTimeTz must be of type time.Time") + } + case "jsonReq": + if _, ok := a.Val.(json.RawMessage); !ok { + errs.Add("jsonReq", a.Val, "type", "field jsonReq must be of type json.RawMessage") + } + case "jsonOpt": + if _, ok := a.Val.(json.RawMessage); !ok { + errs.Add("jsonOpt", a.Val, "type", "field jsonOpt must be of type json.RawMessage") + } + case "bytesReq": + if _, ok := a.Val.([]byte); !ok { + errs.Add("bytesReq", a.Val, "type", "field bytesReq must be of type []byte") + } + case "bytesOpt": + if _, ok := a.Val.([]byte); !ok { + errs.Add("bytesOpt", a.Val, "type", "field bytesOpt must be of type []byte") + } + } + } + if !provided["stringReq"] { + errs.Add("stringReq", "", "required", "field StringReq is required") + } + if !provided["stringVarchar"] { + errs.Add("stringVarchar", "", "required", "field StringVarchar is required") + } + if !provided["stringChar"] { + errs.Add("stringChar", "", "required", "field StringChar is required") + } + if !provided["uuidDb"] { + errs.Add("uuidDb", "", "required", "field UuidDb is required") + } + if !provided["intReq"] { + errs.Add("intReq", nil, "required", "field IntReq is required") + } + if !provided["smallInt"] { + errs.Add("smallInt", nil, "required", "field SmallInt is required") + } + if !provided["tinyInt"] { + errs.Add("tinyInt", nil, "required", "field TinyInt is required") + } + if !provided["bigIntReq"] { + errs.Add("bigIntReq", nil, "required", "field BigIntReq is required") + } + if !provided["floatReq"] { + errs.Add("floatReq", nil, "required", "field FloatReq is required") + } + if !provided["decimalReq"] { + errs.Add("decimalReq", "", "required", "field DecimalReq is required") + } + if !provided["decimalPrecise"] { + errs.Add("decimalPrecise", "", "required", "field DecimalPrecise is required") + } + if !provided["boolReq"] { + errs.Add("boolReq", nil, "required", "field BoolReq is required") + } + if !provided["dateTimeReq"] { + errs.Add("dateTimeReq", nil, "required", "field DateTimeReq is required") + } + if !provided["updatedAt"] { + errs.Add("updatedAt", nil, "required", "field UpdatedAt is required") + } + if !provided["dateTimeTz"] { + errs.Add("dateTimeTz", nil, "required", "field DateTimeTz is required") + } + if !provided["jsonReq"] { + errs.Add("jsonReq", nil, "required", "field JsonReq is required") + } + if !provided["bytesReq"] { + errs.Add("bytesReq", nil, "required", "field BytesReq is required") + } + + if errs.HasErrors() { + return *errs + } + return nil +} + +func assignmentsToAllFieldsSoFarCreate(assignments []FieldAssignment) AllFieldsSoFarCreate { + var input AllFieldsSoFarCreate + for _, a := range assignments { + switch a.Col { + case "id": + if v, ok := a.Val.(int32); ok { + input.Id = &v + } + case "stringReq": + if v, ok := a.Val.(string); ok { + input.StringReq = v + } + case "stringOpt": + if v, ok := a.Val.(string); ok { + input.StringOpt = &v + } + case "stringDefault": + if v, ok := a.Val.(string); ok { + input.StringDefault = &v + } + case "stringVarchar": + if v, ok := a.Val.(string); ok { + input.StringVarchar = v + } + case "stringChar": + if v, ok := a.Val.(string); ok { + input.StringChar = v + } + case "cuidDefault": + if v, ok := a.Val.(string); ok { + input.CuidDefault = &v + } + case "cuid1Default": + if v, ok := a.Val.(string); ok { + input.Cuid1Default = &v + } + case "cuid2Default": + if v, ok := a.Val.(string); ok { + input.Cuid2Default = &v + } + case "uuidDefault": + if v, ok := a.Val.(string); ok { + input.UuidDefault = &v + } + case "uuid4Default": + if v, ok := a.Val.(string); ok { + input.Uuid4Default = &v + } + case "uuid7Default": + if v, ok := a.Val.(string); ok { + input.Uuid7Default = &v + } + case "ulidDefault": + if v, ok := a.Val.(string); ok { + input.UlidDefault = &v + } + case "nanoidDefault": + if v, ok := a.Val.(string); ok { + input.NanoidDefault = &v + } + case "uuidDb": + if v, ok := a.Val.(string); ok { + input.UuidDb = v + } + case "intReq": + if v, ok := a.Val.(int32); ok { + input.IntReq = v + } + case "intOpt": + if v, ok := a.Val.(int32); ok { + input.IntOpt = &v + } + case "intDefault": + if v, ok := a.Val.(int32); ok { + input.IntDefault = &v + } + case "smallInt": + if v, ok := a.Val.(int32); ok { + input.SmallInt = v + } + case "tinyInt": + if v, ok := a.Val.(int32); ok { + input.TinyInt = v + } + case "bigIntReq": + if v, ok := a.Val.(int64); ok { + input.BigIntReq = v + } + case "bigIntOpt": + if v, ok := a.Val.(int64); ok { + input.BigIntOpt = &v + } + case "floatReq": + if v, ok := a.Val.(float64); ok { + input.FloatReq = v + } + case "floatOpt": + if v, ok := a.Val.(float64); ok { + input.FloatOpt = &v + } + case "decimalReq": + if v, ok := a.Val.(string); ok { + input.DecimalReq = v + } + case "decimalOpt": + if v, ok := a.Val.(string); ok { + input.DecimalOpt = &v + } + case "decimalPrecise": + if v, ok := a.Val.(string); ok { + input.DecimalPrecise = v + } + case "boolReq": + if v, ok := a.Val.(bool); ok { + input.BoolReq = v + } + case "boolOpt": + if v, ok := a.Val.(bool); ok { + input.BoolOpt = &v + } + case "boolDefault": + if v, ok := a.Val.(bool); ok { + input.BoolDefault = &v + } + case "dateTimeReq": + if v, ok := a.Val.(time.Time); ok { + input.DateTimeReq = v + } + case "dateTimeOpt": + if v, ok := a.Val.(time.Time); ok { + input.DateTimeOpt = &v + } + case "dateTimeDefault": + if v, ok := a.Val.(time.Time); ok { + input.DateTimeDefault = &v + } + case "updatedAt": + if v, ok := a.Val.(time.Time); ok { + input.UpdatedAt = v + } + case "dateTimeTz": + if v, ok := a.Val.(time.Time); ok { + input.DateTimeTz = v + } + case "jsonReq": + if v, ok := a.Val.(json.RawMessage); ok { + input.JsonReq = v + } + case "jsonOpt": + if v, ok := a.Val.(json.RawMessage); ok { + input.JsonOpt = &v + } + case "bytesReq": + if v, ok := a.Val.([]byte); ok { + input.BytesReq = v + } + case "bytesOpt": + if v, ok := a.Val.([]byte); ok { + input.BytesOpt = &v + } + } + } + return input +} + +func (s *AllFieldsSoFarCreate) ToRowMap() map[string]any { + m := make(map[string]any, 39) + if s.Id != nil { + m["id"] = *s.Id + } + m["stringReq"] = s.StringReq + if s.StringOpt != nil { + m["stringOpt"] = *s.StringOpt + } + if s.StringDefault != nil { + m["stringDefault"] = *s.StringDefault + } + m["stringVarchar"] = s.StringVarchar + m["stringChar"] = s.StringChar + if s.CuidDefault != nil { + m["cuidDefault"] = *s.CuidDefault + } else { + m["cuidDefault"] = generateCUID() + } + if s.Cuid1Default != nil { + m["cuid1Default"] = *s.Cuid1Default + } else { + m["cuid1Default"] = generateCUID() + } + if s.Cuid2Default != nil { + m["cuid2Default"] = *s.Cuid2Default + } else { + m["cuid2Default"] = generateCUID2() + } + if s.UuidDefault != nil { + m["uuidDefault"] = *s.UuidDefault + } else { + m["uuidDefault"] = generateUUID() + } + if s.Uuid4Default != nil { + m["uuid4Default"] = *s.Uuid4Default + } else { + m["uuid4Default"] = generateUUID() + } + if s.Uuid7Default != nil { + m["uuid7Default"] = *s.Uuid7Default + } else { + m["uuid7Default"] = generateUUID7() + } + if s.UlidDefault != nil { + m["ulidDefault"] = *s.UlidDefault + } else { + m["ulidDefault"] = generateULID() + } + if s.NanoidDefault != nil { + m["nanoidDefault"] = *s.NanoidDefault + } else { + m["nanoidDefault"] = generateNanoID() + } + m["uuidDb"] = s.UuidDb + m["intReq"] = s.IntReq + if s.IntOpt != nil { + m["intOpt"] = *s.IntOpt + } + if s.IntDefault != nil { + m["intDefault"] = *s.IntDefault + } + m["smallInt"] = s.SmallInt + m["tinyInt"] = s.TinyInt + m["bigIntReq"] = s.BigIntReq + if s.BigIntOpt != nil { + m["bigIntOpt"] = *s.BigIntOpt + } + m["floatReq"] = s.FloatReq + if s.FloatOpt != nil { + m["floatOpt"] = *s.FloatOpt + } + m["decimalReq"] = s.DecimalReq + if s.DecimalOpt != nil { + m["decimalOpt"] = *s.DecimalOpt + } + m["decimalPrecise"] = s.DecimalPrecise + m["boolReq"] = s.BoolReq + if s.BoolOpt != nil { + m["boolOpt"] = *s.BoolOpt + } + if s.BoolDefault != nil { + m["boolDefault"] = *s.BoolDefault + } + m["dateTimeReq"] = s.DateTimeReq + if s.DateTimeOpt != nil { + m["dateTimeOpt"] = *s.DateTimeOpt + } + if s.DateTimeDefault != nil { + m["dateTimeDefault"] = *s.DateTimeDefault + } else { + m["dateTimeDefault"] = time.Now() + } + m["updatedAt"] = s.UpdatedAt + m["dateTimeTz"] = s.DateTimeTz + m["jsonReq"] = s.JsonReq + if s.JsonOpt != nil { + m["jsonOpt"] = *s.JsonOpt + } + m["bytesReq"] = s.BytesReq + if s.BytesOpt != nil { + m["bytesOpt"] = *s.BytesOpt + } + return m +} + +func (q *Queries) executeAllFieldsSoFarCreate(ctx context.Context, assignments []FieldAssignment, selects *AllFieldsSoFarSelect, omits *AllFieldsSoFarOmit) (*AllFieldsSoFar, error) { + input := assignmentsToAllFieldsSoFarCreate(assignments) + + if q.AllFieldsSoFar.beforeCreate != nil { + if err := q.AllFieldsSoFar.beforeCreate(ctx, &input); err != nil { + return nil, err + } + } + + if err := validateAllFieldsSoFarCreate(assignments); err != nil { + return nil, err + } + + var cols []string + var vals []any + if input.Id != nil { + cols = append(cols, "id") + vals = append(vals, *input.Id) + } + cols = append(cols, "stringReq") + vals = append(vals, input.StringReq) + if input.StringOpt != nil { + cols = append(cols, "stringOpt") + vals = append(vals, *input.StringOpt) + } + if input.StringDefault != nil { + cols = append(cols, "stringDefault") + vals = append(vals, *input.StringDefault) + } + cols = append(cols, "stringVarchar") + vals = append(vals, input.StringVarchar) + cols = append(cols, "stringChar") + vals = append(vals, input.StringChar) + if input.CuidDefault != nil { + cols = append(cols, "cuidDefault") + vals = append(vals, *input.CuidDefault) + } else { + cols = append(cols, "cuidDefault") + vals = append(vals, generateCUID()) + } + if input.Cuid1Default != nil { + cols = append(cols, "cuid1Default") + vals = append(vals, *input.Cuid1Default) + } else { + cols = append(cols, "cuid1Default") + vals = append(vals, generateCUID()) + } + if input.Cuid2Default != nil { + cols = append(cols, "cuid2Default") + vals = append(vals, *input.Cuid2Default) + } else { + cols = append(cols, "cuid2Default") + vals = append(vals, generateCUID2()) + } + if input.UuidDefault != nil { + cols = append(cols, "uuidDefault") + vals = append(vals, *input.UuidDefault) + } else { + cols = append(cols, "uuidDefault") + vals = append(vals, generateUUID()) + } + if input.Uuid4Default != nil { + cols = append(cols, "uuid4Default") + vals = append(vals, *input.Uuid4Default) + } else { + cols = append(cols, "uuid4Default") + vals = append(vals, generateUUID()) + } + if input.Uuid7Default != nil { + cols = append(cols, "uuid7Default") + vals = append(vals, *input.Uuid7Default) + } else { + cols = append(cols, "uuid7Default") + vals = append(vals, generateUUID7()) + } + if input.UlidDefault != nil { + cols = append(cols, "ulidDefault") + vals = append(vals, *input.UlidDefault) + } else { + cols = append(cols, "ulidDefault") + vals = append(vals, generateULID()) + } + if input.NanoidDefault != nil { + cols = append(cols, "nanoidDefault") + vals = append(vals, *input.NanoidDefault) + } else { + cols = append(cols, "nanoidDefault") + vals = append(vals, generateNanoID()) + } + cols = append(cols, "uuidDb") + vals = append(vals, input.UuidDb) + cols = append(cols, "intReq") + vals = append(vals, input.IntReq) + if input.IntOpt != nil { + cols = append(cols, "intOpt") + vals = append(vals, *input.IntOpt) + } + if input.IntDefault != nil { + cols = append(cols, "intDefault") + vals = append(vals, *input.IntDefault) + } + cols = append(cols, "smallInt") + vals = append(vals, input.SmallInt) + cols = append(cols, "tinyInt") + vals = append(vals, input.TinyInt) + cols = append(cols, "bigIntReq") + vals = append(vals, input.BigIntReq) + if input.BigIntOpt != nil { + cols = append(cols, "bigIntOpt") + vals = append(vals, *input.BigIntOpt) + } + cols = append(cols, "floatReq") + vals = append(vals, input.FloatReq) + if input.FloatOpt != nil { + cols = append(cols, "floatOpt") + vals = append(vals, *input.FloatOpt) + } + cols = append(cols, "decimalReq") + vals = append(vals, input.DecimalReq) + if input.DecimalOpt != nil { + cols = append(cols, "decimalOpt") + vals = append(vals, *input.DecimalOpt) + } + cols = append(cols, "decimalPrecise") + vals = append(vals, input.DecimalPrecise) + cols = append(cols, "boolReq") + vals = append(vals, input.BoolReq) + if input.BoolOpt != nil { + cols = append(cols, "boolOpt") + vals = append(vals, *input.BoolOpt) + } + if input.BoolDefault != nil { + cols = append(cols, "boolDefault") + vals = append(vals, *input.BoolDefault) + } + cols = append(cols, "dateTimeReq") + vals = append(vals, input.DateTimeReq) + if input.DateTimeOpt != nil { + cols = append(cols, "dateTimeOpt") + vals = append(vals, *input.DateTimeOpt) + } + if input.DateTimeDefault != nil { + cols = append(cols, "dateTimeDefault") + vals = append(vals, *input.DateTimeDefault) + } else { + cols = append(cols, "dateTimeDefault") + vals = append(vals, time.Now()) + } + cols = append(cols, "updatedAt") + vals = append(vals, input.UpdatedAt) + cols = append(cols, "dateTimeTz") + vals = append(vals, input.DateTimeTz) + cols = append(cols, "jsonReq") + vals = append(vals, input.JsonReq) + if input.JsonOpt != nil { + cols = append(cols, "jsonOpt") + vals = append(vals, *input.JsonOpt) + } + cols = append(cols, "bytesReq") + vals = append(vals, input.BytesReq) + if input.BytesOpt != nil { + cols = append(cols, "bytesOpt") + vals = append(vals, *input.BytesOpt) + } + + returningCols := q.selectAllFieldsSoFarCols(selects, omits) + + scanFunc := func(res *AllFieldsSoFar, cols []string) []any { + return res.ScanFields(cols) + } + + idCol := "id" + + hasRelations := selects.hasAnyRelation() + + var res *AllFieldsSoFar + var err error + if hasRelations { + err = q.transaction(ctx, func(txQ *Queries) error { + var err error + res, err = executeInsert(ctx, txQ, "AllFieldsSoFar", cols, vals, returningCols, idCol, scanFunc) + if err != nil { + return err + } + return txQ.loadAllFieldsSoFarRelations(ctx, []*AllFieldsSoFar{res}, selects) + }) + } else { + res, err = executeInsert(ctx, q, "AllFieldsSoFar", cols, vals, returningCols, idCol, scanFunc) + } + if err != nil { + return nil, err + } + + if q.AllFieldsSoFar.afterCreate != nil { + if err := q.AllFieldsSoFar.afterCreate(ctx, []*AllFieldsSoFar{res}); err != nil { + return nil, err + } + } + + return res, nil +} + +func (d *AllFieldsSoFarDelegate) CreateMany(records ...RecordInput) *CreateManyBuilder[AllFieldsSoFar] { + return &CreateManyBuilder[AllFieldsSoFar]{ + client: d.client, + records: records, + execFunc: d.client.executeAllFieldsSoFarCreateMany, + } +} + +func (d *AllFieldsSoFarDelegate) CreateManyAndReturn(records ...RecordInput) *CreateManyAndReturnBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit] { + return &CreateManyAndReturnBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit]{ + client: d.client, + records: records, + execFunc: d.client.executeAllFieldsSoFarCreateManyAndReturn, + } +} + +func (q *Queries) executeAllFieldsSoFarCreateMany(ctx context.Context, records []RecordInput) (int64, error) { + rowMaps := make([]map[string]any, len(records)) + inputs := make([]AllFieldsSoFarCreate, len(records)) + for i, rec := range records { + if err := validateAllFieldsSoFarCreate(rec.Assignments); err != nil { + return 0, fmt.Errorf("validation failed at index %d: %w", i, err) + } + input := assignmentsToAllFieldsSoFarCreate(rec.Assignments) + if q.AllFieldsSoFar.beforeCreate != nil { + if err := q.AllFieldsSoFar.beforeCreate(ctx, &input); err != nil { + return 0, err + } + } + rowMaps[i] = input.ToRowMap() + inputs[i] = input + } + count, err := executeCreateMany(ctx, q, rowMaps, "AllFieldsSoFar", AllFieldsSoFarColOrder) + if err != nil { + return 0, err + } + if q.AllFieldsSoFar.afterCreateMany != nil { + if err := q.AllFieldsSoFar.afterCreateMany(ctx, inputs, count); err != nil { + return 0, err + } + } + return count, nil +} + +func (q *Queries) executeAllFieldsSoFarCreateManyAndReturn(ctx context.Context, records []RecordInput, selects *AllFieldsSoFarSelect, omits *AllFieldsSoFarOmit) ([]*AllFieldsSoFar, error) { + rowMaps := make([]map[string]any, len(records)) + idCol := "id" + for i, rec := range records { + if err := validateAllFieldsSoFarCreate(rec.Assignments); err != nil { + return nil, fmt.Errorf("validation failed at index %d: %w", i, err) + } + input := assignmentsToAllFieldsSoFarCreate(rec.Assignments) + if q.AllFieldsSoFar.beforeCreate != nil { + if err := q.AllFieldsSoFar.beforeCreate(ctx, &input); err != nil { + return nil, err + } + } + rowMaps[i] = input.ToRowMap() + } + results, err := executeCreateManyAndReturn(ctx, q, rowMaps, "AllFieldsSoFar", AllFieldsSoFarColOrder, selects, omits, + q.selectAllFieldsSoFarCols, + q.loadAllFieldsSoFarRelations, + (*AllFieldsSoFar).ScanFields, + (*AllFieldsSoFarSelect).hasAnyRelation, + idCol, + ) + if err != nil { + return nil, err + } + if q.AllFieldsSoFar.afterCreate != nil { + if err := q.AllFieldsSoFar.afterCreate(ctx, results); err != nil { + return nil, err + } + } + return results, nil +} +func (d *AllFieldsSoFarDelegate) FindUnique(where UniquePredicate) *FindUniqueBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit] { + return &FindUniqueBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit]{ + client: d.client, + where: where, + execFunc: d.client.executeAllFieldsSoFarFindUnique, + } +} + +func (d *AllFieldsSoFarDelegate) FindFirst(preds ...Predicate) *FindFirstBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit] { + return &FindFirstBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit]{ + client: d.client, + where: preds, + execFunc: d.client.executeAllFieldsSoFarFindFirst, + } +} + +func (d *AllFieldsSoFarDelegate) FindMany(preds ...Predicate) *FindManyBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit] { + return &FindManyBuilder[AllFieldsSoFar, AllFieldsSoFarSelect, AllFieldsSoFarOmit]{ + client: d.client, + where: preds, + execFunc: d.client.executeAllFieldsSoFarFindMany, + } +} + +func (q *Queries) executeAllFieldsSoFarFindUnique(ctx context.Context, where UniquePredicate, selects *AllFieldsSoFarSelect, omits *AllFieldsSoFarOmit) (*AllFieldsSoFar, error) { + if where == nil { + return nil, fmt.Errorf("at least one unique field must be set for FindUnique") + } + if err := where.Validate(); err != nil { + return nil, err + } + whereClause, vals := CompilePredicates(q.dialect, []Predicate{where}) + if whereClause != "" { + whereClause = " WHERE " + whereClause + } + returningCols := q.selectAllFieldsSoFarCols(selects, omits) + return executeSingleWithRelations(ctx, q, "AllFieldsSoFar", whereClause, vals, returningCols, + func(res *AllFieldsSoFar, cols []string) []any { return res.ScanFields(cols) }, + selects.hasAnyRelation(), + func(ctx context.Context, txQ *Queries, results []*AllFieldsSoFar) error { + return txQ.loadAllFieldsSoFarRelations(ctx, results, selects) + }, + ) +} + +func (q *Queries) executeAllFieldsSoFarFindFirst(ctx context.Context, where []Predicate, selects *AllFieldsSoFarSelect, omits *AllFieldsSoFarOmit) (*AllFieldsSoFar, error) { + for _, p := range where { + if p != nil { + if err := p.Validate(); err != nil { + return nil, err + } + } + } + whereClause, vals := CompilePredicates(q.dialect, where) + if whereClause != "" { + whereClause = " WHERE " + whereClause + } + returningCols := q.selectAllFieldsSoFarCols(selects, omits) + return executeSingleWithRelations(ctx, q, "AllFieldsSoFar", whereClause, vals, returningCols, + func(res *AllFieldsSoFar, cols []string) []any { return res.ScanFields(cols) }, + selects.hasAnyRelation(), + func(ctx context.Context, txQ *Queries, results []*AllFieldsSoFar) error { + return txQ.loadAllFieldsSoFarRelations(ctx, results, selects) + }, + ) +} + +func (q *Queries) executeAllFieldsSoFarFindMany(ctx context.Context, where []Predicate, selects *AllFieldsSoFarSelect, omits *AllFieldsSoFarOmit) ([]*AllFieldsSoFar, error) { + for _, p := range where { + if p != nil { + if err := p.Validate(); err != nil { + return nil, err + } + } + } + whereClause, vals := CompilePredicates(q.dialect, where) + if whereClause != "" { + whereClause = " WHERE " + whereClause + } + returningCols := q.selectAllFieldsSoFarCols(selects, omits) + return executeManyWithRelations(ctx, q, "AllFieldsSoFar", whereClause, vals, returningCols, + func(res *AllFieldsSoFar, cols []string) []any { return res.ScanFields(cols) }, + selects.hasAnyRelation(), + func(ctx context.Context, txQ *Queries, results []*AllFieldsSoFar) error { + return txQ.loadAllFieldsSoFarRelations(ctx, results, selects) + }, + ) +} +func (q *Queries) loadAllFieldsSoFarRelations(ctx context.Context, records []*AllFieldsSoFar, selects *AllFieldsSoFarSelect) error { + if selects == nil || len(records) == 0 { + return nil + } + + return nil +} diff --git a/integration/valk/allFieldsSoFar/allFieldsSoFar.go b/integration/valk/allFieldsSoFar/allFieldsSoFar.go new file mode 100644 index 0000000..f2d8959 --- /dev/null +++ b/integration/valk/allFieldsSoFar/allFieldsSoFar.go @@ -0,0 +1,118 @@ +package allFieldsSoFar + +import ( + "encoding/json" + "fmt" + "integration/valk" + "time" +) + +type UniquePredicate struct { + valk.StandardPredicate +} + +func (UniquePredicate) IsUnique() {} + +func (p UniquePredicate) Validate() error { + if p.StandardPredicate.Data.Column == "" && len(p.StandardPredicate.Data.Children) == 0 { + return fmt.Errorf("at least one unique field must be set for FindUnique") + } + return p.StandardPredicate.Validate() +} + +type Select = valk.AllFieldsSoFarSelect +type Omit = valk.AllFieldsSoFarOmit + +func Record(assignments ...valk.FieldAssignment) valk.RecordInput { + return valk.RecordInput{Assignments: assignments} +} + +func And(preds ...valk.Predicate) valk.Predicate { + return valk.And(preds...) +} + +func Or(preds ...valk.Predicate) valk.Predicate { + return valk.Or(preds...) +} + +func Not(pred valk.Predicate) valk.Predicate { + return valk.Not(pred) +} + +var Id = valk.UniqueField[int32]{Column: "id"} + +var StringReq = valk.StringField{Column: "stringReq"} + +var StringOpt = valk.StringField{Column: "stringOpt"} + +var StringDefault = valk.StringField{Column: "stringDefault"} + +var StringVarchar = valk.StringField{Column: "stringVarchar"} + +var StringChar = valk.StringField{Column: "stringChar"} + +var CuidDefault = valk.StringField{Column: "cuidDefault"} + +var Cuid1Default = valk.StringField{Column: "cuid1Default"} + +var Cuid2Default = valk.StringField{Column: "cuid2Default"} + +var UuidDefault = valk.StringField{Column: "uuidDefault"} + +var Uuid4Default = valk.StringField{Column: "uuid4Default"} + +var Uuid7Default = valk.StringField{Column: "uuid7Default"} + +var UlidDefault = valk.StringField{Column: "ulidDefault"} + +var NanoidDefault = valk.StringField{Column: "nanoidDefault"} + +var UuidDb = valk.StringField{Column: "uuidDb"} + +var IntReq = valk.Field[int32]{Column: "intReq"} + +var IntOpt = valk.Field[int32]{Column: "intOpt"} + +var IntDefault = valk.Field[int32]{Column: "intDefault"} + +var SmallInt = valk.Field[int32]{Column: "smallInt"} + +var TinyInt = valk.Field[int32]{Column: "tinyInt"} + +var BigIntReq = valk.Field[int64]{Column: "bigIntReq"} + +var BigIntOpt = valk.Field[int64]{Column: "bigIntOpt"} + +var FloatReq = valk.Field[float64]{Column: "floatReq"} + +var FloatOpt = valk.Field[float64]{Column: "floatOpt"} + +var DecimalReq = valk.Field[string]{Column: "decimalReq"} + +var DecimalOpt = valk.Field[string]{Column: "decimalOpt"} + +var DecimalPrecise = valk.Field[string]{Column: "decimalPrecise"} + +var BoolReq = valk.Field[bool]{Column: "boolReq"} + +var BoolOpt = valk.Field[bool]{Column: "boolOpt"} + +var BoolDefault = valk.Field[bool]{Column: "boolDefault"} + +var DateTimeReq = valk.Field[time.Time]{Column: "dateTimeReq"} + +var DateTimeOpt = valk.Field[time.Time]{Column: "dateTimeOpt"} + +var DateTimeDefault = valk.Field[time.Time]{Column: "dateTimeDefault"} + +var UpdatedAt = valk.Field[time.Time]{Column: "updatedAt"} + +var DateTimeTz = valk.Field[time.Time]{Column: "dateTimeTz"} + +var JsonReq = valk.Field[json.RawMessage]{Column: "jsonReq"} + +var JsonOpt = valk.Field[json.RawMessage]{Column: "jsonOpt"} + +var BytesReq = valk.Field[[]byte]{Column: "bytesReq"} + +var BytesOpt = valk.Field[[]byte]{Column: "bytesOpt"} diff --git a/integration/valk/category.go b/integration/valk/category.go index 65291b6..ce930f8 100644 --- a/integration/valk/category.go +++ b/integration/valk/category.go @@ -123,6 +123,9 @@ func validateCategoryCreate(assignments []FieldAssignment) error { provided[a.Col] = true switch a.Col { case "id": + if _, ok := a.Val.(int32); !ok { + errs.Add("id", a.Val, "type", "field id must be of type int32") + } case "name": if v, ok := a.Val.(string); ok { if v == "" { diff --git a/integration/valk/categoryToPost.go b/integration/valk/categoryToPost.go index c2afba3..2fc0e2a 100644 --- a/integration/valk/categoryToPost.go +++ b/integration/valk/categoryToPost.go @@ -138,11 +138,17 @@ func validateCategoryToPostCreate(assignments []FieldAssignment) error { } } case "categoryId": + if _, ok := a.Val.(int32); !ok { + errs.Add("categoryId", a.Val, "type", "field categoryId must be of type int32") + } } } if !provided["postId"] { errs.Add("postId", "", "required", "field PostId is required") } + if !provided["categoryId"] { + errs.Add("categoryId", nil, "required", "field CategoryId is required") + } if errs.HasErrors() { return *errs diff --git a/integration/valk/client.go b/integration/valk/client.go index e2fbb29..80aa186 100644 --- a/integration/valk/client.go +++ b/integration/valk/client.go @@ -20,6 +20,7 @@ var _ = time.Time{} var _ = json.RawMessage{} var _ = strings.Join var _ = uuid.New +var _ = uuid.NewV7 var _ = rand.Read var _ = strconv.AppendUint @@ -45,6 +46,66 @@ func generateUUID() string { return uuid.New().String() } +func generateUUID7() string { + id, err := uuid.NewV7() + if err != nil { + return uuid.New().String() + } + return id.String() +} + +func generateCUID2() string { + now := uint64(time.Now().UnixMilli()) + b := make([]byte, 12) + _, _ = rand.Read(b) + buf := make([]byte, 0, 34) + buf = strconv.AppendUint(buf, now, 36) + const chars = "0123456789abcdefghijklmnopqrstuvwxyz" + for _, v := range b { + buf = append(buf, chars[v&0x1f]) + buf = append(buf, chars[(v>>5)&0x1f]) + } + return string(buf) +} + +func generateULID() string { + now := uint64(time.Now().UnixMilli()) + b := make([]byte, 10) + _, _ = rand.Read(b) + bits := make([]byte, 16) + bits[0] = byte(now >> 40) + bits[1] = byte(now >> 32) + bits[2] = byte(now >> 24) + bits[3] = byte(now >> 16) + bits[4] = byte(now >> 8) + bits[5] = byte(now) + copy(bits[6:], b) + + const crockford = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" + var buf [26]byte + for i := 25; i >= 0; i-- { + bitOff := i * 5 + byteIdx := bitOff / 8 + byteOff := uint(bitOff % 8) + val := uint16(bits[byteIdx]) >> byteOff + if byteIdx+1 < 16 { + val |= uint16(bits[byteIdx+1]) << (8 - byteOff) + } + buf[i] = crockford[val&0x1f] + } + return string(buf[:]) +} + +func generateNanoID() string { + const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-" + b := make([]byte, 21) + _, _ = rand.Read(b) + for i := range b { + b[i] = alphabet[b[i]&0x3f] + } + return string(b) +} + // FieldError represents a single validation failure on a specific field. type FieldError struct { Field string @@ -201,6 +262,60 @@ type Queries struct { // postId string required // categoryId int32 required CategoryToPost *CategoryToPostDelegate + // DefaultsTest provides CRUD operations for DefaultsTest. + // + // uuid4 string default: uuid() + // uuid7 string default: uuid() + // uuidNoArgs string default: uuid() + // cuid1 string default: cuid() + // cuid2 string default: cuid() + // cuidNoArgs string default: cuid() + // ulid string default: ulid() + // nanoid string default: nanoid() + // now time.Time default: now() + DefaultsTest *DefaultsTestDelegate + // AllFieldsSoFar provides CRUD operations for AllFieldsSoFar. + // + // id int32 default: autoincrement() + // stringReq string required + // stringOpt string optional + // stringDefault string default: default + // stringVarchar string required + // stringChar string required + // cuidDefault string default: cuid() + // cuid1Default string default: cuid() + // cuid2Default string default: cuid() + // uuidDefault string default: uuid() + // uuid4Default string default: uuid() + // uuid7Default string default: uuid() + // ulidDefault string default: ulid() + // nanoidDefault string default: nanoid() + // uuidDb string required + // intReq int32 required + // intOpt int32 optional + // intDefault int32 default: 42 + // smallInt int32 required + // tinyInt int32 required + // bigIntReq int64 required + // bigIntOpt int64 optional + // floatReq float64 required + // floatOpt float64 optional + // decimalReq string required + // decimalOpt string optional + // decimalPrecise string required + // boolReq bool required + // boolOpt bool optional + // boolDefault bool default: false + // dateTimeReq time.Time required + // dateTimeOpt time.Time optional + // dateTimeDefault time.Time default: now() + // updatedAt time.Time required + // dateTimeTz time.Time required + // jsonReq json.RawMessage required + // jsonOpt json.RawMessage optional + // bytesReq []byte required + // bytesOpt []byte optional + AllFieldsSoFar *AllFieldsSoFarDelegate UserRole userRoleNamespace } @@ -236,6 +351,8 @@ func (q *Queries) initDelegates() { q.Comment = &CommentDelegate{client: q} q.Category = &CategoryDelegate{client: q} q.CategoryToPost = &CategoryToPostDelegate{client: q} + q.DefaultsTest = &DefaultsTestDelegate{client: q} + q.AllFieldsSoFar = &AllFieldsSoFarDelegate{client: q} } func (q *Queries) copyHooksFrom(other *Queries) { @@ -257,6 +374,12 @@ func (q *Queries) copyHooksFrom(other *Queries) { q.CategoryToPost.beforeCreate = other.CategoryToPost.beforeCreate q.CategoryToPost.afterCreate = other.CategoryToPost.afterCreate q.CategoryToPost.afterCreateMany = other.CategoryToPost.afterCreateMany + q.DefaultsTest.beforeCreate = other.DefaultsTest.beforeCreate + q.DefaultsTest.afterCreate = other.DefaultsTest.afterCreate + q.DefaultsTest.afterCreateMany = other.DefaultsTest.afterCreateMany + q.AllFieldsSoFar.beforeCreate = other.AllFieldsSoFar.beforeCreate + q.AllFieldsSoFar.afterCreate = other.AllFieldsSoFar.afterCreate + q.AllFieldsSoFar.afterCreateMany = other.AllFieldsSoFar.afterCreateMany } // Close closes the database connection. diff --git a/integration/valk/comment.go b/integration/valk/comment.go index 153fdf4..6712cd4 100644 --- a/integration/valk/comment.go +++ b/integration/valk/comment.go @@ -190,6 +190,9 @@ func validateCommentCreate(assignments []FieldAssignment) error { } } case "textify": + if _, ok := a.Val.(int32); !ok { + errs.Add("textify", a.Val, "type", "field textify must be of type int32") + } case "dummy3": if v, ok := a.Val.(string); ok { if v == "" { @@ -203,6 +206,9 @@ func validateCommentCreate(assignments []FieldAssignment) error { } } case "dummy1": + if _, ok := a.Val.(int32); !ok { + errs.Add("dummy1", a.Val, "type", "field dummy1 must be of type int32") + } case "dummy2": if v, ok := a.Val.(string); ok { if v == "" { @@ -245,9 +251,15 @@ func validateCommentCreate(assignments []FieldAssignment) error { } } } + if !provided["textify"] { + errs.Add("textify", nil, "required", "field Textify is required") + } if !provided["dummy3"] { errs.Add("dummy3", "", "required", "field Dummy3 is required") } + if !provided["dummy1"] { + errs.Add("dummy1", nil, "required", "field Dummy1 is required") + } if !provided["dummy2"] { errs.Add("dummy2", "", "required", "field Dummy2 is required") } diff --git a/integration/valk/defaultsTest.go b/integration/valk/defaultsTest.go new file mode 100644 index 0000000..460d388 --- /dev/null +++ b/integration/valk/defaultsTest.go @@ -0,0 +1,652 @@ +package valk + +import ( + "context" + "fmt" + "slices" + "strings" + "time" + "unicode/utf8" +) + +// DefaultsTest represents the database model +type DefaultsTest struct { + Uuid4 string `db:"uuid4" json:"uuid4"` + Uuid7 string `db:"uuid7" json:"uuid7"` + UuidNoArgs string `db:"uuidNoArgs" json:"uuidNoArgs"` + Cuid1 string `db:"cuid1" json:"cuid1"` + Cuid2 string `db:"cuid2" json:"cuid2"` + CuidNoArgs string `db:"cuidNoArgs" json:"cuidNoArgs"` + Ulid string `db:"ulid" json:"ulid"` + Nanoid string `db:"nanoid" json:"nanoid"` + Now time.Time `db:"now" json:"now"` +} + +// DefaultsTestCreate is used for hooks only — the Create API uses FieldAssignment +type DefaultsTestCreate struct { + Uuid4 *string `json:"uuid4"` + Uuid7 *string `json:"uuid7"` + UuidNoArgs *string `json:"uuidNoArgs"` + Cuid1 *string `json:"cuid1"` + Cuid2 *string `json:"cuid2"` + CuidNoArgs *string `json:"cuidNoArgs"` + Ulid *string `json:"ulid"` + Nanoid *string `json:"nanoid"` + Now *time.Time `json:"now"` +} + +// DefaultsTestSelect specifies which fields to include +type DefaultsTestSelect struct { + Uuid4 bool `json:"uuid4"` + Uuid7 bool `json:"uuid7"` + UuidNoArgs bool `json:"uuidNoArgs"` + Cuid1 bool `json:"cuid1"` + Cuid2 bool `json:"cuid2"` + CuidNoArgs bool `json:"cuidNoArgs"` + Ulid bool `json:"ulid"` + Nanoid bool `json:"nanoid"` + Now bool `json:"now"` +} + +// DefaultsTestOmit specifies which fields to exclude +type DefaultsTestOmit struct { + Uuid4 bool `json:"uuid4"` + Uuid7 bool `json:"uuid7"` + UuidNoArgs bool `json:"uuidNoArgs"` + Cuid1 bool `json:"cuid1"` + Cuid2 bool `json:"cuid2"` + CuidNoArgs bool `json:"cuidNoArgs"` + Ulid bool `json:"ulid"` + Nanoid bool `json:"nanoid"` + Now bool `json:"now"` +} + +type DefaultsTestDelegate struct { + client *Queries + beforeCreate func(context.Context, *DefaultsTestCreate) error + afterCreate func(context.Context, []*DefaultsTest) error + afterCreateMany func(context.Context, []DefaultsTestCreate, int64) error +} + +func (d *DefaultsTestDelegate) BeforeCreate(hook func(context.Context, *DefaultsTestCreate) error) { + d.beforeCreate = hook +} + +func (d *DefaultsTestDelegate) AfterCreate(hook func(context.Context, []*DefaultsTest) error) { + d.afterCreate = hook +} + +func (d *DefaultsTestDelegate) AfterCreateMany(hook func(context.Context, []DefaultsTestCreate, int64) error) { + d.afterCreateMany = hook +} + +func (m *DefaultsTest) ScanFields(cols []string) []any { + targets := make([]any, len(cols)) + for i, col := range cols { + switch col { + case "uuid4": + targets[i] = &m.Uuid4 + case "uuid7": + targets[i] = &m.Uuid7 + case "uuidNoArgs": + targets[i] = &m.UuidNoArgs + case "cuid1": + targets[i] = &m.Cuid1 + case "cuid2": + targets[i] = &m.Cuid2 + case "cuidNoArgs": + targets[i] = &m.CuidNoArgs + case "ulid": + targets[i] = &m.Ulid + case "nanoid": + targets[i] = &m.Nanoid + case "now": + targets[i] = &m.Now + } + } + return targets +} + +var defaultsTestDefaultCols = []string{ + "uuid4", + "uuid7", + "uuidNoArgs", + "cuid1", + "cuid2", + "cuidNoArgs", + "ulid", + "nanoid", + "now", +} + +func (q *Queries) selectDefaultsTestCols(selects *DefaultsTestSelect, omits *DefaultsTestOmit, forceCols ...string) []string { + if selects == nil && omits == nil && len(forceCols) == 0 { + return defaultsTestDefaultCols + } + + anySelected := selects != nil && (selects.Uuid4 || selects.Uuid7 || selects.UuidNoArgs || selects.Cuid1 || selects.Cuid2 || selects.CuidNoArgs || selects.Ulid || selects.Nanoid || selects.Now) + + specs := []colSpec{ + {"uuid4", selects != nil && selects.Uuid4, omits != nil && omits.Uuid4, selects != nil && selects.hasAnyRelation()}, + {"uuid7", selects != nil && selects.Uuid7, omits != nil && omits.Uuid7, false}, + {"uuidNoArgs", selects != nil && selects.UuidNoArgs, omits != nil && omits.UuidNoArgs, false}, + {"cuid1", selects != nil && selects.Cuid1, omits != nil && omits.Cuid1, false}, + {"cuid2", selects != nil && selects.Cuid2, omits != nil && omits.Cuid2, false}, + {"cuidNoArgs", selects != nil && selects.CuidNoArgs, omits != nil && omits.CuidNoArgs, false}, + {"ulid", selects != nil && selects.Ulid, omits != nil && omits.Ulid, false}, + {"nanoid", selects != nil && selects.Nanoid, omits != nil && omits.Nanoid, false}, + {"now", selects != nil && selects.Now, omits != nil && omits.Now, false}, + } + + cols := computeCols(specs, selects != nil, anySelected) + + for _, f := range forceCols { + if !slices.Contains(cols, f) { + cols = append(cols, f) + } + } + + return cols +} + +var DefaultsTestColOrder = []string{ + "uuid4", + "uuid7", + "uuidNoArgs", + "cuid1", + "cuid2", + "cuidNoArgs", + "ulid", + "nanoid", + "now", +} + +func (s *DefaultsTestSelect) hasAnyRelation() bool { + if s == nil { + return false + } + return false +} + +func (d *DefaultsTestDelegate) Create(assignments ...FieldAssignment) *CreateBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit] { + return &CreateBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit]{ + client: d.client, + assignments: assignments, + execFunc: d.client.executeDefaultsTestCreate, + } +} + +func validateDefaultsTestCreate(assignments []FieldAssignment) error { + errs := &ValidationError{} + + provided := make(map[string]bool) + for _, a := range assignments { + provided[a.Col] = true + switch a.Col { + case "uuid4": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("uuid4", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("uuid4", v, "safety", "string must be valid UTF-8") + } + } + case "uuid7": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("uuid7", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("uuid7", v, "safety", "string must be valid UTF-8") + } + } + case "uuidNoArgs": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("uuidNoArgs", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("uuidNoArgs", v, "safety", "string must be valid UTF-8") + } + } + case "cuid1": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("cuid1", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("cuid1", v, "safety", "string must be valid UTF-8") + } + } + case "cuid2": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("cuid2", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("cuid2", v, "safety", "string must be valid UTF-8") + } + } + case "cuidNoArgs": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("cuidNoArgs", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("cuidNoArgs", v, "safety", "string must be valid UTF-8") + } + } + case "ulid": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("ulid", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("ulid", v, "safety", "string must be valid UTF-8") + } + } + case "nanoid": + if v, ok := a.Val.(string); ok { + if strings.Contains(v, "\x00") { + errs.Add("nanoid", v, "safety", "string cannot contain null bytes") + } + if !utf8.ValidString(v) { + errs.Add("nanoid", v, "safety", "string must be valid UTF-8") + } + } + case "now": + if _, ok := a.Val.(time.Time); !ok { + errs.Add("now", a.Val, "type", "field now must be of type time.Time") + } + } + } + + if errs.HasErrors() { + return *errs + } + return nil +} + +func assignmentsToDefaultsTestCreate(assignments []FieldAssignment) DefaultsTestCreate { + var input DefaultsTestCreate + for _, a := range assignments { + switch a.Col { + case "uuid4": + if v, ok := a.Val.(string); ok { + input.Uuid4 = &v + } + case "uuid7": + if v, ok := a.Val.(string); ok { + input.Uuid7 = &v + } + case "uuidNoArgs": + if v, ok := a.Val.(string); ok { + input.UuidNoArgs = &v + } + case "cuid1": + if v, ok := a.Val.(string); ok { + input.Cuid1 = &v + } + case "cuid2": + if v, ok := a.Val.(string); ok { + input.Cuid2 = &v + } + case "cuidNoArgs": + if v, ok := a.Val.(string); ok { + input.CuidNoArgs = &v + } + case "ulid": + if v, ok := a.Val.(string); ok { + input.Ulid = &v + } + case "nanoid": + if v, ok := a.Val.(string); ok { + input.Nanoid = &v + } + case "now": + if v, ok := a.Val.(time.Time); ok { + input.Now = &v + } + } + } + return input +} + +func (s *DefaultsTestCreate) ToRowMap() map[string]any { + m := make(map[string]any, 9) + if s.Uuid4 != nil { + m["uuid4"] = *s.Uuid4 + } else { + m["uuid4"] = generateUUID() + } + if s.Uuid7 != nil { + m["uuid7"] = *s.Uuid7 + } else { + m["uuid7"] = generateUUID7() + } + if s.UuidNoArgs != nil { + m["uuidNoArgs"] = *s.UuidNoArgs + } else { + m["uuidNoArgs"] = generateUUID() + } + if s.Cuid1 != nil { + m["cuid1"] = *s.Cuid1 + } else { + m["cuid1"] = generateCUID() + } + if s.Cuid2 != nil { + m["cuid2"] = *s.Cuid2 + } else { + m["cuid2"] = generateCUID2() + } + if s.CuidNoArgs != nil { + m["cuidNoArgs"] = *s.CuidNoArgs + } else { + m["cuidNoArgs"] = generateCUID() + } + if s.Ulid != nil { + m["ulid"] = *s.Ulid + } else { + m["ulid"] = generateULID() + } + if s.Nanoid != nil { + m["nanoid"] = *s.Nanoid + } else { + m["nanoid"] = generateNanoID() + } + if s.Now != nil { + m["now"] = *s.Now + } else { + m["now"] = time.Now() + } + return m +} + +func (q *Queries) executeDefaultsTestCreate(ctx context.Context, assignments []FieldAssignment, selects *DefaultsTestSelect, omits *DefaultsTestOmit) (*DefaultsTest, error) { + input := assignmentsToDefaultsTestCreate(assignments) + + if q.DefaultsTest.beforeCreate != nil { + if err := q.DefaultsTest.beforeCreate(ctx, &input); err != nil { + return nil, err + } + } + + if err := validateDefaultsTestCreate(assignments); err != nil { + return nil, err + } + + var cols []string + var vals []any + if input.Uuid4 != nil { + cols = append(cols, "uuid4") + vals = append(vals, *input.Uuid4) + } else { + cols = append(cols, "uuid4") + vals = append(vals, generateUUID()) + } + if input.Uuid7 != nil { + cols = append(cols, "uuid7") + vals = append(vals, *input.Uuid7) + } else { + cols = append(cols, "uuid7") + vals = append(vals, generateUUID7()) + } + if input.UuidNoArgs != nil { + cols = append(cols, "uuidNoArgs") + vals = append(vals, *input.UuidNoArgs) + } else { + cols = append(cols, "uuidNoArgs") + vals = append(vals, generateUUID()) + } + if input.Cuid1 != nil { + cols = append(cols, "cuid1") + vals = append(vals, *input.Cuid1) + } else { + cols = append(cols, "cuid1") + vals = append(vals, generateCUID()) + } + if input.Cuid2 != nil { + cols = append(cols, "cuid2") + vals = append(vals, *input.Cuid2) + } else { + cols = append(cols, "cuid2") + vals = append(vals, generateCUID2()) + } + if input.CuidNoArgs != nil { + cols = append(cols, "cuidNoArgs") + vals = append(vals, *input.CuidNoArgs) + } else { + cols = append(cols, "cuidNoArgs") + vals = append(vals, generateCUID()) + } + if input.Ulid != nil { + cols = append(cols, "ulid") + vals = append(vals, *input.Ulid) + } else { + cols = append(cols, "ulid") + vals = append(vals, generateULID()) + } + if input.Nanoid != nil { + cols = append(cols, "nanoid") + vals = append(vals, *input.Nanoid) + } else { + cols = append(cols, "nanoid") + vals = append(vals, generateNanoID()) + } + if input.Now != nil { + cols = append(cols, "now") + vals = append(vals, *input.Now) + } else { + cols = append(cols, "now") + vals = append(vals, time.Now()) + } + + returningCols := q.selectDefaultsTestCols(selects, omits) + + scanFunc := func(res *DefaultsTest, cols []string) []any { + return res.ScanFields(cols) + } + + idCol := "uuid4" + + hasRelations := selects.hasAnyRelation() + + var res *DefaultsTest + var err error + if hasRelations { + err = q.transaction(ctx, func(txQ *Queries) error { + var err error + res, err = executeInsert(ctx, txQ, "DefaultsTest", cols, vals, returningCols, idCol, scanFunc) + if err != nil { + return err + } + return txQ.loadDefaultsTestRelations(ctx, []*DefaultsTest{res}, selects) + }) + } else { + res, err = executeInsert(ctx, q, "DefaultsTest", cols, vals, returningCols, idCol, scanFunc) + } + if err != nil { + return nil, err + } + + if q.DefaultsTest.afterCreate != nil { + if err := q.DefaultsTest.afterCreate(ctx, []*DefaultsTest{res}); err != nil { + return nil, err + } + } + + return res, nil +} + +func (d *DefaultsTestDelegate) CreateMany(records ...RecordInput) *CreateManyBuilder[DefaultsTest] { + return &CreateManyBuilder[DefaultsTest]{ + client: d.client, + records: records, + execFunc: d.client.executeDefaultsTestCreateMany, + } +} + +func (d *DefaultsTestDelegate) CreateManyAndReturn(records ...RecordInput) *CreateManyAndReturnBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit] { + return &CreateManyAndReturnBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit]{ + client: d.client, + records: records, + execFunc: d.client.executeDefaultsTestCreateManyAndReturn, + } +} + +func (q *Queries) executeDefaultsTestCreateMany(ctx context.Context, records []RecordInput) (int64, error) { + rowMaps := make([]map[string]any, len(records)) + inputs := make([]DefaultsTestCreate, len(records)) + for i, rec := range records { + if err := validateDefaultsTestCreate(rec.Assignments); err != nil { + return 0, fmt.Errorf("validation failed at index %d: %w", i, err) + } + input := assignmentsToDefaultsTestCreate(rec.Assignments) + if q.DefaultsTest.beforeCreate != nil { + if err := q.DefaultsTest.beforeCreate(ctx, &input); err != nil { + return 0, err + } + } + rowMaps[i] = input.ToRowMap() + inputs[i] = input + } + count, err := executeCreateMany(ctx, q, rowMaps, "DefaultsTest", DefaultsTestColOrder) + if err != nil { + return 0, err + } + if q.DefaultsTest.afterCreateMany != nil { + if err := q.DefaultsTest.afterCreateMany(ctx, inputs, count); err != nil { + return 0, err + } + } + return count, nil +} + +func (q *Queries) executeDefaultsTestCreateManyAndReturn(ctx context.Context, records []RecordInput, selects *DefaultsTestSelect, omits *DefaultsTestOmit) ([]*DefaultsTest, error) { + rowMaps := make([]map[string]any, len(records)) + idCol := "uuid4" + for i, rec := range records { + if err := validateDefaultsTestCreate(rec.Assignments); err != nil { + return nil, fmt.Errorf("validation failed at index %d: %w", i, err) + } + input := assignmentsToDefaultsTestCreate(rec.Assignments) + if q.DefaultsTest.beforeCreate != nil { + if err := q.DefaultsTest.beforeCreate(ctx, &input); err != nil { + return nil, err + } + } + rowMaps[i] = input.ToRowMap() + } + results, err := executeCreateManyAndReturn(ctx, q, rowMaps, "DefaultsTest", DefaultsTestColOrder, selects, omits, + q.selectDefaultsTestCols, + q.loadDefaultsTestRelations, + (*DefaultsTest).ScanFields, + (*DefaultsTestSelect).hasAnyRelation, + idCol, + ) + if err != nil { + return nil, err + } + if q.DefaultsTest.afterCreate != nil { + if err := q.DefaultsTest.afterCreate(ctx, results); err != nil { + return nil, err + } + } + return results, nil +} +func (d *DefaultsTestDelegate) FindUnique(where UniquePredicate) *FindUniqueBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit] { + return &FindUniqueBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit]{ + client: d.client, + where: where, + execFunc: d.client.executeDefaultsTestFindUnique, + } +} + +func (d *DefaultsTestDelegate) FindFirst(preds ...Predicate) *FindFirstBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit] { + return &FindFirstBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit]{ + client: d.client, + where: preds, + execFunc: d.client.executeDefaultsTestFindFirst, + } +} + +func (d *DefaultsTestDelegate) FindMany(preds ...Predicate) *FindManyBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit] { + return &FindManyBuilder[DefaultsTest, DefaultsTestSelect, DefaultsTestOmit]{ + client: d.client, + where: preds, + execFunc: d.client.executeDefaultsTestFindMany, + } +} + +func (q *Queries) executeDefaultsTestFindUnique(ctx context.Context, where UniquePredicate, selects *DefaultsTestSelect, omits *DefaultsTestOmit) (*DefaultsTest, error) { + if where == nil { + return nil, fmt.Errorf("at least one unique field must be set for FindUnique") + } + if err := where.Validate(); err != nil { + return nil, err + } + whereClause, vals := CompilePredicates(q.dialect, []Predicate{where}) + if whereClause != "" { + whereClause = " WHERE " + whereClause + } + returningCols := q.selectDefaultsTestCols(selects, omits) + return executeSingleWithRelations(ctx, q, "DefaultsTest", whereClause, vals, returningCols, + func(res *DefaultsTest, cols []string) []any { return res.ScanFields(cols) }, + selects.hasAnyRelation(), + func(ctx context.Context, txQ *Queries, results []*DefaultsTest) error { + return txQ.loadDefaultsTestRelations(ctx, results, selects) + }, + ) +} + +func (q *Queries) executeDefaultsTestFindFirst(ctx context.Context, where []Predicate, selects *DefaultsTestSelect, omits *DefaultsTestOmit) (*DefaultsTest, error) { + for _, p := range where { + if p != nil { + if err := p.Validate(); err != nil { + return nil, err + } + } + } + whereClause, vals := CompilePredicates(q.dialect, where) + if whereClause != "" { + whereClause = " WHERE " + whereClause + } + returningCols := q.selectDefaultsTestCols(selects, omits) + return executeSingleWithRelations(ctx, q, "DefaultsTest", whereClause, vals, returningCols, + func(res *DefaultsTest, cols []string) []any { return res.ScanFields(cols) }, + selects.hasAnyRelation(), + func(ctx context.Context, txQ *Queries, results []*DefaultsTest) error { + return txQ.loadDefaultsTestRelations(ctx, results, selects) + }, + ) +} + +func (q *Queries) executeDefaultsTestFindMany(ctx context.Context, where []Predicate, selects *DefaultsTestSelect, omits *DefaultsTestOmit) ([]*DefaultsTest, error) { + for _, p := range where { + if p != nil { + if err := p.Validate(); err != nil { + return nil, err + } + } + } + whereClause, vals := CompilePredicates(q.dialect, where) + if whereClause != "" { + whereClause = " WHERE " + whereClause + } + returningCols := q.selectDefaultsTestCols(selects, omits) + return executeManyWithRelations(ctx, q, "DefaultsTest", whereClause, vals, returningCols, + func(res *DefaultsTest, cols []string) []any { return res.ScanFields(cols) }, + selects.hasAnyRelation(), + func(ctx context.Context, txQ *Queries, results []*DefaultsTest) error { + return txQ.loadDefaultsTestRelations(ctx, results, selects) + }, + ) +} +func (q *Queries) loadDefaultsTestRelations(ctx context.Context, records []*DefaultsTest, selects *DefaultsTestSelect) error { + if selects == nil || len(records) == 0 { + return nil + } + + return nil +} diff --git a/integration/valk/defaultsTest/defaultsTest.go b/integration/valk/defaultsTest/defaultsTest.go new file mode 100644 index 0000000..4fc1e4d --- /dev/null +++ b/integration/valk/defaultsTest/defaultsTest.go @@ -0,0 +1,57 @@ +package defaultsTest + +import ( + "fmt" + "integration/valk" + "time" +) + +type UniquePredicate struct { + valk.StandardPredicate +} + +func (UniquePredicate) IsUnique() {} + +func (p UniquePredicate) Validate() error { + if p.StandardPredicate.Data.Column == "" && len(p.StandardPredicate.Data.Children) == 0 { + return fmt.Errorf("at least one unique field must be set for FindUnique") + } + return p.StandardPredicate.Validate() +} + +type Select = valk.DefaultsTestSelect +type Omit = valk.DefaultsTestOmit + +func Record(assignments ...valk.FieldAssignment) valk.RecordInput { + return valk.RecordInput{Assignments: assignments} +} + +func And(preds ...valk.Predicate) valk.Predicate { + return valk.And(preds...) +} + +func Or(preds ...valk.Predicate) valk.Predicate { + return valk.Or(preds...) +} + +func Not(pred valk.Predicate) valk.Predicate { + return valk.Not(pred) +} + +var Uuid4 = valk.StringUniqueField{Column: "uuid4"} + +var Uuid7 = valk.StringField{Column: "uuid7"} + +var UuidNoArgs = valk.StringField{Column: "uuidNoArgs"} + +var Cuid1 = valk.StringField{Column: "cuid1"} + +var Cuid2 = valk.StringField{Column: "cuid2"} + +var CuidNoArgs = valk.StringField{Column: "cuidNoArgs"} + +var Ulid = valk.StringField{Column: "ulid"} + +var Nanoid = valk.StringField{Column: "nanoid"} + +var Now = valk.Field[time.Time]{Column: "now"} diff --git a/integration/valk/migrations/00001_init.sql b/integration/valk/migrations/00001_init.sql index 81cd929..6489eee 100644 --- a/integration/valk/migrations/00001_init.sql +++ b/integration/valk/migrations/00001_init.sql @@ -58,6 +58,59 @@ CREATE TABLE `CategoryToPost` ( CONSTRAINT `CategoryToPost_postId_fkey` FOREIGN KEY (`postId`) REFERENCES `Post` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT `CategoryToPost_categoryId_fkey` FOREIGN KEY (`categoryId`) REFERENCES `Category` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION ); +CREATE TABLE `DefaultsTest` ( + `uuid4` text NOT NULL, + `uuid7` text NOT NULL, + `uuidNoArgs` text NOT NULL, + `cuid1` text NOT NULL, + `cuid2` text NOT NULL, + `cuidNoArgs` text NOT NULL, + `ulid` text NOT NULL, + `nanoid` text NOT NULL, + `now` timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP), + PRIMARY KEY (`uuid4`) +); +CREATE TABLE `AllFieldsSoFar` ( + `id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, + `stringReq` text NOT NULL, + `stringOpt` text NULL, + `stringDefault` text NOT NULL DEFAULT ('default'), + `stringVarchar` text NOT NULL, + `stringChar` text NOT NULL, + `cuidDefault` text NOT NULL, + `cuid1Default` text NOT NULL, + `cuid2Default` text NOT NULL, + `uuidDefault` text NOT NULL, + `uuid4Default` text NOT NULL, + `uuid7Default` text NOT NULL, + `ulidDefault` text NOT NULL, + `nanoidDefault` text NOT NULL, + `uuidDb` text NOT NULL, + `intReq` integer NOT NULL, + `intOpt` integer NULL, + `intDefault` integer NOT NULL DEFAULT (42), + `smallInt` integer NOT NULL, + `tinyInt` integer NOT NULL, + `bigIntReq` integer NOT NULL, + `bigIntOpt` integer NULL, + `floatReq` real NOT NULL, + `floatOpt` real NULL, + `decimalReq` real NOT NULL, + `decimalOpt` real NULL, + `decimalPrecise` real NOT NULL, + `boolReq` integer NOT NULL, + `boolOpt` integer NULL, + `boolDefault` integer NOT NULL DEFAULT (FALSE), + `dateTimeReq` timestamp NOT NULL, + `dateTimeOpt` timestamp NULL, + `dateTimeDefault` timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP), + `updatedAt` timestamp NOT NULL, + `dateTimeTz` timestamp NOT NULL, + `jsonReq` blob NOT NULL, + `jsonOpt` blob NULL, + `bytesReq` blob NOT NULL, + `bytesOpt` blob NULL +); -- +goose Down PRAGMA foreign_keys = off; @@ -67,4 +120,6 @@ DROP TABLE `Post`; DROP TABLE `Comment`; DROP TABLE `Category`; DROP TABLE `CategoryToPost`; +DROP TABLE `DefaultsTest`; +DROP TABLE `AllFieldsSoFar`; PRAGMA foreign_keys = on; diff --git a/migration/postgresDialect.go b/migration/postgresDialect.go index 512c89d..2de3d0a 100644 --- a/migration/postgresDialect.go +++ b/migration/postgresDialect.go @@ -49,9 +49,13 @@ func (d PostgresDialect) GetSQLDefault(dv *schema.DefaultValue, pslType string) switch dv.FuncName { case "now": return "CURRENT_TIMESTAMP" - case "uuid": + case "uuid", "uuid(4)", "uuid(7)": return "gen_random_uuid()" - case "cuid": + case "cuid", "cuid(1)", "cuid(2)": + return "" + case "ulid": + return "" + case "nanoid": return "" } diff --git a/migration/sqliteDialect.go b/migration/sqliteDialect.go index 5e50a5a..95368eb 100644 --- a/migration/sqliteDialect.go +++ b/migration/sqliteDialect.go @@ -4,6 +4,7 @@ import ( "database/sql" "fmt" "strings" + "github.com/voidclancy/valk/schema" "ariga.io/atlas/sql/migrate" @@ -52,9 +53,13 @@ func (SqliteDialect) GetSQLDefault(dv *schema.DefaultValue, pslType string) stri switch dv.FuncName { case "now": return "CURRENT_TIMESTAMP" - case "uuid": + case "uuid", "uuid(4)", "uuid(7)": + return "" + case "cuid", "cuid(1)", "cuid(2)": + return "" + case "ulid": return "" - case "cuid": + case "nanoid": return "" } diff --git a/schema/resolver.go b/schema/resolver.go index 1bd9170..0a23ff0 100644 --- a/schema/resolver.go +++ b/schema/resolver.go @@ -2,6 +2,8 @@ package schema import ( "fmt" + "strings" + providers "github.com/voidclancy/valk/dbProviders" ) @@ -281,12 +283,19 @@ func (r *Resolver) resolveDefaultValue(a Attribute, enum *Enum) *DefaultValue { } return nil } - funcCallStr := v.Scalar + "()" + funcName := v.Scalar + if len(v.Args) > 0 { + var argStrs []string + for _, a := range v.Args { + argStrs = append(argStrs, a.Value.Scalar) + } + funcName += "(" + strings.Join(argStrs, ",") + ")" + } return &DefaultValue{ - Value: funcCallStr, + Value: v.Scalar + "()", Type: "Func", Kind: DefaultFunc, - FuncName: v.Scalar, + FuncName: funcName, } case ValIdent: