gnormal / gnorm

A database-first code generator for any language
https://gnorm.org
Other
487 stars 40 forks source link

Versus on website is disingenuous towards sqlboiler #103

Closed aarondl closed 5 years ago

aarondl commented 5 years ago

https://github.com/gnormal/gnorm/blob/master/site/content/gnorm-vs/_index.md

1. SqlBoiler only outputs Go code.
Gnorm outputs any langauge, even non-code things, like protobufs or docs.
SqlBoiler produces a fluent API style of code that is non-idiomatic for Go.
2. Gnorm can generate idiomatic go code.
SqlBoiler requires magic strings for queries (e.g. Where("age > ?", 30), which offer no type safety or compile-time checks. If you change the name or type of a column, that code won't fail until it's triggered by a test or in production.
3. Gnorm can generate type-safe queries that ensure that if a column's type or name change, your code will fail to compile.

Of these 1 and 3 are not true, as of version 3.0 you can generate whatever file types you want using sqlboiler using custom templates, and column names, relationship names (3.0 only), and table names are all generated as well which users can use as constants which will change when the database changes to ensure compiler errors. I'd appreciate if this text was rectified to be factually accurate as of the latest released version. Thanks!

natefinch commented 5 years ago

Thanks for bringing this to my attention. Totally did not intend to misconstrue what Sqlboiler was capable of.

natefinch commented 5 years ago

Let me know if you think this is accurate. I don't know everything about SQLBoiler, so I want to make sure I'm not mis-stating things.

  1. SQLBoiler's primary focus is outputting the code generated by its default templates. While it supports user-written templates, it's an advanced feature.
    • Gnorm's primary focus is user-written templates with full control of the output text and files (for example, the filenames and directories generated by Gnorm can use templated DB info).
  2. SQLBoiler defaults to producing a fluent API style of code that is non-idiomatic for Go.
    • Gnorm's example templates generate idiomatic go code (or non-idiomatic code, if that's what you want!).
  3. SQLBoiler uses dev-written strings for queries, and interface{} for parameters (for example Where(personTable.Age + " > ?", 30), which could lead to typos and type-incompatibility. If you change the type of a column, that code won't fail until it's triggered by a test or in production.
    • Gnorm can generate type-safe queries that ensure that if a column's type or name changes, your code will fail to compile.
  4. SQLBoiler is more mature than Gnorm, and offers features Gnorm's example templates don't, currently, such as support for complex joins.
aarondl commented 5 years ago

This is how I would write it:

  1. Workflow and Templates:

    • SQLBoiler's default workflow is to use the templates provided to get something working out of the box immediately, whereas writing your own templates is considered an advanced feature.
    • Gnorm's primary focus is user-written templates with full control of the output text and files (for example, the filenames and directories generated by Gnorm can use templated DB info).
  2. Generated API style:

    • SQLBoiler's default template set uses a fluent API which is typically viewed as non-idiomatic in Go.
    • Gnorm's example templates generate idiomatic go code (or non-idiomatic code, if that's what you want!).
  3. Type safety:

    • SQLBoiler uses type-unsafe interface{} and developer written strings for clauses where conditions are involved (where, inner join). Where(models.PersonColumns.Age + " > ?", 30) which can lead to typos and type-incompatibility. If you change the type of a column this code will not fail until it's used in a test or production. Only a name change will cause this to fail (unless you completely replace the templates with something type safe).
    • Gnorm on the other hand can generate type-safe queries that ensure if a column's type changes your code will fail to compile.
  4. Relationship support:

    • SQLBoiler supports complex joins and relationship queries which allows you to get the data you want efficiently out of your DB (one-to-one, one-to-many, many-to-many, eager loads all supported)
    • Gnorm's default templates do not support these out of the box just yet
  5. Database support:

    • SQLBoiler additionally supports mssql and sqlite on top of mysql and postgres
    • Gnorm only supports mysql and postgres
  6. Optimization:

    • SQLBoiler has been tuned for performance and outperforms every "ORM" solution in Go currently (but has not been tested against Gnorm since it is new)
    • Gnorm has yet to publish benchmarks against any ORM, but it should perform incredibly well since it's hand-written to solve the problem

Obviously because you are trying to "sell" your library I don't mind what you do. This is simply how I would write this section but it may be that you wouldn't want to include the last 3 points because like as the first 3 put Gnorm in a favorable light, the opposite is true for the last 3 since those are areas where SQLBoiler is very strong. My main goal here was to remove the errors in the facts with this issue though a true comparison would probably include all 6 points. I leave it to you to do as you will with what I've written here and trust that at the very least we've removed all the factual errors.

Also note I haven't seen any example of how Gnorm does it's 100% type safe where clause. Can you point out an example? I'm genuinely curious as an "ORM person" haha.

On a final note, it's too bad you had gripes with sqlboiler that we couldn't attempt to solve together. I saw your posts on reddit and even asked you about it once, and the difference just didn't seem large enough to me (because you could replace templates in sqlboiler with whatever you wanted, even 100% type safe code if you wanted). I don't recall ever being reached out to about changing sqlboiler up in a way that would make it work for you. Clearly I'm biased but it would have been great to work together on a solution instead of have two solutions that are so incredibly similar. At the end of the day they're just templating engines and sqlboiler just has more opinions on what comes out of the box whereas gnorm has more fine grained control over generated code. Anyway, thanks for addressing this issue.

natefinch commented 5 years ago

I really appreciate the fact checking. I definitely don't want to be misleading about SQLBoiler.

So, the type safety in Gnorm is just generating the right wrapper code for each type. So, for example, if you have a table with an integer column, we generate a struct with an int field, of course. But we also generate an "IntField" variable for each column like this: https://github.com/gnormal/postgres-go/blob/master/example/gnorm/books/books.go#L34

An IntField has a bunch of methods on it that return a WhereClause value that generates the right SQL for that type. So for example, you'd do like bks, err := books.Query(db, books.PagesCol.Equals(20)) . You can't typo the name of the column, because it's built into the generated code, and you can't pass it the wrong type, because the column value has methods that only accept the right type.

This is not to say that you can't still intentionally do the wrong thing if you want to generate your own WhereClause... but that is difficult and would be glaringly obvious at the call site.

There's more I could do to make it even more safe, but this seems like a good middle ground, where all the common errors are avoided without making the code too convoluted.

Yeah, when I was looking at SQLBoiler last year, I didn't realize one could swap out the templates. That definitely would have been an easier solution than writing my own tool :)

I honestly don't remember you reaching out, but I have a terrible memory. I hope I was nice, at least. :) . When I was looking at writing Gnorm, we had been using xo at work, and I was just annoyed at how it did everything, so I basically wrote Gnorm in response to xo. I looked around at alternatives, but SQLBoiler's docs focused mostly on the built-in templates, which produced code that I didn't like, so I didn't look much further into it.

natefinch commented 5 years ago

I copied all but 5 and 6... 5 because I have a blanket statement at the bottom about DB support, and 6 because, as you said, I haven't benchmarked gnorm and neither has anyone else... and that's mostly because whatever benchmark you do is entirely dependent on what the templates generate.