All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Added a new field
QueryFolders
togen/drivers.DBInfo
for drivers to be able to include information about parsed queries. - Added
gen/QueriesTemplates
which in the future will contain base templates for generating code for parsed qureries. - Added a
QueryTemplate
field tobobgen_helpers.Templates
for drivers to include additional templates for queries. - Added a new reserved output key
queries
. This is handled specially for each query folder supplied by the driver. - Added new
wm
package to each dialect for mods that modifyWindow
clauses.
- Updated error constant generation to employ specific error types for making error matching easier. (thanks @mbezhanov)
- Collation in
clause.OrderDef
is now a string not an expression and is always quoted - Calling
UpdateAll
,DeleteAll
andReloadAll
on an empty model slice now returns nil without running any queries. UNION
,INTERSECT
andEXCEPT
mods now append to the query instead of replacing it.- Generated files now end with
.bob.go
instead of.go
and are always cleaned up before generating new files. Singleton templates are now required to have a.bob.go.tpl
extension. - The expected structure for templates have been changed:
- Previously, singleton templates should be kept in a
singleton
folder. Now, any template not inside a folder is considered a singleton template. - Previoulsy, templates in the root folder are merged and run for each table. Now, this will happen to templates in the
table/
folder. - Previoulsy, the entire file tree and every subdirectory is walked to find templates. Now only templates in the root folder and the
table/
folder are considered.
- Previously, singleton templates should be kept in a
- Change
From
inclause.Window
toBasedOn
to avoid confusion withFromPreceding
andFromFollowing
. Also changeSetFrom
toSetBasedOn
. - Embed
clause.OrderBy
inclause.Window
to make it possible to reuseOrderBy
mods in window definitions. - Change the
Definition
field inclause.NamedWindow
fromany
toclause.Window
for extra type safety. sm.Window
now takes mods to modify the window clause.fm.Over
now takes mods to modify the window for the window function.
- Deprecated the
wipe
option to delete all files in the output folder. Files are now generated with a.bob.go
extension and are always cleaned up before generating new files.
- Remove redundatnt type parameters from
orm.ExecQuery
. - Remove unnecessary interface in
orm.Query
andorm.ExecQuery
. - Remove the redundant
clause.IWindow
interface. - Remove
dialect.WindowMod
anddialect.WindowMods
which use chainable methods to modifyWindow
clauses. This is now handled by thewm
package which used mods.
- Fix random value generation for pq.Float64Array factory (thanks @felipeparaujo)
- Using the
UpdateMod()
andDeleteMod()
methods on an empty model slice now appendsWHERE primary_key IN NULL
which will return no results. Instead ofWHERE primary_key IN ()
which is a syntax error. - Ensure
net/netip
is imported for thepgtypes.Inet
random expression (thanks @plunkettscott) - Fix a data race when adding enum types.
- Fix missing schema in table alias in pkEQ and pkIN clauses (thanks @adatob).
- Added error constants for matching against both specific and generic unique constraint errors raised by the underlying database driver. (thanks @mbezhanov)
- Added support for regular expressions in the
only
andexcept
table filters. (thanks @mbezhanov) - Added
ContextualMods
which are similar to regular mods but take a context argument. They are applied whenever the query is built.
This makes it cleaner to do certain things, like populating the select columns of a model if none was explicitly added.
The previous way this was done was unreliable since usingq.MustBuild()
would not add the columns whilebob.MustBuild(q)
will add them correctly. modelSlice.UpdateMod()
andmodelSlice.DeleteMod()
are new methods that returns a mod for update and delete queries on a slice of models.
It addsWHERE pk IN (pk1, pk2, pk3, ...)
to the query, and also schedule running the hooks.- Added
bob.ToMods
which a slice of structs that implementbob.Mod[T]
to a Mod. This is useful since Go does not allow using a slice of structs as a slice of an interface the struct implements. - Added
bob.HookableQuery
interface. If a query implements this interface, the methodRunHooks(ctx, exec)
will be called before the query is executed. - Added
bob.HookableType
interface. If a type implements this interface, the methodAfterQueryHook(ctx, exec, bob.QueryType)
will be called after the query is executed.
This is howAfterSeleect/Insert/Update/DeleteHooks
hooks are now implemented. - Added
Type() QueryType
method tobob.Query
to get the type of query it is. Available constants areUnknown, Select, Insert, Update, Delete
. - Postgres and SQLite Update/Delete queries now refresh the models after the query is executed. This is enabled by the
RETURNING
clause, so it is not available in MySQL. - Added the
Case()
starter to all dialects to buildCASE
expressions. (thanks @k4n4ry) - Added
bob.Named()
which is used to add named arguments to the query and bind them later. - Added
bob.BindNamed
which takes an argument (struct, map, or a single value type) to be used to bind named arguments in a query. See changes tobob.Prepare()
for details of which type can be used. - Indexes now include more information such as the type, unique and comment fields.
- Constraints now include a comment field.
- Added
Checks
field to DBConstraints so that drivers can also load check constraints. (not yet supported by the SQLite driver). - Added comments field to Table definitions.
-
context.Context
is now passed toQuery.WriteQuery()
andExpression.WriteSQL()
methods. This allows for more control over how the query is built and executed.
This change made is possible to delete some hacks and simplify the codebase.- The
Name()
andNameAs()
methods of Views/Tables no longer need the context argument since the context will be passed when writing the expression. The API then becomes cleaner. - Preloading mods no longer need to store a context internally.
SetLoadContext()
andGetLoadContext()
have removed. - The
ToExpr
field inorm.RelSide
which was used for preloading is no longer needed and has been removed.
- The
-
Moved
orm.Hooks
tobob.Hooks
since it should not be limited to only ORM queries. -
Moved
mods.QueryModFunc
tobob.ModFunc
since it should be available to all packages. -
The mod capability for
orm.Setter
is now reversed. It should now be a mod for Insert and have a method that returns a mod for Update.
This makes more sense since one would at most use one setter during updates, but can use multiple setters in a bulk insert. -
table.InsertQ
has been renamed totable.Insert
. The old implementation ofInsert
has been removed.
The same functionality can be achieved in the following way://---------------------------------------------- // OLD WAY //---------------------------------------------- user, err := models.Users.Insert(ctx, db, setter) // insert one users, err := models.Users.InsertMany(ctx, db, setters...) // insert many //---------------------------------------------- // NEW WAY //---------------------------------------------- user, err := models.Users.Insert(setter).One(ctx, db) // insert one users, err := models.Users.Insert(setters[0], setters[1]).All(ctx, db) // insert many // For cases where you already have a slice of setters and you want to pass them all, you can use `bob.ToMods` users, err := models.Users.Insert(bob.ToMods(setters)).All(ctx, db) // insert many
-
table.UpdateQ
has been renamed totable.Update
. The old implementation ofUpdate
has been removed.
The same functionality can be achieved by usingmodel.Update()
ormodelSlice.UpdateAll()
. -
table.DeleteQ
has been renamed totable.Delete
. The old implementation ofDelete
has been removed.
The same functionality can be achieved by usingmodelSlice.DeleteAll()
or creating anDelete
query usingtable.Delete()
. -
BeforeInsertHooks
now only takes a singleModelSetter
at a time.
This is because it is not possible to know before executing the queries exactly how many setters are being used since additional rows can be inserted by applying another setter as a mod. -
bob.Cache()
now requires anExecutor
. This is used to run any query hooks. -
bob.Prepare()
now requires a type parameter to be used to bind named arguments. The type can either be:- A struct with fields that match the named arguments in the query
- A map with string keys. When supplied, the values in the map will be used to bind the named arguments in the query.
- When there is only a single named argument, one of the following can be used:
- A primitive type (int, bool, string, etc)
time.Time
- Any type that implements
driver.Valuer
.
-
Index
columns are no longer just strings, but are a struct to include more information such as the sort order.
- Remove MS SQL artifacts. (thanks @mbezhanov)
- Remove redundant type parameter from
bob.Load
. - Removed
Before/AfterUpsertMods
. Upserts are really just inserts with a conflict clause and should be treated as such. - Removed
Insert/InsertMany/Upsert/UpsertMany
methods fromorm.Table
since they are not needed.
It is possible to do the same thing, with similar effor using the theInsertQ
method (which is now renamed toInsert
). - Remove
Update
andDelete
methods fromorm.Table
since they are not needed.
It is possible to do the same thing, with similar effor using the theUpdateQ
andDeleteQ
methods (which are now renamed toUpdate
andDelete
). context.Context
andbob.Executor
are no longer passed when creating a Table/ViewQuery. It is now passed at the point of execution withExec/One/All/Cursor
.- Remove
Prepare
methods from table and view qureries. Sincebob.Prepare()
now takes a type parameter, it is not possible to prepare from a method since Go does not allow additional type parameters in methods. - Removed the Prisma and Atlas code generation drivers. It is better for Bob to focus on being able to generate code from the database in the most robust and detailed way and if the user wants, they can use other tools (such as prisma and atlas) to manage migrations before the code generation.
- Removed
Expressions
from Index definitions. It is now merged with theColumns
field with anIsExpression
field to indicate if the column is an expression.
- Removed unnecessary import of
strings
inbobfactory_random.go
. - Fixed data races in unit tests. (thanks @mbezhanov)
- Fixed invalid SQL statements generated by
sm.OrderBy().Collate()
. (thanks @mbezhanov) - Fixed a bug preventing specific columns from being excluded when generating models from SQLite. (thanks @mbezhanov)
- Fixed an issue where invalid code is generated if a configured relationship has
from_where
orto_where
. - Fixed
ModelSlice.ReloadAll()
method for models with multiple primary keys.
- Also add the enum to the type array if an array of the enum is added. This is to prvent issues if the enum is only used in an array.
- Handle null column names in expression indexes. (thanks @mbezhanov)
- CROSS JOINS now allow aliases
- Added the
pgtypes.Inet
forinet
type in PostgreSQL. (thanks @gstarikov) - Added the
pgtypes.Macaddr
formacaddr
andmacaddr8
types in PostgreSQL. - Added the
pgtypes.LSN
type for thepg_lsn
type in PostgreSQL. - Added the
pgtypes.TxIDSnapshot
type for thetxid_snapshot
type in PostgreSQL. - Added the
pgtypes.TSVector
type for thetsvector
type in PostgreSQL. - Added
AliasOf
property to codegen type definitions to allow for defining types that have their own randomization logic. - Added
DependsOn
property to codegen type definitions to allow for defining types that depend on other types. This ensures that necessary code for the dependent types is generated. - Add
xml
type definition for custom randomization logic. - Add the
Cast()
starter to all dialects to buildCAST(expr AS type)
expressions. - Load index information for MySQL, PostgreSQL, and SQLite tables. (thanks @mbezhanov)
- Changed the
parray
package topgtypes
. - Moved
HStore
to thepgtypes
package. - Simplified how random expressions are written for types by using standalone functions instead of a single generic function.
- The default
DebugPrinter
now prints args a bit more nicely by using thierValue()
method if they implement thedriver.Valuer
interface. - Only generate 2 random values when testing random expressions.
- Removed
types.Stringer[T]
. It makes assumptions for how the type should be scanned and is not reliable.
- Do not add
FROM
clause toSELECT
queries that are used as subqueries. - Enum values are now checked for validity after scanning.
- Fixed bug in
Count()
queries not removing the offset from the original query. (thanks @daddz)
-
Add PreloadAs PreloadOption to override the join alias when preloading a relationship with a left join. (thanks @daddz)
-
Add
AliasedAs()
method totableColumns
andtableWhere
types to use a custom alias. -
Add
AliasedAs()
method to generated relationship join mods. This is avaible in two places:-
one to change the alias of the table being queried
models.SelectJoins.Jets.AliasedAs("j").InnerJoin.Pilots(ctx)
-
and the other to change the alias of the relationship.
models.SelectJoins.Jets.InnerJoin.Pilots(ctx).AliasedAs("p")
-
-
Add
fm
mods to all supported dialects (psql, mysql and sqlite). These are mods for functions and are used to modify the function call. For example:// import "github.com/stephenafamo/bob/dialect/psql/fm" psql.F( "count", "*",)(fm.Filter(psql.Quote("status").EQ(psql.S("done"))))
-
Add
MustCreate
,MustCreateMany
,CreateOrFail
andCreateManyOrFail
methods to generated factory Templates
-
Change the function call point for generated relationship join mods. This reduces the amount of allocations and only does the work for the relationship being used.
// Before models.SelectJoins(ctx).Jets.InnerJoin.Pilots // After models.SelectJoins.Jets.InnerJoin.Pilots(ctx)
-
Changed the
Count()
function onViews
to clone the query instead of changing the existing one. This makes queries reusable and theCount()
function to behave as one would expect.// This now works as expected query := models.Jets.Query(ctx, db, /** list of various mods **/) count, err := query.Count() items, err := query.All()
-
Changed how functions are modified. Instead of chained methods, the
F()
starter now returns a function which can be called with mods:// Before psql.F( "count", "*",).FilterWhere(psql.Quote("status").EQ(psql.S("done"))), // After // import "github.com/stephenafamo/bob/dialect/psql/fm" psql.F( "count", "*",)(fm.Filter(psql.Quote("status").EQ(psql.S("done")))),
This makes it possible to support more queries.
-
Use
netip.Addr
instead ofnetip.Prefix
for Postgrescidr
type. -
Use
decimal.Decimal
instead ofstring
for Postgresmoney
type. -
Use
net.HardwareAddr
for Postgresmacaddr8
type, in addition to themacaddr
type. -
Code generation now generates struct tags for the generated model Setters as well, if configured through the
Tags
configuration option. Previoulsy, only the model struct fields were tagged. (thanks @singhsays)
- Remove
TableWhere
function from the generated code. It was not used by the rest of the generated code and offered no clear benefit. - Removed
As
starter. It takes anExpression
and is not needed since theExpression
has anAs
method which can be used directly.
- Fix a bug with
types.Stringer[T]
where the wrong value was returned in theValue()
method.
- Use
netip.Prefix
instead ofnetip.Addr
for postgres inet column type. This makes it possible to contain a subnet. - Allow underscores in enum variable names.
- Fix an issue with title casing enum values
- Add
bobgen-sql
a code generation driver for SQL schema files. Supports PostgreSQL and SQLite. - Add new properties
compare_expr
andcompare_expr_imports
to thetypes
configuration. This is used when comparing primary keys and in testing. - Add
never_required
to relationships configuration. This makes sure the factories does not require the relationship to be set. Useful if you're not using foreign keys. (thanks @jacobmolby) - Add wrapper types for Stringer, TextMarshaler/Unmarshaler, and BinaryMarshaler/Unmarshaler to the
types
configuration. - Make generated enum types implement the
fmt.Stringer
,encoding.TextMarshaler
,encoding.TextUnmarshaler
,encoding.BinaryMarshaler
andencoding.BinaryUnmarshaler
interfaces.
- Properly detect reference columns for implicit foreign keys in SQLite.
- Fix panic when generating random values for nullable columns. (thanks @jacobmolby)
- Sort relationships and imports for deterministic generated code. (thanks @jacobmolby)
- Correctly allow
OVER ()
with an empty window definition in PostgreSQL. (thanks @relvacode) - Set
GROUP BY
toNULL
if there are no expressions to group by. - Replace symbols in enum values with their unicode point to make them valid Go identifiers.
- Properly detect implicit foreign keys in SQLite.
- Fix issue with attaching multi-sided relationships. (thanks @jacobmolby)
- Update
github.com/jaswdr/faker
dependency fromv1
tov2
- Add randomization for all primitive types
- Add test for factory type randomization
- Drivers are now required to send in type definitions for generated types
- Custom types can now be configured at a top level in the config file
- Format generated files with
gofumpt
- Replace key in
replacements
configuration is now a string referring to the type defined in thetypes
configuration.
- Remove
Imports
from column definition.
INTEGER
columns are now correctly generated asint32
notint
- Fix panic when inferring
modify
for relationships with no primary key - Skip factory enum generation if there are no enums
- Return
sql.ErrNoRows
when Insert/Upsert returns nothing
- Do not wrap
Setter.Expressions()
in parenthesis
- Add
bob.Cache()
which saves the built SQL and args to prevent rebuilding the same query multiple times. - Add
As()
starter to alias expressions - Add
OP()
builder method for using custom operators - Add
table.InsertQ(ctx, db)
now includes the insert columns from the table model. - It is now possible to configure additional constraints for code generation.
- Add
um.SetCol()
which maintains the old behavior ofum.Set()
. - Generate additional
Expressions()
method for Setters to make it easier to use them inum.Set()
orim.Set()
.
- Aliases configuration for code generation no longer has a top level
table
key - When configuring relationships,
from_unique
,to_unique
,key_nullable
can no longer be configured. They are now inferred from the database. - When configuring relationships,
to_key
has been changed tomodify
and should be set tofrom
,to
or""
to indicate which side of the relationship to modify. If left empty, Bob will try to guess which side to modify based on the presence of primary keys and unique columns. RelWhere.Value
is nowRelWhere.SQLValue
- Change CONFLICT/DUPLICATE KEY UPDATE to use mods instead of a chainable methods.
- Change
um.Set()
to take a list of expressions. - Rename Setter method from
Insert()
toInsertMod()
to avoid confusion.
- Prevent generating duplicate relationships for many-to-many self-join relationships
- Correctly use table alias in generated relationship join mods
- Fix an issue where CTEs were encased in double parenthesis
- Fix invalid SQL generated when doing
JOIN USING
- Correctly include "AS" in function query if alias is set
- Setters are also generated for tables that have relationships, even if they have no primary key
- Column aliases in CTEs are now correctly included in the final query
- Fix several issues with generating code for multi-sided relationships
- Fix an issue where loading many-to-many relationships cause no columns to be selected unless specified
- Fix an issue where many-to-many relationships would not be able to use nested loaders
- Expand expressions when used in Raw (thanks @RangelReale)
- Add
InsertQ
,UpdateQ
, andDeleteQ
methods to Table models to start INSERT, UPDATE and DELETE queries respectively. - Allow column comment for replacement matching (thanks @jroenf)
- Add table query hooks to modify model queries
- Include
WhereOr
andWhereAnd
to make it easier to combine multiple generated where clauses - Print a warning if a replacement rule did not find a match (thanks @jacobmolby)
- Export generated factory.Factory struct
- Allow Limit and Offset to be used as Arguments in PostgreSQL (thanks @RangelReale)
- Make model hooks take slices not single objects
- Return rows affected from
Exec()
method of view queries instead ofsql.Result
- Chain comparison methods now take an
Expression
instead ofany
- Table models now require the types to implement
orm.Table
andorm.Setter
interfaces.
- Remove UpdateAll and DeleteAll methods on the Table models.
- Honor Only and Except in sqlite driver
- Always surround subqueries with parenthesis when used as an expression
- Fix mysql
TablesInfo
method and make sure we don't exclude entire table when targeting columns (thanks @jacobmolby) - Fix bug in sqlite foreign key and join table detection
- Fix
Upsert
andUpsertAll
methods ofmysql.Table
- Force uniqueness of relationship names in
psql
driver
- Fix panic when attaching associated relationships
- Make getting a random integer for preloading thread-safe
- Check all members when loading relationships
- Fix panic in Insert/Attach Relationship loop
- Replace
huandu/go-clone
withqdm12/reprint
- Fix cloning bug by replacing
jinzhu/copier
withhuandu/go-clone
- Account for windows when calculating models module path
- Update the generated code to use
github.com/gofrs/uuid/v5
- Fix bug where auto-increment columns were marked as generated in the SQLite driver
- Add the
PreloadWhere
preload mod to filter what relation should be preloaded. ViewQuery
now embedsbob.BaseQuery
giving it additional methods likeApply
andBuild
- Add plugin support. Currently 3 plugin hooks are provided.
PlugState
,PlugDBInfo
andPlugTemplateData
.
- Make
View.Name()
return a dialect-specific expression - Improve
Debug
helpers.Debug
writes query output to stdoutDebugToWriter
writes the query output to anyio.Writer
with a fallback to stdout.DebugToPrinter
prints the query with a givenbob.DebugPrinter
. Also falls back to stdout.
- Rename
OnlyColumns
toPreloadOnly
andExceptColumns
toPreloadExcept
to be more consistent with the newly addedPreloadWhere
. JoinChain.On
now takes Expressions instead ofany
.JoinChain.Using
now takes strings instead ofany
.- Export
gen.TemplateData
- Remove
ILIKE
operator from MySQL since it is not supported. - Removed
Destination()
method in driver interface. - Removed
PackageName()
method in driver interface.
- Account for possible clashes between column and relationship alias
- Make
Preload
mods work the same way as other query mods - Avoid overwriting manually flipped relationships.
- Account for potentially null relationship in Load methods
- Fix
On
method for JoinChain inmysql
andsqlite
- Add
LIKE
andILIKE
operators - Print generated files
- Add
no_reverse
option for user-configured relationships
- Move common parts of loading to shared internal package
- Fix generated joins for multi-sided relationships
- Account for relationships with tables that do not have a primary key in models and factories
- Properly extract preloaders nested in
mods.QueryMods
- Fix bug in preloading with multiple sides
- Fix issue with multi-sided relationships that include views
- Make self-referencing foreign keys work
- Tweak factory types to make collisions even less likely
- Comparison methods now require an expression (
bob.Expression
)
- No more
X
builder start function - No more
P
builder method
- Fix bug with args in table updates
- Fix some typos in documentation (thanks @leonardtan13)
- Fix a bug when multiple multi-column foreign keys exist
- Multiple internal changes to the generator to make it easier to write a custom entrypoint
- More robust testing of code generatio