volatiletech / sqlboiler Goto Github PK
View Code? Open in Web Editor NEWGenerate a Go ORM tailored to your database schema.
License: BSD 3-Clause "New" or "Revised" License
Generate a Go ORM tailored to your database schema.
License: BSD 3-Clause "New" or "Revised" License
Bind:
- It needs to look for a fully qualified table name, so if you get an as with a dot, split on the dot, look for the table name. If you can't find a struct with that table name then you just bind to the column name in the current struct.
- If there is no dot, you first try to bind it to the from table struct if it exists in the object, if that doesn't exist then you just bind the column name to columns inside the object.
Eager loading:
- Eager loading is defined through the Load("tablename", ...querymods) query mod. Once that's there then bind will know to do additional queries for each load invocation (it splits up the query into multiple to get all column data). The Relationship struct on the model (exported) will be filled with the results of each one of these loads.
- For all the relationship stuffs it needs to short circuit on cached copies in the relationship struct. For example, if you see a user that's already loaded in the relationship struct, you don't do the query.
- Need a reload method for reloading cache data. Example:
v.Reload()
v.Relationships.Videos.Reload()```
/*
fix "count(thing)" to count(thing)
select colName
select count(*)
select count(colname)
select count(colname), colName
select count("colname") as "blah"
select count(colname) as blah, colName
select (select * from blah)
select (select * from blah) as blah
select "a"."id", "b"."id"
from "x" as "a"
inner join "y" "b" on "a"."id" = "b"."id"
select v.id, u.name
from videos as v
inner join users u on u.id=v.user_id
where v.id=15
Videos(
Select("v.title, u.name"),
InnerJoin("users u on u.id=v.user_id"),
Where("v.id=?", 15)
).Bind(&thing)
*/
Hi,
The db object is passed repetitively, for example:
err = p1.Insert(db)
pilot, err := models.PilotFind(db, 1)
pilot, err := models.Pilots(db, qm.Where("name=?", "Tim")).One()
Why we don't just pass the db object to the models package as a configuration?
We'll need starter queries to be able to pass something in
non-whitelist case: checks for all columns without default values, and includes that in the insert. it additionally appends any default values that have non-zero default values.
whitelist: it will only insert columns specified, default values is the same rule as non-whitelist.
returning: what is available in default values, minus what is being inserted in default values.
also add null library support for those types
Do you support Postgre's JSON/JSONB data type?
Hi,
At first thanks for such great tool! The workflow combined with both goose and sqlboiler works very well and conveys the impression of doing things in elegant manner :)
I have encountered a problem when ENUM type is used, for example:
CREATE TYPE workday AS ENUM('monday', 'tuesday', 'wednesday', 'thursday', 'friday');
CREATE TABLE event (
id SERIAL,
name VARCHAR(255),
day workday NOT NULL
);
ALTER TABLE event ADD CONSTRAINT event_key PRIMARY KEY(id);
Executing sqlboiler output warning:
Warning: Incompatible data type detected: workday
Thus the models directory is created, running tests
go test models/
results in massive fail log:
--- FAIL: TestDelete (0.00s)
--- FAIL: TestDelete/Events (0.01s)
event_test.go:35: models: unable to insert into event: pq: invalid input value for enum workday: ""
event_test.go:39: models: unable to delete from event: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:44: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
--- FAIL: TestQueryDeleteAll (0.00s)
--- FAIL: TestQueryDeleteAll/Events (0.00s)
event_test.go:65: models: unable to insert into event: pq: invalid input value for enum workday: ""
event_test.go:69: models: unable to delete all from event: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:74: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
--- FAIL: TestSliceDeleteAll (0.00s)
--- FAIL: TestSliceDeleteAll/Events (0.00s)
event_test.go:95: models: unable to insert into event: pq: invalid input value for enum workday: ""
event_test.go:101: models: unable to delete all from event slice: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:106: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
--- FAIL: TestExists (0.00s)
--- FAIL: TestExists/Events (0.00s)
event_test.go:126: models: unable to insert into event: pq: invalid input value for enum workday: "b"
event_test.go:131: Unable to check if Event exists: models: unable to check if event exists: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:134: Expected EventExistsG to return true, but got false.
--- FAIL: TestFind (0.00s)
--- FAIL: TestFind/Events (0.00s)
event_test.go:150: models: unable to insert into event: pq: invalid input value for enum workday: "b"
event_test.go:155: models: unable to select from event: bind failed to execute query: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:159: want a record, got nil
--- FAIL: TestBind (0.00s)
--- FAIL: TestBind/Events (0.00s)
event_test.go:175: models: unable to insert into event: pq: invalid input value for enum workday: "b"
event_test.go:179: bind failed to execute query: pq: current transaction is aborted, commands ignored until end of transaction block
--- FAIL: TestOne (0.00s)
--- FAIL: TestOne/Events (0.00s)
event_test.go:196: models: unable to insert into event: pq: invalid input value for enum workday: "b"
event_test.go:200: models: failed to execute a one query for event: bind failed to execute query: pq: current transaction is aborted, commands ignored until end of transaction block
--- FAIL: TestAll (0.00s)
--- FAIL: TestAll/Events (0.00s)
event_test.go:223: models: unable to insert into event: pq: invalid input value for enum workday: "a"
event_test.go:226: models: unable to insert into event: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:231: models: failed to assign all query results to Event slice: bind failed to execute query: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:235: want 2 records, got: 0
--- FAIL: TestCount (0.00s)
--- FAIL: TestCount/Events (0.00s)
event_test.go:256: models: unable to insert into event: pq: invalid input value for enum workday: "a"
event_test.go:259: models: unable to insert into event: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:264: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:268: want 2 records, got: 0
--- FAIL: TestInsert (0.00s)
--- FAIL: TestInsert/Events (0.00s)
event_test.go:423: models: unable to insert into event: pq: invalid input value for enum workday: "b"
event_test.go:428: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:432: want one record, got: 0
--- FAIL: TestInsert/Events#01 (0.01s)
event_test.go:449: models: unable to insert into event: pq: invalid input value for enum workday: ""
event_test.go:454: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:458: want one record, got: 0
--- FAIL: TestReload (0.00s)
--- FAIL: TestReload/Events (0.00s)
event_test.go:475: models: unable to insert into event: pq: invalid input value for enum workday: "b"
event_test.go:479: models: unable to select from event: bind failed to execute query: pq: current transaction is aborted, commands ignored until end of transaction block
--- FAIL: TestReloadAll (0.00s)
--- FAIL: TestReloadAll/Events (0.00s)
event_test.go:496: models: unable to insert into event: pq: invalid input value for enum workday: "b"
event_test.go:502: models: unable to reload all in EventSlice: bind failed to execute query: pq: current transaction is aborted, commands ignored until end of transaction block
--- FAIL: TestSelect (0.00s)
--- FAIL: TestSelect/Events (0.00s)
event_test.go:518: models: unable to insert into event: pq: invalid input value for enum workday: "b"
event_test.go:523: models: failed to assign all query results to Event slice: bind failed to execute query: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:527: want one record, got: 0
--- FAIL: TestUpdate (0.00s)
--- FAIL: TestUpdate/Events (0.00s)
event_test.go:553: models: unable to insert into event: pq: invalid input value for enum workday: ""
event_test.go:558: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:562: want one record, got: 0
event_test.go:570: models: unable to update event row: pq: current transaction is aborted, commands ignored until end of transaction block
--- FAIL: TestSliceUpdateAll (0.00s)
--- FAIL: TestSliceUpdateAll/Events (0.00s)
event_test.go:591: models: unable to insert into event: pq: invalid input value for enum workday: ""
event_test.go:596: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:600: want one record, got: 0
event_test.go:626: models: unable to update all in event slice: pq: current transaction is aborted, commands ignored until end of transaction block
--- FAIL: TestUpsert (0.00s)
--- FAIL: TestUpsert/Events (0.00s)
event_test.go:647: Unable to upsert Event: models: unable to upsert for event: pq: syntax error at or near "ON"
event_test.go:652: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:655: want one record, got: 0
event_test.go:664: Unable to upsert Event: models: unable to upsert for event: pq: syntax error at or near "ON"
event_test.go:669: models: failed to count event rows: pq: current transaction is aborted, commands ignored until end of transaction block
event_test.go:672: want one record, got: 0
models/event.go
type Event struct {
ID int `boil:"id" json:"id" toml:"id" yaml:"id"`
Name null.String `boil:"name" json:"name,omitempty" toml:"name" yaml:"name,omitempty"`
Day string `boil:"day" json:"day,omitempty" toml:"day" yaml:"day,omitempty"`
R *eventR `boil:"-" json:"-" toml:"-" yaml:"-"`
L eventL `boil:"-" json:"-" toml:"-" yaml:"-"`
}
If combine the warning, test output and generated code the answer is simple: enum type is not handled properly and it is treated as string type.
Are there any workarounds (other than editing generated files) for such case? (or maybe I have done something wrong?).
Thanks for your help!
Have a nice day
EDIT:
Of course the easiest workaround:
create table workday (
name VARCHAR(20) PRIMARY KEY
);
SetX doesn't work when you can have a nullable foreign key. This is an easy fix and results from manually assigning the null.Int.Int value but not setting null.Int.Valid to true.
For example, in this scenario, every user might have a plan, but the foreign key plan_id
can be null if they don't yet have a plan assigned. So, the generated code represents plan_id
as a null.Int
CREATE TYPE plan_name AS ENUM ('trial', 'small', 'medium', 'large', 'cancel', 'unpaid');
CREATE TABLE plans (
id serial NOT NULL,
created_at timestamp,
updated_at timestamp,
start timestamp NOT NULL,
expire timestamp,
name plan_name NOT NULL,
user_name text NOT NULL
);
ALTER TABLE plans ADD CONSTRAINT plan_pkey PRIMARY KEY (id);
CREATE TABLE users (
id serial NOT NULL,
name text UNIQUE NOT NULL,
password text NOT NULL,
email text UNIQUE NOT NULL,
first_name text,
last_name text,
plan_id integer,
created_at timestamp,
updated_at timestamp
);
ALTER TABLE users ADD CONSTRAINT user_pkey PRIMARY KEY (id);
ALTER TABLE users ADD CONSTRAINT plans_fkey FOREIGN KEY (plan_id) REFERENCES plans(id);
In the generated code, you have a user.SetPlan()
method that is implemented like this:
func (u *User) SetPlan(exec boil.Executor, insert bool, related *Plan) error {
var err error
if insert {
if err = related.Insert(exec); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
}
}
oldVal := u.PlanID
u.PlanID.Int = related.ID
// u.PlanID.Valid = true <========== needs this line
if err = u.Update(exec, "plan_id"); err != nil {
u.PlanID = oldVal
return errors.Wrap(err, "failed to update local table")
}
The generated code reaches into the null.Int
struct to set the Int
value directly, but does not set Valid
to true. This causes the subsequent update to silently fail with no error. Should probably be using null.IntFrom(related.ID)
rather than directly manipulating the struct values.
If I use a uuid
(as a primary key) in a postgres table, sqlboiler generates a string
field in the model. Is there a reasoning behind this?
I'm not too bothered by this, but I think it should either be [16]byte
or a type UUID [16]byte
. It would make using https://github.com/satori/go.uuid easier, and it would consume less memory as it would be 16 bytes instead of 36 bytes (characters) plus the string header.
CC @teasherm
It would be nice to have "include" option in configuration that would allow us to specify just the tables that we want to be processed by the generator, as opposite to "exclude".
A cleaner debug logging option for all generated statements. A Debug query mod to debug single statements would be useful too.
In this situation, I have a table, with a child table, which in turn has multiple children. I want to eager load all of these. The test case:
CREATE SCHEMA test_schema;
CREATE TABLE test_schema.parents (
id BIGSERIAL PRIMARY KEY,
name TEXT
);
CREATE TABLE test_schema.children (
id BIGSERIAL PRIMARY KEY,
parent_id BIGINT REFERENCES test_schema.parents(id),
name TEXT
);
CREATE TABLE test_schema.third (
id BIGSERIAL PRIMARY KEY,
children_id BIGINT REFERENCES test_schema.children(id),
name TEXT
);
CREATE TABLE test_schema.another (
id BIGSERIAL PRIMARY KEY,
children_id BIGINT REFERENCES test_schema.children(id),
name TEXT
);
INSERT INTO test_schema.parents (name) values ('P1');
INSERT INTO test_schema.parents (name) values ('P2');
INSERT INTO test_schema.children (parent_id, name) values ((select id from test_schema.parents where name = 'P1'), 'C11');
INSERT INTO test_schema.children (parent_id, name) values ((select id from test_schema.parents where name = 'P2'), 'C21');
INSERT INTO test_schema.children (parent_id, name) values ((select id from test_schema.parents where name = 'P2'), 'C22');
INSERT INTO test_schema.third (children_id, name) values ((select id from test_schema.children where name = 'C21'), 'T211');
INSERT INTO test_schema.third (children_id, name) values ((select id from test_schema.children where name = 'C21'), 'T212');
INSERT INTO test_schema.another (children_id, name) values ((select id from test_schema.children where name = 'C21'), 'A211');
Generate models, and then run:
package main
import (
"database/sql"
"fmt"
"github.com/jseriff/sqltest/models"
_ "github.com/lib/pq"
"github.com/vattle/sqlboiler/boil"
"github.com/vattle/sqlboiler/queries/qm"
)
func main() {
db, _ := sql.Open("postgres", "postgres://...?options=+-c+search_path%%3Dtest_schema")
boil.DebugMode = true
parents, _ := models.Parents(db,
qm.Load("Children.Thirds"),
qm.Load("Children.Anothers"),
).All()
}
The debug output generated here is:
SELECT * FROM "test_schema"."parents";
[]
select * from "test_schema"."children" where "parent_id" in ($1,$2)
[1 2]
select * from "test_schema"."another" where "children_id" in ($1,$2,$3)
[1 2 3]
Notice, that "another" is queried, but "thirds" is not. If you switch the order of the qm.Load calls, you will find the opposite is true.
Uid
Uuid
Guid
Ssn
Tz
Similar to #58, if I use a "char"
column on postgres, this gets mapped to string
in the generated model. Like the other issue, this isn't a dealbreaker but it would be much better to have it as byte
. rune
could also be used, but it's a little silly to use a 4-byte type when "char"
in postgres is 1-byte.
Will you add a feature to generate types for views in the future?
Currently it is not possible, because Views don't have primary keys.
We're considering doing this but it's a breaking change.
So far we can't come up with any real use case of when you would want to use this, but we see it in other public APIs. If you require this feature please Thumbs up this issue instead of making a new one.
So people get a better idea of what I mean I'll edit here:
Currently queries like Update/Delete etc do not return rows affected:
jet, err := models.Jets(tx).One()
jet.Fast = true
err := jet.Update(tx)
jets, err := models.Jets(tx).All()
err := jets.UpdateAll(tx, models.M{"fast": true})
After this change they would:
jet, err := models.Jets(tx).One()
jet.Fast = true
affected, err := jet.Update(tx)
jets, err := models.Jets(tx).All()
affected, err := jets.UpdateAll(tx, models.M{"fast": true})
This is a breaking change and will be driven by user need. Please let us know.
We have a table which can reference back to itself in a many to many relationship. When generating the schema, we get the an error.
Here are the (abbreviated) tables in question, pulled from a database dump:
CREATE TABLE industries (
id integer NOT NULL,
name text
);
ALTER TABLE ONLY industries
ADD CONSTRAINT industries_pkey PRIMARY KEY (id);
CREATE TABLE industries_industries (
industry_id integer NOT NULL,
mapped_industry_id integer NOT NULL,
relation text NOT NULL
);
ALTER TABLE ONLY industries_industries
ADD CONSTRAINT industries_industries_pkey PRIMARY KEY (industry_id, mapped_industry_id);
ALTER TABLE ONLY industries_industries
ADD CONSTRAINT industries_industries_industry_id_fkey FOREIGN KEY (industry_id) REFERENCES industries(id);
ALTER TABLE ONLY industries_industries
ADD CONSTRAINT industries_industries_mapped_industry_id_fkey FOREIGN KEY (mapped_industry_id) REFERENCES industries(id);
Upon running sqlboiler, I get the following output:
Error: 3:22: expected ')', found '(' (and 10 more errors)
failed to format template
0 // G retrieves all the industry's .
1 func (i *Industry) G(mods ...qm.QueryMod) Query {
2 return i.(boil.GetDB(), mods...)
>>> }
4
5 // retrieves all the industry's with an executor.
6 func (i *Industry) (exec boil.Executor, mods ...qm.QueryMod) Query {
7 queryMods := []qm.QueryMod{
8 qm.Select("\"a\".*"),
To generate this output, I ran the sql script above in its own schema, and then used:
sqlboiler -s test_schema postgres
when support mysql?
type VideoSlice []*Video
type videoQuery struct {
*boil.Query
}
All:
func Videos(querymods) videoQuery
func VideosX(executor, querymods) videoQuery
Delete:
func (v *Video) Delete(...querymods) error
func (v videoQuery) DeleteAll() error
Find:
func VideoFind(id int64, selectList ...string) (*Video, error)
func VideoFindX(executor, id int64, selectList ...string) (*Video, error)
Insert:
func (v *Video) Insert(querymods) error
func (v *Video) InsertX(executor, querymods) error
Update:
func (v *Video) Update(whitelist ... string) error
func (v *Video) UpdateX(executor, whitelist ... string) error
func (v videoQuery) UpdateAll(boil.M) error
Bind:
func (q *Query) Bind(interface{}) error
Do functions:
func (videoQuery) One() (*Video, error)
func (videoQuery) All() (VideoSlice, error)
Hi there!
First of all, I want to start by thanking you guys for this great project.
Now, on to the helping part:
In my project, we're using gb as the primary build tool (although we experienced the same issues with the go tool)
Sqlboiler version is tag 2.0.1
$ gb test ./models
Password: *types password*
Password: *types password*
Password: Password: *types password*
(hangs forever)
This is my sqlboiler.toml:
[postgres]
dbname="mydbname"
host="localhost"
port=5432
user="postgres"
pass="postgres"
Any idea what could be happening?
Thanks!
Do we want a paginate method like in ActiveRecord?
@battles = Battle
.unscoped
.where('challengee_id = ? or challenger_id = ?', v.id, v.id)
.order('id desc')
.paginate(page: params[:page], per_page: 100)
What is needed to create the adapter?
When doing an eager, the R field is left as null if no eager objects are loaded. I think a non-null R, with an empty array in the loaded relationship would be more appropriate.
To reproduce, here is the schema I used:
CREATE SCHEMA test_schema;
CREATE TABLE test_schema.parents (
id BIGSERIAL PRIMARY KEY,
name TEXT
);
CREATE TABLE test_schema.children (
id BIGSERIAL PRIMARY KEY,
parent_id BIGINT REFERENCES test_schema.parents(id),
name TEXT
);
INSERT INTO test_schema.parents (name) values ('P1');
INSERT INTO test_schema.parents (name) values ('P2');
INSERT INTO test_schema.children (parent_id, name) values ((select id from test_schema.parents where name = 'P2'), 'C21');
INSERT INTO test_schema.children (parent_id, name) values ((select id from test_schema.parents where name = 'P2'), 'C22');
So, P1 has no children, and P2 has two children. Create the models for this schema, then run the following:
package main
import (
"database/sql"
"fmt"
"github.com/jseriff/sqltest/models"
_ "github.com/lib/pq"
"github.com/vattle/sqlboiler/queries/qm"
)
func main() {
db, _ := sql.Open("postgres", "postgres://evsr_development:evsr_development@localhost:5432/evsr_development?options=+-c+search_path%%3Dtest_schema")
parents, _ := models.Parents(db,
qm.Load("Children"),
).All()
for i := range parents {
fmt.Printf("Parent %d.R: %v\n", i, parents[i].R)
}
}
The output is:
Parent 0.R: <nil>
Parent 1.R: &{[0xc4200118c0 0xc420011900]}
You can see in the output that Parent 0.R is nil. This forces me to check R for nil before looking at the relationship, adding quite a bit of verbosity.
Maybe I am wrong but currently there is only support for 'public' schema, right?
It would be nice to probably add a configuration option for specifying other schemas as well.
i've got the following table (postgres):
create table if not exists posts (
id serial primary key not null,
uuid uuid not null default (uuid_generate_v4()) unique,
created_at_utc timestamp without time zone not null default (now() at time zone 'utc'),
approved boolean not null default false,
flagged boolean not null default false,
-- problem here:
parent_uuid uuid references posts (uuid),
--
updated_at_utc timestamp without time zone default null
);
but the generated code gives the following errors when building:
models/posts.go:330: cannot call non-function p.ParentUUID (type null.String)
models/posts.go:334: type Post has both field and method named ParentUUID
am i doing something wrong? is there a way to name things that won't cause this problem? Thanks in advance! Other than this minor issue, I'm really enjoying using this!
I use Mysql workbench to design tables, the database works fine, but sqlboiler can't recognize the join table. Below is the sql scripts, store_coupons is the join table.
-- -----------------------------------------------------
-- Table `htk_dev`.`coupons`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `htk_dev`.`coupons` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`category` INT(11) NOT NULL DEFAULT '0',
`user_id` INT(11) NOT NULL,
`created_at` DATETIME NOT NULL,
`count` INT(11) NOT NULL,
`remain_count` INT(11) NOT NULL,
`content` TEXT NOT NULL,
PRIMARY KEY (`id`),
INDEX `index_coupons_on_user_id` (`user_id` ASC),
CONSTRAINT `fk_rails_69b54b3afe`
FOREIGN KEY (`user_id`)
REFERENCES `htk_dev`.`users` (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `htk_dev`.`stores`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `htk_dev`.`stores` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`user_id` INT(11) NOT NULL,
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
`license_url` VARCHAR(255) NOT NULL,
`verify` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
INDEX `index_stores_on_user_id` (`user_id` ASC),
CONSTRAINT `fk_rails_b526db2ffb`
FOREIGN KEY (`user_id`)
REFERENCES `htk_dev`.`users` (`id`))
ENGINE = InnoDB
AUTO_INCREMENT = 8
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `htk_dev`.`store_coupons`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `htk_dev`.`store_coupons` (
`stores_id` INT(11) NOT NULL,
`coupons_id` INT(11) NOT NULL,
PRIMARY KEY (`stores_id`, `coupons_id`),
INDEX `fk_stores_has_coupons_coupons1_idx` (`coupons_id` ASC),
INDEX `fk_stores_has_coupons_stores1_idx` (`stores_id` ASC),
CONSTRAINT `fk_stores_has_coupons_coupons1`
FOREIGN KEY (`coupons_id`)
REFERENCES `htk_dev`.`coupons` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_stores_has_coupons_stores1`
FOREIGN KEY (`stores_id`)
REFERENCES `htk_dev`.`stores` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
I want to be able to easily embed sqlboiler
into my own single-binary Cobra project
main
package into discrete sub-packages as needed (e.g. cmd
)I'd be happy to draft a PR for review, but would like to see what you think about the idea first.
Let me know if I can clarify anything.
Try to change prerun and postrun to PreRunE and PostRunE and bubble up errors to main func opposed to errorQuit().
Remove all commented out code that isnt be used, and use linter tool to check for any unused functions in the project for removal.
For 1.1, to expand each query type functionality (update, delete, upsert etc) we can consider having the function signatures take a *[]StatementMod that allow for complex modification of the statements. Example:
type UpsertMod func(q *boil.Thing)
type UpsertMods *[]UpsertMod
func Conflict(cols ...string) UpsertMod
func Update(cols ...string) UpsertMod
func Where(clause string, args ...interface{}) UpsertMod
func Upsert(update bool, mods UpsertMods, whitelist ...string)
This allows for complex modifications of the query statements without requiring a bucketload of additional function params.
When attempting a multi-level eager load, I am seeing a reflection error. Here is a reproduction case:
I constructed a simple 3 level hierarchy to show this error:
CREATE SCHEMA test_schema;
CREATE TABLE test_schema.parents (
id BIGSERIAL PRIMARY KEY,
name TEXT
);
CREATE TABLE test_schema.children (
id BIGSERIAL PRIMARY KEY,
parent_id BIGINT REFERENCES test_schema.parents(id),
name TEXT
);
CREATE TABLE test_schema.third (
id BIGSERIAL PRIMARY KEY,
children_id BIGINT REFERENCES test_schema.children(id),
name TEXT
);
INSERT INTO test_schema.parents (name) values ('P1');
INSERT INTO test_schema.parents (name) values ('P2');
INSERT INTO test_schema.children (parent_id, name) values ((select id from test_schema.parents where name = 'P1'), 'C11');
INSERT INTO test_schema.children (parent_id, name) values ((select id from test_schema.parents where name = 'P2'), 'C21');
INSERT INTO test_schema.children (parent_id, name) values ((select id from test_schema.parents where name = 'P2'), 'C22');
INSERT INTO test_schema.third (children_id, name) values ((select id from test_schema.children where name = 'C21'), 'T211');
INSERT INTO test_schema.third (children_id, name) values ((select id from test_schema.children where name = 'C21'), 'T212');
Generated the models on this schema, and then ran a the following code:
package main
import (
"database/sql"
"github.com/jseriff/sqltest/models"
_ "github.com/lib/pq"
"github.com/vattle/sqlboiler/queries/qm"
)
func main() {
db, _ := sql.Open("postgres", "postgres://evsr_development:evsr_development@localhost:5432/evsr_development?options=+-c+search_path%%3Dtest_schema")
_, _ = models.Parents(db,
qm.Load("Children.Thirds"),
).All()
}
I receive the following error:
panic: reflect.Value.Addr of unaddressable value
goroutine 1 [running]:
panic(0x2acb60, 0xc4201ba2a0)
/usr/local/Cellar/go/1.7/libexec/src/runtime/panic.go:500 +0x1a1
reflect.Value.Addr(0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
/usr/local/Cellar/go/1.7/libexec/src/reflect/value.go:239 +0xbc
github.com/vattle/sqlboiler/queries.loadRelationshipState.loadRelationshipsRecurse(0x4546c0, 0xc420016630, 0xc4200db260, 0xc42011e920, 0x2, 0x2, 0x1, 0x2e2bc0, 0xc4200db200, 0x199, ...)
/Users/jseriff/go/src/github.com/vattle/sqlboiler/queries/eager_load.go:145 +0x337
github.com/vattle/sqlboiler/queries.loadRelationshipState.loadRelationships(0x4546c0, 0xc420016630, 0xc4200db260, 0xc42011e920, 0x2, 0x2, 0x1, 0x2eaf60, 0xc420014d20, 0x2, ...)
/Users/jseriff/go/src/github.com/vattle/sqlboiler/queries/eager_load.go:126 +0x2c3
github.com/vattle/sqlboiler/queries.(*Query).Bind(0xc4200a9b80, 0x2eaf60, 0xc420014d20, 0x0, 0x0)
/Users/jseriff/go/src/github.com/vattle/sqlboiler/queries/reflect.go:123 +0x2d6
github.com/jseriff/sqltest/models.parentQuery.All(0xc4200a9b80, 0xc420016630, 0xc42000d660, 0x2, 0x2, 0xc4200a9b80)
/Users/jseriff/go/src/github.com/jseriff/sqltest/models/parents.go:254 +0x71
main.main()
/Users/jseriff/go/src/github.com/jseriff/sqltest/main.go:16 +0xea
exit status 2
I spent a few minutes looking into loadRelationships - it looks like when it recurses, the loadRelationshipsRecurse method is expecting to receive the "Children"'s R, but instead receives the "Parent"s R, which does not have a Thirds member.
p, err := Profiles(Where("id = $1", 111)).One()
Profiles(UserM(1)).One()
that way we dont need to bind when we dont need a bind
Possibly related to #45, but I see a different behavior, so I wanted to file a separate ticket.
Here is the SQL this time:
CREATE TABLE industries (
id integer NOT NULL,
name text,
parent_id integer
);
ALTER TABLE ONLY industries
ADD CONSTRAINT industries_pkey PRIMARY KEY (id);
ALTER TABLE ONLY industries
ADD CONSTRAINT industries_parent_id_fkey FOREIGN KEY (parent_id) REFERENCES industries(id);
So, parent_id references another industry.
The models build works, but generates invalid code:
type Industry struct {
ID int `boil:"id" json:"id" toml:"id" yaml:"id"`
Name null.String `boil:"name" json:"name,omitempty" toml:"name" yaml:"name,omitempty"`
ParentID null.Int `boil:"parent_id" json:"parent_id,omitempty" toml:"parent_id" yaml:"parent_id,omitempty"`
R *industryR `boil:"-" json:"-" toml:"-" yaml:"-"`
L industryL `boil:"-" json:"-" toml:"-" yaml:"-"`
}
// industryR is where relationships are stored.
type industryR struct {
Parent *Industry
Parent *Industry
Parent *Industry
Parent *Industry
ParentIndustries IndustrySlice
ParentIndustries IndustrySlice
ParentIndustries IndustrySlice
ParentIndustries IndustrySlice
}
...
It generates 4 copies of Parent, and 4 copies of ParentIndustries. There should only be a single instance of each. It also appears that the code for Adding/Replacing on the relationship does not compile.
Use https://bitbucket.org/pkg/inflect/ for inflections. Up to 27x faster on benchmarks.
For example, I have a user model
type User struct {
ID int `boil:"id" json:"id" toml:"id" yaml:"id"`
Name string `boil:"name" json:"name" toml:"name" yaml:"name"`
PasswordDigest string `boil:"password_digest" json:"password_digest" toml:"password_digest" yaml:"password_digest"`
Phone string `boil:"phone" json:"phone" toml:"phone" yaml:"phone"`
CreatedAt time.Time `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
UpdatedAt time.Time `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"`
R *userR `boil:"-" json:"-" toml:"-" yaml:"-"`
L userL `boil:"-" json:"-" toml:"-" yaml:"-"`
}
I want to change Name's tag to
Name string `boil:"name" json:"name" toml:"name" yaml:"name" valid:"Required;MinSize(5);MaxSize(26)"`
so I can run my valid function to check if the parameter in http request is valid
and I also want to change passwordDiagest's json tag to json:"-"
, so when I send user struct over http response, the password will not be included.
How to specific tags for different tables and different properties?
I really like the sqlboiler so far. It looks like a great project!
I searched the code base and didn't see any reference to it on master or dev branches.
I have one table which has both hstore
and text[]
columns. How much work would be involved in supporting these?
(I also have jsonb but I see you support that on the dev branch now).
Generated slices are missing InsertAll(); it would be nice to be able to do multiple row inserts:
INSERT INTO x(col1, col2) VALUES
("a1", "a2"),
("b1", "b2"),
...
Not sure how that will work for the maximum query size settings for each database. Should probably have a UpsertAll() as well?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.