cqllint
cqllint is a Go linter that checks that cql queries will not generate run-time errors.
While, in most cases, queries created using cql are checked at compile time, there are still some cases that can generate run-time errors (see Runtime errors).
cqllint analyses the Go code written to detect these cases and fix them without the need to execute the query. It also adds other detections that would not generate runtime errors but are possible misuses of cql.
Note
At the moment, only the errors cql.ErrFieldModelNotConcerned, cql.ErrFieldIsRepeated, cql.ErrAppearanceMustBeSelected and cql.ErrAppearanceOutOfRange are detected.
We recommend integrating cqllint into your CI so that the use of cql ensures 100% that your queries will be executed correctly.
Installation
For simply installing it, use:
go install github.com/FrancoLiberali/cql/cqllint@latest
Warning
The version of cqllint used must be the same as the version of cql. You can install a specific version using go install github.com/FrancoLiberali/cql/cqllint@vX.Y.Z, where X.Y.Z is the version number.
Execution
cqllint can be used independently by running:
cqllint ./...
or using go vet:
go vet -vettool=$(which cqllint) ./...
Errors
ErrFieldModelNotConcerned
The simplest example this error case is trying to make a comparison with an attribute of a model that is not joined by the query:
1_, err := cql.Query[models.Brand](
2 db,
3 conditions.Brand.Name.IsDynamic().Eq(conditions.City.Name.Value()),
4).Find()
If we execute this query we will obtain an error of type cql.ErrFieldModelNotConcerned with the following message:
field's model is not concerned by the query (not joined); not concerned model: models.City; operator: Eq; model: models.Brand, field: Name
Now, if we run cqllint we will see the following report:
$ cqllint ./...
example.go:3: models.City is not joined by the query
ErrFieldIsRepeated
The simplest example this error case is trying to set the value of an attribute twice:
1_, err := cql.Update[models.Brand](
2 db,
3 conditions.Brand.Name.Is().Eq("nike"),
4).Set(
5 conditions.Brand.Name.Set().Eq("adidas"),
6 conditions.Brand.Name.Set().Eq("puma"),
7)
If we execute this query we will obtain an error of type cql.ErrFieldIsRepeated with the following message:
field is repeated; field: models.Brand.Name; method: Set
Now, if we run cqllint we will see the following report:
$ cqllint ./...
example.go:5: conditions.Brand.Name is repeated
example.go:6: conditions.Brand.Name is repeated
ErrAppearanceMustBeSelected
To generate this error we must join the same model more than once and not select the appearance number:
1_, err := cql.Query[models.Child](
2 db,
3 conditions.Child.Parent1(
4 conditions.Parent1.ParentParent(),
5 ),
6 conditions.Child.Parent2(
7 conditions.Parent2.ParentParent(),
8 ),
9 conditions.Child.ID.IsDynamic().Eq(conditions.ParentParent.ID.Value()),
10).Find()
If we execute this query we will obtain an error of type cql.ErrAppearanceMustBeSelected with the following message:
field's model appears more than once, select which one you want to use with Appearance; model: models.ParentParent; operator: Eq; model: models.Child, field: ID
Now, if we run cqllint we will see the following report:
$ cqllint ./...
example.go:9: models.ParentParent appears more than once, select which one you want to use with Appearance
ErrAppearanceOutOfRange
To generate this error we must use the Appearance method with a value greater than the number of appearances of a model:
1_, err := cql.Query[models.Phone](
2 db,
3 conditions.Phone.Brand(
4 conditions.Brand.Name.IsDynamic().Eq(conditions.Phone.Name.Appearance(1).Value()),
5 ),
6).Find()
If we execute this query we will obtain an error of type cql.ErrAppearanceOutOfRange with the following message:
selected appearance is bigger than field's model number of appearances; model: models.Phone; operator: Eq; model: models.Brand, field: Name
Now, if we run cqllint we will see the following report:
$ cqllint ./...
example.go:4: selected appearance is bigger than models.Phone's number of appearances
Misuses
Although some cases would not generate runtime errors, cqllint will detect them as they are possible misuses of cql.
Set the same value
This case occurs when making a Set of exactly the same value:
1_, err := cql.Update[models.Brand](
2 db,
3 conditions.Brand.Name.Is().Eq("nike"),
4).Set(
5 conditions.Brand.Name.Set().Dynamic(conditions.Brand.Name.Value()),
6)
If we run cqllint we will see the following report:
$ cqllint ./...
example.go:5: conditions.Brand.Name is set to itself
Unnecessary Appearance selection
This is the case when the Appearance method is used without being necessary, i.e. when the model appears only once:
1_, err := cql.Query[models.Phone](
2 db,
3 conditions.Phone.Brand(
4 conditions.Brand.Name.IsDynamic().Eq(conditions.Phone.Name.Appearance(0).Value()),
5 ),
6).Find()
If we run cqllint we will see the following report:
$ cqllint ./...
example.go:4: Appearance call not necessary, models.Phone appears only once