From 72c68e22de662813f01140860de62dd18550169d Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Mon, 19 Dec 2016 19:29:11 -0800 Subject: [PATCH 001/179] Fix exists - Add tests for exists --- templates_test/finishers.tpl | 46 ++++++++++++++++++- templates_test/singleton/boil_suites_test.tpl | 10 ++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/templates_test/finishers.tpl b/templates_test/finishers.tpl index fa8b129..b3ba672 100644 --- a/templates_test/finishers.tpl +++ b/templates_test/finishers.tpl @@ -95,6 +95,15 @@ func test{{$tableNamePlural}}Count(t *testing.T) { tx := MustTx(boil.Begin()) defer tx.Rollback() + count, err := {{$tableNamePlural}}(tx).Count() + if err != nil { + t.Error(err) + } + + if count != 0 { + t.Error("want 0 records found") + } + if err = {{$varNameSingular}}One.Insert(tx); err != nil { t.Error(err) } @@ -102,7 +111,7 @@ func test{{$tableNamePlural}}Count(t *testing.T) { t.Error(err) } - count, err := {{$tableNamePlural}}(tx).Count() + count, err = {{$tableNamePlural}}(tx).Count() if err != nil { t.Error(err) } @@ -111,3 +120,38 @@ func test{{$tableNamePlural}}Count(t *testing.T) { t.Error("want 2 records, got:", count) } } + +func test{{$tableNamePlural}}ExistsFinisher(t *testing.T) { + t.Parallel() + + var err error + seed := randomize.NewSeed() + {{$varNameSingular}}One := &{{$tableNameSingular}}{} + if err = randomize.Struct(seed, {{$varNameSingular}}One, {{$varNameSingular}}DBTypes, false, {{$varNameSingular}}ColumnsWithDefault...); err != nil { + t.Errorf("Unable to randomize {{$tableNameSingular}} struct: %s", err) + } + + tx := MustTx(boil.Begin()) + defer tx.Rollback() + exists, err := {{$tableNamePlural}}(tx).Exists() + if err != nil { + t.Error(err) + } + + if exists { + t.Error("the record should not exist") + } + + if err = {{$varNameSingular}}One.Insert(tx); err != nil { + t.Error(err) + } + + exists, err = {{$tableNamePlural}}(tx).Exists() + if err != nil { + t.Error(err) + } + + if !exists { + t.Error("wanted record to exist") + } +} diff --git a/templates_test/singleton/boil_suites_test.tpl b/templates_test/singleton/boil_suites_test.tpl index 8723da4..bf105a3 100644 --- a/templates_test/singleton/boil_suites_test.tpl +++ b/templates_test/singleton/boil_suites_test.tpl @@ -105,6 +105,16 @@ func TestCount(t *testing.T) { {{- end -}} } +func TestExistsFinisher(t *testing.T) { + {{- range $index, $table := .Tables}} + {{- if $table.IsJoinTable -}} + {{- else -}} + {{- $tableName := $table.Name | plural | titleCase -}} + t.Run("{{$tableName}}", test{{$tableName}}ExistsFinisher) + {{end -}} + {{- end -}} +} + {{if not .NoHooks -}} func TestHooks(t *testing.T) { {{- range $index, $table := .Tables}} From 94a6604d195bb3c52d926cece8af27b33a3fcbd7 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Wed, 28 Dec 2016 14:47:57 +1000 Subject: [PATCH 002/179] Fix missing string array import --- imports.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/imports.go b/imports.go index 880ba77..f545a28 100644 --- a/imports.go +++ b/imports.go @@ -342,6 +342,9 @@ var importsBasedOnType = map[string]imports{ "types.BoolArray": { thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, }, + "types.StringArray": { + thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + }, "types.Hstore": { thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, }, From 5449ce7c6ca1fd2f49f2cf96ccb7e2d9ae698c90 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 27 Dec 2016 23:31:24 -0800 Subject: [PATCH 003/179] Bump to 2.1.3 --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index cf68812..b836181 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,7 @@ import ( "github.com/spf13/viper" ) -const sqlBoilerVersion = "2.1.1" +const sqlBoilerVersion = "2.1.3" var ( cmdState *State From dea748d40945eb0a9077476931d787b3c93ea1f7 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Mon, 2 Jan 2017 15:16:08 +1000 Subject: [PATCH 004/179] Add --tinyint-as-bool flag (v2.1.4 release) --- bdb/column.go | 6 ++++++ bdb/drivers/mysql.go | 35 ++++++++++++++++++++++++++-------- main.go | 7 ++++++- testdata/mysql_test_schema.sql | 4 +++- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/bdb/column.go b/bdb/column.go index b5c408e..5ed7a8b 100644 --- a/bdb/column.go +++ b/bdb/column.go @@ -23,6 +23,12 @@ type Column struct { // https://www.postgresql.org/docs/9.1/static/infoschema-element-types.html ArrType *string UDTName string + + // MySQL only bits + // Used to get full type, ex: + // tinyint(1) instead of tinyint + // Used for "tinyint-as-bool" flag + FullDBType string } // ColumnNames of the columns. diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index 7f1cd4b..4e74232 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -11,6 +11,12 @@ import ( "github.com/vattle/sqlboiler/bdb" ) +// TinyintAsBool is a global that is set from main.go if a user specifies +// this flag when generating. This flag only applies to MySQL so we're using +// a global instead, to avoid breaking the interface. If TinyintAsBool is true +// then tinyint(1) will be mapped in your generated structs to bool opposed to int8. +var TinyintAsBool bool + // MySQLDriver holds the database connection string and a handle // to the database connection. type MySQLDriver struct { @@ -123,6 +129,7 @@ func (m *MySQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { rows, err := m.dbConn.Query(` select c.column_name, + c.column_type, if(c.data_type = 'enum', c.column_type, c.data_type), if(extra = 'auto_increment','auto_increment', c.column_default), c.is_nullable = 'YES', @@ -144,19 +151,21 @@ func (m *MySQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { defer rows.Close() for rows.Next() { - var colName, colType string + var colName, colType, colFullType string var nullable, unique bool var defaultValue *string - if err := rows.Scan(&colName, &colType, &defaultValue, &nullable, &unique); err != nil { + if err := rows.Scan(&colName, &colFullType, &colType, &defaultValue, &nullable, &unique); err != nil { return nil, errors.Wrapf(err, "unable to scan for table %s", tableName) } column := bdb.Column{ - Name: colName, - DBType: colType, - Nullable: nullable, - Unique: unique, + Name: colName, + FullDBType: colFullType, // example: tinyint(1) instead of tinyint + DBType: colType, + Nullable: nullable, + Unique: unique, } + if defaultValue != nil && *defaultValue != "NULL" { column.Default = *defaultValue } @@ -260,7 +269,12 @@ func (m *MySQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { if c.Nullable { switch c.DBType { case "tinyint": - c.Type = "null.Int8" + // map tinyint(1) to bool if TinyintAsBool is true + if TinyintAsBool && c.FullDBType == "tinyint(1)" { + c.Type = "null.Bool" + } else { + c.Type = "null.Int8" + } case "smallint": c.Type = "null.Int16" case "mediumint": @@ -287,7 +301,12 @@ func (m *MySQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { } else { switch c.DBType { case "tinyint": - c.Type = "int8" + // map tinyint(1) to bool if TinyintAsBool is true + if TinyintAsBool && c.FullDBType == "tinyint(1)" { + c.Type = "bool" + } else { + c.Type = "int8" + } case "smallint": c.Type = "int16" case "mediumint": diff --git a/main.go b/main.go index b836181..3df0983 100644 --- a/main.go +++ b/main.go @@ -10,9 +10,10 @@ import ( "github.com/kat-co/vala" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/vattle/sqlboiler/bdb/drivers" ) -const sqlBoilerVersion = "2.1.3" +const sqlBoilerVersion = "2.1.4" var ( cmdState *State @@ -82,6 +83,7 @@ func main() { rootCmd.PersistentFlags().BoolP("no-hooks", "", false, "Disable hooks feature for your models") rootCmd.PersistentFlags().BoolP("no-auto-timestamps", "", false, "Disable automatic timestamps for created_at/updated_at") rootCmd.PersistentFlags().BoolP("version", "", false, "Print the version") + rootCmd.PersistentFlags().BoolP("tinyint-as-bool", "", false, "Map MySQL tinyint(1) in Go to bool instead of int8") viper.SetDefault("postgres.sslmode", "require") viper.SetDefault("postgres.port", "5432") @@ -206,6 +208,9 @@ func preRun(cmd *cobra.Command, args []string) error { SSLMode: viper.GetString("mysql.sslmode"), } + // Set MySQL TinyintAsBool global var. This flag only applies to MySQL. + drivers.TinyintAsBool = viper.GetBool("tinyint-as-bool") + // MySQL doesn't have schemas, just databases cmdConfig.Schema = cmdConfig.MySQL.DBName diff --git a/testdata/mysql_test_schema.sql b/testdata/mysql_test_schema.sql index 423aceb..a3b4af4 100644 --- a/testdata/mysql_test_schema.sql +++ b/testdata/mysql_test_schema.sql @@ -135,7 +135,9 @@ CREATE TABLE magicest ( aaaa char NULL, bbbb char NOT NULL, cccc text NULL, - dddd text NOT NULL + dddd text NOT NULL, + eeee tinyint(2) NULL, + ffff tinyint(2) NOT NULL ); create table owner ( From 920cb42f9baedb6e3295fdcdb654024ed63e2ebb Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Mon, 2 Jan 2017 15:20:32 +1000 Subject: [PATCH 005/179] Add examples for top level config --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 50a316a..428bf69 100644 --- a/README.md +++ b/README.md @@ -256,19 +256,20 @@ not to pass them through the command line or environment variables: Example: ```toml +blacklist=["migrations", "other"] +schema="myschema" [postgres] -dbname="dbname" -host="localhost" -port=5432 -user="dbusername" -pass="dbpassword" - + dbname="dbname" + host="localhost" + port=5432 + user="dbusername" + pass="dbpassword" [mysql] -dbname="dbname" -host="localhost" -port=3306 -user="dbusername" -pass="dbpassword" + dbname="dbname" + host="localhost" + port=3306 + user="dbusername" + pass="dbpassword" sslmode="false" ``` From dd73656d8e04491027d3d99a222cda1f130e9f41 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Mon, 2 Jan 2017 15:21:16 +1000 Subject: [PATCH 006/179] Fix readme example spacing --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 428bf69..3f01292 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ schema="myschema" port=3306 user="dbusername" pass="dbpassword" -sslmode="false" + sslmode="false" ``` #### Initial Generation From 23b6221f8bb02da704b065d7eb441d80b6692623 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 3 Jan 2017 19:07:00 -0800 Subject: [PATCH 007/179] Relax the definition of unique on columns (mysql) - Fix #77 --- bdb/drivers/mysql.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index 4e74232..a803f4a 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -139,7 +139,8 @@ func (m *MySQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { inner join information_schema.key_column_usage kcu on tc.constraint_name = kcu.constraint_name and tc.table_name = kcu.table_name and tc.table_schema = kcu.table_schema where c.column_name = kcu.column_name and tc.table_name = c.table_name and - (tc.constraint_type = 'PRIMARY KEY' or tc.constraint_type = 'UNIQUE') + (tc.constraint_type = 'PRIMARY KEY' or tc.constraint_type = 'UNIQUE') and + (select count(*) from information_schema.key_column_usage where constraint_name = tc.constraint_name) = 1 ) as is_unique from information_schema.columns as c where table_name = ? and table_schema = ?; From 5f7bee14a032f31e4e560923dd282a97661f51f0 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 3 Jan 2017 19:43:01 -0800 Subject: [PATCH 008/179] Relax the definition of unique on columns (psql) - Fix formatting on giant postgres query - Invert the section that looks for unique constraints to look for a constraint and join to the constraint_column_usage to be more in line with the mysql query and also it makes a little bit more sense. - Add more checks to ensure the schema is being enforced in the postgres side of things as several pieces in the unique checks were missing it which would lead to false positives. - Fix #77 for postgres --- bdb/drivers/postgres.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/bdb/drivers/postgres.go b/bdb/drivers/postgres.go index d14fb1b..ce94561 100644 --- a/bdb/drivers/postgres.go +++ b/bdb/drivers/postgres.go @@ -155,16 +155,17 @@ func (p *PostgresDriver) Columns(schema, tableName string) ([]bdb.Column, error) c.is_nullable = 'YES' as is_nullable, (select exists( select 1 - from information_schema.constraint_column_usage as ccu - inner join information_schema.table_constraints tc on ccu.constraint_name = tc.constraint_name - where ccu.table_name = c.table_name and ccu.column_name = c.column_name and tc.constraint_type = 'UNIQUE' - )) OR (select exists( + from information_schema.table_constraints tc + inner join information_schema.constraint_column_usage as ccu on tc.constraint_name = ccu.constraint_name + where tc.table_schema = $1 and tc.constraint_type = 'UNIQUE' and ccu.constraint_schema = $1 and ccu.table_name = c.table_name and ccu.column_name = c.column_name and + (select count(*) from information_schema.constraint_column_usage where constraint_schema = $1 and constraint_name = tc.constraint_name) = 1 + )) OR + (select exists( select 1 - from - pg_indexes pgix - inner join pg_class pgc on pgix.indexname = pgc.relname and pgc.relkind = 'i' - inner join pg_index pgi on pgi.indexrelid = pgc.oid - inner join pg_attribute pga on pga.attrelid = pgi.indrelid and pga.attnum = ANY(pgi.indkey) + from pg_indexes pgix + inner join pg_class pgc on pgix.indexname = pgc.relname and pgc.relkind = 'i' and pgc.relnatts = 1 + inner join pg_index pgi on pgi.indexrelid = pgc.oid + inner join pg_attribute pga on pga.attrelid = pgi.indrelid and pga.attnum = ANY(pgi.indkey) where pgix.schemaname = $1 and pgix.tablename = c.table_name and pga.attname = c.column_name and pgi.indisunique = true )) as is_unique @@ -173,7 +174,7 @@ func (p *PostgresDriver) Columns(schema, tableName string) ([]bdb.Column, error) left join information_schema.element_types e on ((c.table_catalog, c.table_schema, c.table_name, 'TABLE', c.dtd_identifier) = (e.object_catalog, e.object_schema, e.object_name, e.object_type, e.collection_type_identifier)) - where c.table_name=$2 and c.table_schema = $1; + where c.table_name = $2 and c.table_schema = $1; `, schema, tableName) if err != nil { From 14b9122a087fdc952d971c51611f3e558a5981d6 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 3 Jan 2017 20:29:43 -0800 Subject: [PATCH 009/179] Fix underspecification of constraint lookups. --- bdb/drivers/mysql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index a803f4a..1d80634 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -140,7 +140,7 @@ func (m *MySQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { on tc.constraint_name = kcu.constraint_name and tc.table_name = kcu.table_name and tc.table_schema = kcu.table_schema where c.column_name = kcu.column_name and tc.table_name = c.table_name and (tc.constraint_type = 'PRIMARY KEY' or tc.constraint_type = 'UNIQUE') and - (select count(*) from information_schema.key_column_usage where constraint_name = tc.constraint_name) = 1 + (select count(*) from information_schema.key_column_usage where table_schema = kcu.table_schema and table_name = tc.table_name and constraint_name = tc.constraint_name) = 1 ) as is_unique from information_schema.columns as c where table_name = ? and table_schema = ?; From 4ae933653838c0ed6e0accef855856dd0f5e6141 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Wed, 4 Jan 2017 20:31:29 -0800 Subject: [PATCH 010/179] Fix bug in ToManySetOpsAdd and impure join tables - This is a bug that manifests itself a bunch with our update code where you cannot actually use the update method to update a key since it uses the values on the struct to both update the values and find the object to update but in this operation the key must have two different values. --- templates/12_relationship_to_many_setops.tpl | 34 ++++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/templates/12_relationship_to_many_setops.tpl b/templates/12_relationship_to_many_setops.tpl index cfa0819..0f92342 100644 --- a/templates/12_relationship_to_many_setops.tpl +++ b/templates/12_relationship_to_many_setops.tpl @@ -6,6 +6,8 @@ {{- $txt := txtsFromToMany $dot.Tables $table . -}} {{- $varNameSingular := .Table | singular | camelCase -}} {{- $foreignVarNameSingular := .ForeignTable | singular | camelCase}} + {{- $foreignPKeyCols := (getTable $dot.Tables .ForeignTable).PKey.Columns -}} + {{- $foreignSchemaTable := .ForeignTable | $dot.SchemaTable}} // Add{{$txt.Function.Name}} adds the given related objects to the existing relationships // of the {{$table.Name | singular}}, optionally inserting them as new records. // Appends related to o.R.{{$txt.Function.Name}}. @@ -13,20 +15,38 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executor, insert bool, related ...*{{$txt.ForeignTable.NameGo}}) error { var err error for _, rel := range related { - {{if not .ToJoinTable -}} - rel.{{$txt.Function.ForeignAssignment}} = o.{{$txt.Function.LocalAssignment}} - {{if .ForeignColumnNullable -}} - rel.{{$txt.ForeignTable.ColumnNameGo}}.Valid = true - {{end -}} - {{end -}} if insert { + {{if not .ToJoinTable -}} + rel.{{$txt.Function.ForeignAssignment}} = o.{{$txt.Function.LocalAssignment}} + {{if .ForeignColumnNullable -}} + rel.{{$txt.ForeignTable.ColumnNameGo}}.Valid = true + {{end -}} + {{end -}} + if err = rel.Insert(exec); err != nil { return errors.Wrap(err, "failed to insert into foreign table") } }{{if not .ToJoinTable}} else { - if err = rel.Update(exec, "{{.ForeignColumn}}"); err != nil { + updateQuery := fmt.Sprintf( + "UPDATE {{$foreignSchemaTable}} SET %s WHERE %s", + strmangle.SetParamNames("{{$dot.LQ}}", "{{$dot.RQ}}", {{if $dot.Dialect.IndexPlaceholders}}1{{else}}0{{end}}, []string{{"{"}}"{{.ForeignColumn}}"{{"}"}}), + strmangle.WhereClause("{{$dot.LQ}}", "{{$dot.RQ}}", {{if $dot.Dialect.IndexPlaceholders}}2{{else}}0{{end}}, {{$foreignVarNameSingular}}PrimaryKeyColumns), + ) + values := []interface{}{o.{{$txt.LocalTable.ColumnNameGo}}, rel.{{$foreignPKeyCols | stringMap $dot.StringFuncs.titleCase | join ", rel."}}{{"}"}} + + if boil.DebugMode { + fmt.Fprintln(boil.DebugWriter, updateQuery) + fmt.Fprintln(boil.DebugWriter, values) + } + + if _, err = exec.Exec(updateQuery, values...); err != nil { return errors.Wrap(err, "failed to update foreign table") } + + rel.{{$txt.Function.ForeignAssignment}} = o.{{$txt.Function.LocalAssignment}} + {{if .ForeignColumnNullable -}} + rel.{{$txt.ForeignTable.ColumnNameGo}}.Valid = true + {{end -}} }{{end -}} } From 22f7a45847c8b77adab9ab31a69736eeaf5b7183 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Thu, 5 Jan 2017 21:31:43 -0800 Subject: [PATCH 011/179] Ignore empty errors on postgres upsert fail - Postgres's behavior when there is no update is that there is an ErrNoRows thrown back, which we can safely ignore since the only time this can happen in postgres's case is under this circumstance since there's no race unlike the mysql upsert code. - Fix #84 --- templates/17_upsert.tpl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index f9f0f55..cf3b635 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -181,11 +181,14 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if ne .DriverName {{- else}} if len(cache.retMapping) != 0 { err = exec.QueryRow(cache.query, vals...).Scan(returns...) + if err == sql.ErrNoRows { + err = nil // Postgres doesn't return anything when there's no update + } } else { _, err = exec.Exec(cache.query, vals...) } if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to upsert for {{.Table.Name}}") + return errors.Wrap(err, "{{.PkgName}}: unable to upsert {{.Table.Name}}") } {{- end}} From 158d9cbec0b37aec9b80c4205d02626f1021bd41 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 7 Jan 2017 00:48:39 +1000 Subject: [PATCH 012/179] remove commas from schema example --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f01292..a272fc2 100644 --- a/README.md +++ b/README.md @@ -342,7 +342,7 @@ Most examples in this section will be demonstrated using the following Postgres ```sql CREATE TABLE pilots ( id integer NOT NULL, - name text NOT NULL, + name text NOT NULL ); ALTER TABLE pilots ADD CONSTRAINT pilot_pkey PRIMARY KEY (id); @@ -352,7 +352,7 @@ CREATE TABLE jets ( pilot_id integer NOT NULL, age integer NOT NULL, name text NOT NULL, - color text NOT NULL, + color text NOT NULL ); ALTER TABLE jets ADD CONSTRAINT jet_pkey PRIMARY KEY (id); From be2131c7639dbb64df3e935c2887540c9d29d55a Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 7 Jan 2017 01:00:02 +1000 Subject: [PATCH 013/179] Bump to 2.1.5 * Includes fix #77 #82 (to one associations erroneously generated) * Includes readme update --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 3df0983..7cca6d6 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,7 @@ import ( "github.com/vattle/sqlboiler/bdb/drivers" ) -const sqlBoilerVersion = "2.1.4" +const sqlBoilerVersion = "2.1.5" var ( cmdState *State From fac1a7fe6941c3200e39bfbbdf71090c710aaa8e Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 7 Jan 2017 01:17:28 +1000 Subject: [PATCH 014/179] update schemas to incl broken example from readme --- testdata/mysql_test_schema.sql | 69 ++++++++++++++++--------------- testdata/postgres_test_schema.sql | 69 ++++++++++++++++--------------- 2 files changed, 71 insertions(+), 67 deletions(-) diff --git a/testdata/mysql_test_schema.sql b/testdata/mysql_test_schema.sql index a3b4af4..4755a63 100644 --- a/testdata/mysql_test_schema.sql +++ b/testdata/mysql_test_schema.sql @@ -240,40 +240,6 @@ create table worms ( foreign key (tiger_id) references tigers (id) ); -create table pilots ( - id int primary key not null auto_increment, - name varchar(255) -); - -create table airports ( - id int primary key not null auto_increment, - name varchar(255) -); - -create table languages ( - id int primary key not null auto_increment, - name varchar(255) -); - -create table jets ( - id int primary key not null auto_increment, - name varchar(255), - pilot_id integer, - airport_id integer, - - foreign key (pilot_id) references pilots (id), - foreign key (airport_id) references airports (id) -); - -create table pilot_languages ( - pilot_id integer not null, - language_id integer not null, - - primary key (pilot_id, language_id), - foreign key (pilot_id) references pilots (id), - foreign key (language_id) references languages (id) -); - create table byte_pilots ( id binary primary key not null, name varchar(255) @@ -351,3 +317,38 @@ CREATE TABLE race_result_scratchings ( foreign key (results_id) references race_results(id) ); +CREATE TABLE pilots ( + id integer NOT NULL, + name text NOT NULL +); + +ALTER TABLE pilots ADD CONSTRAINT pilot_pkey PRIMARY KEY (id); + +CREATE TABLE jets ( + id integer NOT NULL, + pilot_id integer NOT NULL, + age integer NOT NULL, + name text NOT NULL, + color text NOT NULL +); + +ALTER TABLE jets ADD CONSTRAINT jet_pkey PRIMARY KEY (id); +ALTER TABLE jets ADD CONSTRAINT pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); + +CREATE TABLE languages ( + id integer NOT NULL, + language text NOT NULL +); + +ALTER TABLE languages ADD CONSTRAINT language_pkey PRIMARY KEY (id); + +-- Join table +CREATE TABLE pilot_languages ( + pilot_id integer NOT NULL, + language_id integer NOT NULL +); + +-- Composite primary key +ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pkey PRIMARY KEY (pilot_id, language_id); +ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); +ALTER TABLE pilot_languages ADD CONSTRAINT languages_fkey FOREIGN KEY (language_id) REFERENCES languages(id); diff --git a/testdata/postgres_test_schema.sql b/testdata/postgres_test_schema.sql index 7a3893e..52f33e4 100644 --- a/testdata/postgres_test_schema.sql +++ b/testdata/postgres_test_schema.sql @@ -246,7 +246,6 @@ create table enemies ( primary key (enemies) ); - create table chocolate ( dog varchar(100) primary key ); @@ -307,38 +306,6 @@ create table worms ( foreign key (tiger_id) references tigers (id) ); -create table pilots ( - id serial primary key not null, - name character varying -); - -create table airports ( - id serial primary key not null, - name character varying -); - -create table languages ( - id serial primary key not null, - name character varying -); - -create table jets ( - id serial primary key not null, - name character varying, - pilot_id integer, - airport_id integer, - foreign key (pilot_id) references pilots (id), - foreign key (airport_id) references airports (id) -); - -create table pilot_languages ( - pilot_id integer not null, - language_id integer not null, - - primary key (pilot_id, language_id), - foreign key (pilot_id) references pilots (id), - foreign key (language_id) references languages (id) -); create table byte_pilots ( id bytea primary key not null, @@ -416,3 +383,39 @@ CREATE TABLE race_result_scratchings ( name text NOT NULL, foreign key (results_id) references race_results(id) ); + +CREATE TABLE pilots ( + id integer NOT NULL, + name text NOT NULL +); + +ALTER TABLE pilots ADD CONSTRAINT pilot_pkey PRIMARY KEY (id); + +CREATE TABLE jets ( + id integer NOT NULL, + pilot_id integer NOT NULL, + age integer NOT NULL, + name text NOT NULL, + color text NOT NULL +); + +ALTER TABLE jets ADD CONSTRAINT jet_pkey PRIMARY KEY (id); +ALTER TABLE jets ADD CONSTRAINT pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); + +CREATE TABLE languages ( + id integer NOT NULL, + language text NOT NULL +); + +ALTER TABLE languages ADD CONSTRAINT language_pkey PRIMARY KEY (id); + +-- Join table +CREATE TABLE pilot_languages ( + pilot_id integer NOT NULL, + language_id integer NOT NULL +); + +-- Composite primary key +ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pkey PRIMARY KEY (pilot_id, language_id); +ALTER TABLE pilot_languages ADD CONSTRAINT pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); +ALTER TABLE pilot_languages ADD CONSTRAINT languages_fkey FOREIGN KEY (language_id) REFERENCES languages(id); From 711ecbbe8d24dbb3b317ad3930b356344a894fbc Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Fri, 6 Jan 2017 17:45:32 -0800 Subject: [PATCH 015/179] Use pg-specific schema tables for fkey detection - Postgres doesn't care about names for uniqueness of keys unlike mysql because internally it keeps "oid" values to keep track of everything. Unfortunately this means that the information_schema standard is inadequate to differentiate between constraints that are named the same (which isn't possible in mysql, but is in pg). Hence we have to dip into the pg specific schemas for better or worse. - Fix naming of the sample schema in the README since it would fail for mysql due to duplicate naming. - Mark test schema up so we don't fix the bad names so we catch regressions here. - Fix #85 --- README.md | 6 +++--- bdb/drivers/postgres.go | 21 ++++++++++++--------- testdata/postgres_test_schema.sql | 2 ++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a272fc2..ac8159a 100644 --- a/README.md +++ b/README.md @@ -356,7 +356,7 @@ CREATE TABLE jets ( ); ALTER TABLE jets ADD CONSTRAINT jet_pkey PRIMARY KEY (id); -ALTER TABLE jets ADD CONSTRAINT pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); +ALTER TABLE jets ADD CONSTRAINT jet_pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); CREATE TABLE languages ( id integer NOT NULL, @@ -373,8 +373,8 @@ CREATE TABLE pilot_languages ( -- Composite primary key ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pkey PRIMARY KEY (pilot_id, language_id); -ALTER TABLE pilot_languages ADD CONSTRAINT pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); -ALTER TABLE pilot_languages ADD CONSTRAINT languages_fkey FOREIGN KEY (language_id) REFERENCES languages(id); +ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); +ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_languages_fkey FOREIGN KEY (language_id) REFERENCES languages(id); ``` The generated model structs for this schema look like the following. Note that we've included the relationship diff --git a/bdb/drivers/postgres.go b/bdb/drivers/postgres.go index ce94561..5ae0283 100644 --- a/bdb/drivers/postgres.go +++ b/bdb/drivers/postgres.go @@ -264,15 +264,18 @@ func (p *PostgresDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.Foreign query := ` select - tc.constraint_name, - kcu.table_name as source_table, - kcu.column_name as source_column, - ccu.table_name as dest_table, - ccu.column_name as dest_column - from information_schema.table_constraints as tc - inner join information_schema.key_column_usage as kcu ON tc.constraint_name = kcu.constraint_name and tc.constraint_schema = kcu.constraint_schema - inner join information_schema.constraint_column_usage as ccu ON tc.constraint_name = ccu.constraint_name and tc.constraint_schema = ccu.constraint_schema - where tc.table_name = $1 and tc.constraint_type = 'FOREIGN KEY' and tc.table_schema = $2;` + pgcon.conname, + pgc.relname as source_table, + pgasrc.attname as source_column, + dstlookupname.relname as dest_table, + pgadst.attname as dest_column + from pg_namespace pgn + inner join pg_class pgc on pgn.oid = pgc.relnamespace and pgc.relkind = 'r' + inner join pg_constraint pgcon on pgn.oid = pgcon.connamespace and pgc.oid = pgcon.conrelid + inner join pg_class dstlookupname on pgcon.confrelid = dstlookupname.oid + inner join pg_attribute pgasrc on pgc.oid = pgasrc.attrelid and pgasrc.attnum = ANY(pgcon.conkey) + inner join pg_attribute pgadst on pgcon.confrelid = pgadst.attrelid and pgadst.attnum = ANY(pgcon.confkey) + where pgn.nspname = $2 and pgc.relname = $1 and pgcon.contype = 'f'` var rows *sql.Rows var err error diff --git a/testdata/postgres_test_schema.sql b/testdata/postgres_test_schema.sql index 52f33e4..e0a043c 100644 --- a/testdata/postgres_test_schema.sql +++ b/testdata/postgres_test_schema.sql @@ -400,6 +400,7 @@ CREATE TABLE jets ( ); ALTER TABLE jets ADD CONSTRAINT jet_pkey PRIMARY KEY (id); +-- The following fkey remains poorly named to avoid regressions related to psql naming ALTER TABLE jets ADD CONSTRAINT pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); CREATE TABLE languages ( @@ -417,5 +418,6 @@ CREATE TABLE pilot_languages ( -- Composite primary key ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pkey PRIMARY KEY (pilot_id, language_id); +-- The following fkey remains poorly named to avoid regressions related to psql naming ALTER TABLE pilot_languages ADD CONSTRAINT pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); ALTER TABLE pilot_languages ADD CONSTRAINT languages_fkey FOREIGN KEY (language_id) REFERENCES languages(id); From 4997ba96bf27b05757e170dee7de1b0865b25d5d Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Mon, 9 Jan 2017 20:28:07 -0800 Subject: [PATCH 016/179] Add more clear documentation about boil.Begin() - Fix #88 --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index ac8159a..1479d6c 100644 --- a/README.md +++ b/README.md @@ -558,6 +558,9 @@ err := pilot.Delete(db) // Regular variant, takes a db handle (boil.Executor int pilot.DeleteP(db) // Panic variant, takes a db handle and panics on error. err := pilot.DeleteG() // Global variant, uses the globally set db handle (boil.SetDB()). pilot.DeleteGP() // Global&Panic variant, combines the global db handle and panic on error. + +db.Begin() // Normal sql package way of creating a transaction +boil.Begin() // Uses the global database handle set by boil.SetDB() ``` Note that it's slightly different for query building. @@ -857,6 +860,10 @@ tx.Commit() tx.Rollback() ``` +It's also worth noting that there's a way to take advantage of `boil.SetDB()` +by using the [boil.Begin()](https://godoc.org/github.com/vattle/sqlboiler/boil#Begin) function. +This opens a transaction using the globally stored database. + ### Debug Logging Debug logging will print your generated SQL statement and the arguments it is using. From 61ab7e254a192046c1351b84fb0c00eaf756a029 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Mon, 9 Jan 2017 21:06:47 -0800 Subject: [PATCH 017/179] Add missing function variants for setops. - Fix #87 --- templates/10_relationship_to_one_setops.tpl | 56 +++++++++++ .../11_relationship_one_to_one_setops.tpl | 56 +++++++++++ templates/12_relationship_to_many_setops.tpl | 96 +++++++++++++++++++ 3 files changed, 208 insertions(+) diff --git a/templates/10_relationship_to_one_setops.tpl b/templates/10_relationship_to_one_setops.tpl index 728ab10..14f726e 100644 --- a/templates/10_relationship_to_one_setops.tpl +++ b/templates/10_relationship_to_one_setops.tpl @@ -6,6 +6,34 @@ {{- $foreignNameSingular := .ForeignTable | singular | camelCase -}} {{- $varNameSingular := .Table | singular | camelCase}} {{- $schemaTable := .Table | $dot.SchemaTable}} +// Set{{$txt.Function.Name}}G of the {{.Table | singular}} to the related item. +// Sets o.R.{{$txt.Function.Name}} to related. +// Adds o to related.R.{{$txt.Function.ForeignName}}. +// Uses the global database handle. +func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}G(insert bool, related *{{$txt.ForeignTable.NameGo}}) error { + return o.Set{{$txt.Function.Name}}(boil.GetDB(), insert, related) +} + +// Set{{$txt.Function.Name}}P of the {{.Table | singular}} to the related item. +// Sets o.R.{{$txt.Function.Name}} to related. +// Adds o to related.R.{{$txt.Function.ForeignName}}. +// Panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}P(exec boil.Executor, insert bool, related *{{$txt.ForeignTable.NameGo}}) { + if err := o.Set{{$txt.Function.Name}}(exec, insert, related); err != nil { + panic(boil.WrapErr(err)) + } +} + +// Set{{$txt.Function.Name}}GP of the {{.Table | singular}} to the related item. +// Sets o.R.{{$txt.Function.Name}} to related. +// Adds o to related.R.{{$txt.Function.ForeignName}}. +// Uses the global database handle and panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}GP(insert bool, related *{{$txt.ForeignTable.NameGo}}) { + if err := o.Set{{$txt.Function.Name}}(boil.GetDB(), insert, related); err != nil { + panic(boil.WrapErr(err)) + } +} + // Set{{$txt.Function.Name}} of the {{.Table | singular}} to the related item. // Sets o.R.{{$txt.Function.Name}} to related. // Adds o to related.R.{{$txt.Function.ForeignName}}. @@ -68,6 +96,34 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo } {{- if .Nullable}} +// Remove{{$txt.Function.Name}}G relationship. +// Sets o.R.{{$txt.Function.Name}} to nil. +// Removes o from all passed in related items' relationships struct (Optional). +// Uses the global database handle. +func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}G(related *{{$txt.ForeignTable.NameGo}}) error { + return o.Remove{{$txt.Function.Name}}(boil.GetDB(), related) +} + +// Remove{{$txt.Function.Name}}P relationship. +// Sets o.R.{{$txt.Function.Name}} to nil. +// Removes o from all passed in related items' relationships struct (Optional). +// Panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}P(exec boil.Executor, related *{{$txt.ForeignTable.NameGo}}) { + if err := o.Remove{{$txt.Function.Name}}(exec, related); err != nil { + panic(boil.WrapErr(err)) + } +} + +// Remove{{$txt.Function.Name}}GP relationship. +// Sets o.R.{{$txt.Function.Name}} to nil. +// Removes o from all passed in related items' relationships struct (Optional). +// Uses the global database handle and panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}GP(related *{{$txt.ForeignTable.NameGo}}) { + if err := o.Remove{{$txt.Function.Name}}(boil.GetDB(), related); err != nil { + panic(boil.WrapErr(err)) + } +} + // Remove{{$txt.Function.Name}} relationship. // Sets o.R.{{$txt.Function.Name}} to nil. // Removes o from all passed in related items' relationships struct (Optional). diff --git a/templates/11_relationship_one_to_one_setops.tpl b/templates/11_relationship_one_to_one_setops.tpl index 7466d7a..826eec2 100644 --- a/templates/11_relationship_one_to_one_setops.tpl +++ b/templates/11_relationship_one_to_one_setops.tpl @@ -7,6 +7,34 @@ {{- $foreignVarNameSingular := .ForeignTable | singular | camelCase -}} {{- $foreignPKeyCols := (getTable $dot.Tables .ForeignTable).PKey.Columns -}} {{- $foreignSchemaTable := .ForeignTable | $dot.SchemaTable}} +// Set{{$txt.Function.Name}}G of the {{.Table | singular}} to the related item. +// Sets o.R.{{$txt.Function.Name}} to related. +// Adds o to related.R.{{$txt.Function.ForeignName}}. +// Uses the global database handle. +func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}G(insert bool, related *{{$txt.ForeignTable.NameGo}}) error { + return o.Set{{$txt.Function.Name}}(boil.GetDB(), insert, related) +} + +// Set{{$txt.Function.Name}}P of the {{.Table | singular}} to the related item. +// Sets o.R.{{$txt.Function.Name}} to related. +// Adds o to related.R.{{$txt.Function.ForeignName}}. +// Panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}P(exec boil.Executor, insert bool, related *{{$txt.ForeignTable.NameGo}}) { + if err := o.Set{{$txt.Function.Name}}(exec, insert, related); err != nil { + panic(boil.WrapErr(err)) + } +} + +// Set{{$txt.Function.Name}}GP of the {{.Table | singular}} to the related item. +// Sets o.R.{{$txt.Function.Name}} to related. +// Adds o to related.R.{{$txt.Function.ForeignName}}. +// Uses the global database handle and panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}GP(insert bool, related *{{$txt.ForeignTable.NameGo}}) { + if err := o.Set{{$txt.Function.Name}}(boil.GetDB(), insert, related); err != nil { + panic(boil.WrapErr(err)) + } +} + // Set{{$txt.Function.Name}} of the {{.Table | singular}} to the related item. // Sets o.R.{{$txt.Function.Name}} to related. // Adds o to related.R.{{$txt.Function.ForeignName}}. @@ -65,6 +93,34 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo } {{- if .ForeignColumnNullable}} +// Remove{{$txt.Function.Name}}G relationship. +// Sets o.R.{{$txt.Function.Name}} to nil. +// Removes o from all passed in related items' relationships struct (Optional). +// Uses the global database handle. +func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}G(related *{{$txt.ForeignTable.NameGo}}) error { + return o.Remove{{$txt.Function.Name}}(boil.GetDB(), related) +} + +// Remove{{$txt.Function.Name}}P relationship. +// Sets o.R.{{$txt.Function.Name}} to nil. +// Removes o from all passed in related items' relationships struct (Optional). +// Panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}P(exec boil.Executor, related *{{$txt.ForeignTable.NameGo}}) { + if err := o.Remove{{$txt.Function.Name}}(exec, related); err != nil { + panic(boil.WrapErr(err)) + } +} + +// Remove{{$txt.Function.Name}}GP relationship. +// Sets o.R.{{$txt.Function.Name}} to nil. +// Removes o from all passed in related items' relationships struct (Optional). +// Uses the global database handle and panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}GP(related *{{$txt.ForeignTable.NameGo}}) { + if err := o.Remove{{$txt.Function.Name}}(boil.GetDB(), related); err != nil { + panic(boil.WrapErr(err)) + } +} + // Remove{{$txt.Function.Name}} relationship. // Sets o.R.{{$txt.Function.Name}} to nil. // Removes o from all passed in related items' relationships struct (Optional). diff --git a/templates/12_relationship_to_many_setops.tpl b/templates/12_relationship_to_many_setops.tpl index 0f92342..04c1b97 100644 --- a/templates/12_relationship_to_many_setops.tpl +++ b/templates/12_relationship_to_many_setops.tpl @@ -8,6 +8,37 @@ {{- $foreignVarNameSingular := .ForeignTable | singular | camelCase}} {{- $foreignPKeyCols := (getTable $dot.Tables .ForeignTable).PKey.Columns -}} {{- $foreignSchemaTable := .ForeignTable | $dot.SchemaTable}} +// Add{{$txt.Function.Name}}G adds the given related objects to the existing relationships +// of the {{$table.Name | singular}}, optionally inserting them as new records. +// Appends related to o.R.{{$txt.Function.Name}}. +// Sets related.R.{{$txt.Function.ForeignName}} appropriately. +// Uses the global database handle. +func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}G(insert bool, related ...*{{$txt.ForeignTable.NameGo}}) error { + return o.Add{{$txt.Function.Name}}(boil.GetDB(), insert, related...) +} + +// Add{{$txt.Function.Name}}P adds the given related objects to the existing relationships +// of the {{$table.Name | singular}}, optionally inserting them as new records. +// Appends related to o.R.{{$txt.Function.Name}}. +// Sets related.R.{{$txt.Function.ForeignName}} appropriately. +// Panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}P(exec boil.Executor, insert bool, related ...*{{$txt.ForeignTable.NameGo}}) { + if err := o.Add{{$txt.Function.Name}}(exec, insert, related...); err != nil { + panic(boil.WrapErr(err)) + } +} + +// Add{{$txt.Function.Name}}GP adds the given related objects to the existing relationships +// of the {{$table.Name | singular}}, optionally inserting them as new records. +// Appends related to o.R.{{$txt.Function.Name}}. +// Sets related.R.{{$txt.Function.ForeignName}} appropriately. +// Uses the global database handle and panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}GP(insert bool, related ...*{{$txt.ForeignTable.NameGo}}) { + if err := o.Add{{$txt.Function.Name}}(boil.GetDB(), insert, related...); err != nil { + panic(boil.WrapErr(err)) + } +} + // Add{{$txt.Function.Name}} adds the given related objects to the existing relationships // of the {{$table.Name | singular}}, optionally inserting them as new records. // Appends related to o.R.{{$txt.Function.Name}}. @@ -101,6 +132,43 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo } {{- if (or .ForeignColumnNullable .ToJoinTable)}} +// Set{{$txt.Function.Name}}G removes all previously related items of the +// {{$table.Name | singular}} replacing them completely with the passed +// in related items, optionally inserting them as new records. +// Sets o.R.{{$txt.Function.ForeignName}}'s {{$txt.Function.Name}} accordingly. +// Replaces o.R.{{$txt.Function.Name}} with related. +// Sets related.R.{{$txt.Function.ForeignName}}'s {{$txt.Function.Name}} accordingly. +// Uses the global database handle. +func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}G(insert bool, related ...*{{$txt.ForeignTable.NameGo}}) error { + return o.Set{{$txt.Function.Name}}(boil.GetDB(), insert, related...) +} + +// Set{{$txt.Function.Name}}P removes all previously related items of the +// {{$table.Name | singular}} replacing them completely with the passed +// in related items, optionally inserting them as new records. +// Sets o.R.{{$txt.Function.ForeignName}}'s {{$txt.Function.Name}} accordingly. +// Replaces o.R.{{$txt.Function.Name}} with related. +// Sets related.R.{{$txt.Function.ForeignName}}'s {{$txt.Function.Name}} accordingly. +// Panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}P(exec boil.Executor, insert bool, related ...*{{$txt.ForeignTable.NameGo}}) { + if err := o.Set{{$txt.Function.Name}}(exec, insert, related...); err != nil { + panic(boil.WrapErr(err)) + } +} + +// Set{{$txt.Function.Name}}GP removes all previously related items of the +// {{$table.Name | singular}} replacing them completely with the passed +// in related items, optionally inserting them as new records. +// Sets o.R.{{$txt.Function.ForeignName}}'s {{$txt.Function.Name}} accordingly. +// Replaces o.R.{{$txt.Function.Name}} with related. +// Sets related.R.{{$txt.Function.ForeignName}}'s {{$txt.Function.Name}} accordingly. +// Uses the global database handle and panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}GP(insert bool, related ...*{{$txt.ForeignTable.NameGo}}) { + if err := o.Set{{$txt.Function.Name}}(boil.GetDB(), insert, related...); err != nil { + panic(boil.WrapErr(err)) + } +} + // Set{{$txt.Function.Name}} removes all previously related items of the // {{$table.Name | singular}} replacing them completely with the passed // in related items, optionally inserting them as new records. @@ -146,6 +214,34 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo return o.Add{{$txt.Function.Name}}(exec, insert, related...) } +// Remove{{$txt.Function.Name}}G relationships from objects passed in. +// Removes related items from R.{{$txt.Function.Name}} (uses pointer comparison, removal does not keep order) +// Sets related.R.{{$txt.Function.ForeignName}}. +// Uses the global database handle. +func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}G(related ...*{{$txt.ForeignTable.NameGo}}) error { + return o.Remove{{$txt.Function.Name}}(boil.GetDB(), related...) +} + +// Remove{{$txt.Function.Name}}P relationships from objects passed in. +// Removes related items from R.{{$txt.Function.Name}} (uses pointer comparison, removal does not keep order) +// Sets related.R.{{$txt.Function.ForeignName}}. +// Panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}P(exec boil.Executor, related ...*{{$txt.ForeignTable.NameGo}}) { + if err := o.Remove{{$txt.Function.Name}}(exec, related...); err != nil { + panic(boil.WrapErr(err)) + } +} + +// Remove{{$txt.Function.Name}}GP relationships from objects passed in. +// Removes related items from R.{{$txt.Function.Name}} (uses pointer comparison, removal does not keep order) +// Sets related.R.{{$txt.Function.ForeignName}}. +// Uses the global database handle and panics on error. +func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}GP(related ...*{{$txt.ForeignTable.NameGo}}) { + if err := o.Remove{{$txt.Function.Name}}(boil.GetDB(), related...); err != nil { + panic(boil.WrapErr(err)) + } +} + // Remove{{$txt.Function.Name}} relationships from objects passed in. // Removes related items from R.{{$txt.Function.Name}} (uses pointer comparison, removal does not keep order) // Sets related.R.{{$txt.Function.ForeignName}}. From aab85fd2cde5468e3347c3c9e6430e6230bec2be Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 10 Jan 2017 18:09:20 -0800 Subject: [PATCH 018/179] Add issue template. --- .github/ISSUE_TEMPLATE.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..062a49c --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,18 @@ +If you're having a generation problem please answer these questions before submitting your issue. Thanks! + +### What version of SQLBoiler are you using (`sqlboiler --version`)? + + +### If this happened at generation time what was the full SQLBoiler command you used to generate your models? (if not applicable leave blank) + + +### If this happened at runtime what code produced the issue? (if not applicable leave blank) + + +### What is the output of the command above with the `-d` flag added to it? (Provided you are comfortable sharing this, it contains a blueprint of your schema) + + +### Please provide a relevant database schema so we can replicate your issue (Provided you are comfortable sharing this) + + +### Further information. What did you do, what did you expect? From ab01c9d234dddc4dcdcb77d30d05bf23ba3b0af4 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 10 Jan 2017 18:16:02 -0800 Subject: [PATCH 019/179] Don't eat so many spaces in to_many templates - Fix #89 --- templates_test/relationship_to_many.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates_test/relationship_to_many.tpl b/templates_test/relationship_to_many.tpl index d23984e..0080688 100644 --- a/templates_test/relationship_to_many.tpl +++ b/templates_test/relationship_to_many.tpl @@ -28,7 +28,7 @@ func test{{$txt.LocalTable.NameGo}}ToMany{{$txt.Function.Name}}(t *testing.T) { {{if .Nullable -}} a.{{.Column | titleCase}}.Valid = true {{- end}} - {{- if .ForeignColumnNullable -}} + {{- if .ForeignColumnNullable}} b.{{.ForeignColumn | titleCase}}.Valid = true c.{{.ForeignColumn | titleCase}}.Valid = true {{- end}} From 88cde8df0cbf4b7855d55a7a308f17cfe5f34517 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 14 Jan 2017 13:13:55 +1000 Subject: [PATCH 020/179] Made new core package to hold core of app * This was primarily done so it can be used as a library in ABCWeb and other apps. --- config.go => core/config.go | 2 +- sqlboiler.go => core/core.go | 2 +- sqlboiler_test.go => core/core_test.go | 2 +- imports.go => core/imports.go | 2 +- imports_test.go => core/imports_test.go | 2 +- output.go => core/output.go | 2 +- output_test.go => core/output_test.go | 2 +- templates.go => core/templates.go | 2 +- templates_test.go => core/templates_test.go | 2 +- text_helpers.go => core/text_helpers.go | 2 +- text_helpers_test.go => core/text_helpers_test.go | 2 +- main.go | 13 +++++++------ 12 files changed, 18 insertions(+), 17 deletions(-) rename config.go => core/config.go (98%) rename sqlboiler.go => core/core.go (99%) rename sqlboiler_test.go => core/core_test.go (99%) rename imports.go => core/imports.go (99%) rename imports_test.go => core/imports_test.go (99%) rename output.go => core/output.go (99%) rename output_test.go => core/output_test.go (99%) rename templates.go => core/templates.go (99%) rename templates_test.go => core/templates_test.go (98%) rename text_helpers.go => core/text_helpers.go (99%) rename text_helpers_test.go => core/text_helpers_test.go (99%) diff --git a/config.go b/core/config.go similarity index 98% rename from config.go rename to core/config.go index da28134..a23f6f2 100644 --- a/config.go +++ b/core/config.go @@ -1,4 +1,4 @@ -package main +package core // Config for the running of the commands type Config struct { diff --git a/sqlboiler.go b/core/core.go similarity index 99% rename from sqlboiler.go rename to core/core.go index 0c52dce..a650559 100644 --- a/sqlboiler.go +++ b/core/core.go @@ -1,6 +1,6 @@ // Package sqlboiler has types and methods useful for generating code that // acts as a fully dynamic ORM might. -package main +package core import ( "encoding/json" diff --git a/sqlboiler_test.go b/core/core_test.go similarity index 99% rename from sqlboiler_test.go rename to core/core_test.go index 367e429..fc56d66 100644 --- a/sqlboiler_test.go +++ b/core/core_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "bufio" diff --git a/imports.go b/core/imports.go similarity index 99% rename from imports.go rename to core/imports.go index f545a28..97be767 100644 --- a/imports.go +++ b/core/imports.go @@ -1,4 +1,4 @@ -package main +package core import ( "bytes" diff --git a/imports_test.go b/core/imports_test.go similarity index 99% rename from imports_test.go rename to core/imports_test.go index 628d955..4a919da 100644 --- a/imports_test.go +++ b/core/imports_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "reflect" diff --git a/output.go b/core/output.go similarity index 99% rename from output.go rename to core/output.go index d6914c3..1ed13c9 100644 --- a/output.go +++ b/core/output.go @@ -1,4 +1,4 @@ -package main +package core import ( "bufio" diff --git a/output_test.go b/core/output_test.go similarity index 99% rename from output_test.go rename to core/output_test.go index 3a33eca..05e94c1 100644 --- a/output_test.go +++ b/core/output_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "bytes" diff --git a/templates.go b/core/templates.go similarity index 99% rename from templates.go rename to core/templates.go index 9baae19..2da75b7 100644 --- a/templates.go +++ b/core/templates.go @@ -1,4 +1,4 @@ -package main +package core import ( "fmt" diff --git a/templates_test.go b/core/templates_test.go similarity index 98% rename from templates_test.go rename to core/templates_test.go index df45e1e..6dea42b 100644 --- a/templates_test.go +++ b/core/templates_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "sort" diff --git a/text_helpers.go b/core/text_helpers.go similarity index 99% rename from text_helpers.go rename to core/text_helpers.go index 56451e6..2df5887 100644 --- a/text_helpers.go +++ b/core/text_helpers.go @@ -1,4 +1,4 @@ -package main +package core import ( "fmt" diff --git a/text_helpers_test.go b/core/text_helpers_test.go similarity index 99% rename from text_helpers_test.go rename to core/text_helpers_test.go index b203dd9..b879fb9 100644 --- a/text_helpers_test.go +++ b/core/text_helpers_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "reflect" diff --git a/main.go b/main.go index 7cca6d6..738df64 100644 --- a/main.go +++ b/main.go @@ -11,13 +11,14 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/vattle/sqlboiler/bdb/drivers" + "github.com/vattle/sqlboiler/core" ) const sqlBoilerVersion = "2.1.5" var ( - cmdState *State - cmdConfig *Config + cmdState *core.State + cmdConfig *core.Config ) func main() { @@ -122,7 +123,7 @@ func preRun(cmd *cobra.Command, args []string) error { driverName := args[0] - cmdConfig = &Config{ + cmdConfig = &core.Config{ DriverName: driverName, OutFolder: viper.GetString("output"), Schema: viper.GetString("schema"), @@ -163,7 +164,7 @@ func preRun(cmd *cobra.Command, args []string) error { } if driverName == "postgres" { - cmdConfig.Postgres = PostgresConfig{ + cmdConfig.Postgres = core.PostgresConfig{ User: viper.GetString("postgres.user"), Pass: viper.GetString("postgres.pass"), Host: viper.GetString("postgres.host"), @@ -199,7 +200,7 @@ func preRun(cmd *cobra.Command, args []string) error { } if driverName == "mysql" { - cmdConfig.MySQL = MySQLConfig{ + cmdConfig.MySQL = core.MySQLConfig{ User: viper.GetString("mysql.user"), Pass: viper.GetString("mysql.pass"), Host: viper.GetString("mysql.host"), @@ -240,7 +241,7 @@ func preRun(cmd *cobra.Command, args []string) error { } } - cmdState, err = New(cmdConfig) + cmdState, err = core.New(cmdConfig) return err } From 788d28400ec710c137b756612633fb2fee1db701 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 14 Jan 2017 13:38:40 +1000 Subject: [PATCH 021/179] rename core to boilingcore to make a more sense * make more sense as a third party library --- core/core.go => boilingcore/boilingcore.go | 2 +- .../boilingcore_test.go | 2 +- {core => boilingcore}/config.go | 2 +- {core => boilingcore}/imports.go | 2 +- {core => boilingcore}/imports_test.go | 2 +- {core => boilingcore}/output.go | 2 +- {core => boilingcore}/output_test.go | 2 +- {core => boilingcore}/templates.go | 2 +- {core => boilingcore}/templates_test.go | 2 +- {core => boilingcore}/text_helpers.go | 2 +- {core => boilingcore}/text_helpers_test.go | 2 +- main.go | 14 +++++++------- 12 files changed, 18 insertions(+), 18 deletions(-) rename core/core.go => boilingcore/boilingcore.go (99%) rename core/core_test.go => boilingcore/boilingcore_test.go (99%) rename {core => boilingcore}/config.go (97%) rename {core => boilingcore}/imports.go (99%) rename {core => boilingcore}/imports_test.go (99%) rename {core => boilingcore}/output.go (99%) rename {core => boilingcore}/output_test.go (98%) rename {core => boilingcore}/templates.go (99%) rename {core => boilingcore}/templates_test.go (98%) rename {core => boilingcore}/text_helpers.go (99%) rename {core => boilingcore}/text_helpers_test.go (99%) diff --git a/core/core.go b/boilingcore/boilingcore.go similarity index 99% rename from core/core.go rename to boilingcore/boilingcore.go index a650559..f3e9ddb 100644 --- a/core/core.go +++ b/boilingcore/boilingcore.go @@ -1,6 +1,6 @@ // Package sqlboiler has types and methods useful for generating code that // acts as a fully dynamic ORM might. -package core +package boilingcore import ( "encoding/json" diff --git a/core/core_test.go b/boilingcore/boilingcore_test.go similarity index 99% rename from core/core_test.go rename to boilingcore/boilingcore_test.go index fc56d66..f9e7182 100644 --- a/core/core_test.go +++ b/boilingcore/boilingcore_test.go @@ -1,4 +1,4 @@ -package core +package boilingcore import ( "bufio" diff --git a/core/config.go b/boilingcore/config.go similarity index 97% rename from core/config.go rename to boilingcore/config.go index a23f6f2..2fa7346 100644 --- a/core/config.go +++ b/boilingcore/config.go @@ -1,4 +1,4 @@ -package core +package boilingcore // Config for the running of the commands type Config struct { diff --git a/core/imports.go b/boilingcore/imports.go similarity index 99% rename from core/imports.go rename to boilingcore/imports.go index 97be767..236d16d 100644 --- a/core/imports.go +++ b/boilingcore/imports.go @@ -1,4 +1,4 @@ -package core +package boilingcore import ( "bytes" diff --git a/core/imports_test.go b/boilingcore/imports_test.go similarity index 99% rename from core/imports_test.go rename to boilingcore/imports_test.go index 4a919da..43c46e5 100644 --- a/core/imports_test.go +++ b/boilingcore/imports_test.go @@ -1,4 +1,4 @@ -package core +package boilingcore import ( "reflect" diff --git a/core/output.go b/boilingcore/output.go similarity index 99% rename from core/output.go rename to boilingcore/output.go index 1ed13c9..baee236 100644 --- a/core/output.go +++ b/boilingcore/output.go @@ -1,4 +1,4 @@ -package core +package boilingcore import ( "bufio" diff --git a/core/output_test.go b/boilingcore/output_test.go similarity index 98% rename from core/output_test.go rename to boilingcore/output_test.go index 05e94c1..fa954e3 100644 --- a/core/output_test.go +++ b/boilingcore/output_test.go @@ -1,4 +1,4 @@ -package core +package boilingcore import ( "bytes" diff --git a/core/templates.go b/boilingcore/templates.go similarity index 99% rename from core/templates.go rename to boilingcore/templates.go index 2da75b7..9e8695c 100644 --- a/core/templates.go +++ b/boilingcore/templates.go @@ -1,4 +1,4 @@ -package core +package boilingcore import ( "fmt" diff --git a/core/templates_test.go b/boilingcore/templates_test.go similarity index 98% rename from core/templates_test.go rename to boilingcore/templates_test.go index 6dea42b..25e4777 100644 --- a/core/templates_test.go +++ b/boilingcore/templates_test.go @@ -1,4 +1,4 @@ -package core +package boilingcore import ( "sort" diff --git a/core/text_helpers.go b/boilingcore/text_helpers.go similarity index 99% rename from core/text_helpers.go rename to boilingcore/text_helpers.go index 2df5887..e9577a7 100644 --- a/core/text_helpers.go +++ b/boilingcore/text_helpers.go @@ -1,4 +1,4 @@ -package core +package boilingcore import ( "fmt" diff --git a/core/text_helpers_test.go b/boilingcore/text_helpers_test.go similarity index 99% rename from core/text_helpers_test.go rename to boilingcore/text_helpers_test.go index b879fb9..8beed50 100644 --- a/core/text_helpers_test.go +++ b/boilingcore/text_helpers_test.go @@ -1,4 +1,4 @@ -package core +package boilingcore import ( "reflect" diff --git a/main.go b/main.go index 738df64..76ae0f8 100644 --- a/main.go +++ b/main.go @@ -11,14 +11,14 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/vattle/sqlboiler/bdb/drivers" - "github.com/vattle/sqlboiler/core" + "github.com/vattle/sqlboiler/boilingcore" ) const sqlBoilerVersion = "2.1.5" var ( - cmdState *core.State - cmdConfig *core.Config + cmdState *boilingcore.State + cmdConfig *boilingcore.Config ) func main() { @@ -123,7 +123,7 @@ func preRun(cmd *cobra.Command, args []string) error { driverName := args[0] - cmdConfig = &core.Config{ + cmdConfig = &boilingcore.Config{ DriverName: driverName, OutFolder: viper.GetString("output"), Schema: viper.GetString("schema"), @@ -164,7 +164,7 @@ func preRun(cmd *cobra.Command, args []string) error { } if driverName == "postgres" { - cmdConfig.Postgres = core.PostgresConfig{ + cmdConfig.Postgres = boilingcore.PostgresConfig{ User: viper.GetString("postgres.user"), Pass: viper.GetString("postgres.pass"), Host: viper.GetString("postgres.host"), @@ -200,7 +200,7 @@ func preRun(cmd *cobra.Command, args []string) error { } if driverName == "mysql" { - cmdConfig.MySQL = core.MySQLConfig{ + cmdConfig.MySQL = boilingcore.MySQLConfig{ User: viper.GetString("mysql.user"), Pass: viper.GetString("mysql.pass"), Host: viper.GetString("mysql.host"), @@ -241,7 +241,7 @@ func preRun(cmd *cobra.Command, args []string) error { } } - cmdState, err = core.New(cmdConfig) + cmdState, err = boilingcore.New(cmdConfig) return err } From 4506da6b4184e454c3d094eaf79472acfb499869 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Fri, 13 Jan 2017 20:01:00 -0800 Subject: [PATCH 022/179] Change detection hack for broken arrays in viper - Fix #91 --- main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 76ae0f8..992860d 100644 --- a/main.go +++ b/main.go @@ -140,7 +140,7 @@ func preRun(cmd *cobra.Command, args []string) error { // detect a malformed value coming out of viper. // Once the bug is fixed we'll be able to move this into the init above cmdConfig.BlacklistTables = viper.GetStringSlice("blacklist") - if len(cmdConfig.BlacklistTables) == 1 && strings.HasPrefix(cmdConfig.BlacklistTables[0], "[") { + if len(cmdConfig.BlacklistTables) == 1 && strings.ContainsRune(cmdConfig.BlacklistTables[0], ',') { cmdConfig.BlacklistTables, err = cmd.PersistentFlags().GetStringSlice("blacklist") if err != nil { return err @@ -148,7 +148,7 @@ func preRun(cmd *cobra.Command, args []string) error { } cmdConfig.WhitelistTables = viper.GetStringSlice("whitelist") - if len(cmdConfig.WhitelistTables) == 1 && strings.HasPrefix(cmdConfig.WhitelistTables[0], "[") { + if len(cmdConfig.WhitelistTables) == 1 && strings.ContainsRune(cmdConfig.WhitelistTables[0], ',') { cmdConfig.WhitelistTables, err = cmd.PersistentFlags().GetStringSlice("whitelist") if err != nil { return err @@ -156,7 +156,7 @@ func preRun(cmd *cobra.Command, args []string) error { } cmdConfig.Tags = viper.GetStringSlice("tag") - if len(cmdConfig.Tags) == 1 && strings.HasPrefix(cmdConfig.Tags[0], "[") { + if len(cmdConfig.Tags) == 1 && strings.ContainsRune(cmdConfig.Tags[0], ',') { cmdConfig.Tags, err = cmd.PersistentFlags().GetStringSlice("tag") if err != nil { return err From faf511a7e67a614b7226db145ac88a0b62041358 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 14 Jan 2017 16:13:32 +1000 Subject: [PATCH 023/179] fix boilingcore package comment --- boilingcore/boilingcore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index f3e9ddb..c5d5519 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -1,4 +1,4 @@ -// Package sqlboiler has types and methods useful for generating code that +// Package boilingcore has types and methods useful for generating code that // acts as a fully dynamic ORM might. package boilingcore From cb54ded2077b1f9fb6ff31df8f3a31485f712cfe Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sun, 15 Jan 2017 11:43:58 +1000 Subject: [PATCH 024/179] add sqlboiler logo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1479d6c..34e6f2e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SQLBoiler + [](https://github.com/vattle/sqlboiler/blob/master/LICENSE) [](https://godoc.org/github.com/vattle/sqlboiler) From bfba60eaad30f691f1e3b83882b370223fa5416b Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 15 Jan 2017 21:10:01 -0800 Subject: [PATCH 025/179] Bump version --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 992860d..3ce0514 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( "github.com/vattle/sqlboiler/boilingcore" ) -const sqlBoilerVersion = "2.1.5" +const sqlBoilerVersion = "2.1.7" var ( cmdState *boilingcore.State From 761efee9f0ec673fb6c329300ed2114427d4957d Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 15 Jan 2017 21:21:04 -0800 Subject: [PATCH 026/179] Add ability to replace a template - This feature remains undocumented because it's not a good idea in most cases but it enables us to replace a template. This is especially useful when using sqlboiler as a library since testmain problematically loads config in it's own magical way, divorced from even the way sqlboiler in "normal" mode loads it. This enables replacement of that mechanism by replacing it's template. --- boilingcore/boilingcore.go | 38 +++++++++++++++++++++++++++++++++++++- boilingcore/config.go | 1 + boilingcore/templates.go | 24 +++++++++++++++++------- main.go | 9 +++++++++ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index c5d5519..3bd9fdc 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -198,7 +198,43 @@ func (s *State) initTemplates() error { return err } - s.TestMainTemplate, err = loadTemplate(filepath.Join(basePath, templatesTestMainDirectory), s.Config.DriverName+"_main.tpl") + testMain := s.Config.DriverName + "_main.tpl" + s.TestMainTemplate, err = loadTemplate(nil, testMain, filepath.Join(basePath, templatesTestMainDirectory, testMain)) + if err != nil { + return err + } + } + + return s.processReplacements() +} + +// processReplacements loads any replacement templates +func (s *State) processReplacements() error { + for _, replace := range s.Config.Replacements { + fmt.Println(replace) + splits := strings.Split(replace, ":") + if len(splits) != 2 { + return errors.Errorf("replace parameters must have 2 arguments, given: %s", replace) + } + + toReplace, replaceWith := splits[0], splits[1] + + var err error + switch filepath.Dir(toReplace) { + case templatesDirectory: + _, err = loadTemplate(s.Templates.Template, toReplace, replaceWith) + case templatesSingletonDirectory: + _, err = loadTemplate(s.SingletonTemplates.Template, toReplace, replaceWith) + case templatesTestDirectory: + _, err = loadTemplate(s.TestTemplates.Template, toReplace, replaceWith) + case templatesSingletonTestDirectory: + _, err = loadTemplate(s.SingletonTestTemplates.Template, toReplace, replaceWith) + case templatesTestMainDirectory: + s.TestMainTemplate, err = loadTemplate(nil, toReplace, replaceWith) + default: + return errors.Errorf("replace file's directory not part of any known folder: %s", toReplace) + } + if err != nil { return err } diff --git a/boilingcore/config.go b/boilingcore/config.go index 2fa7346..0b2f15c 100644 --- a/boilingcore/config.go +++ b/boilingcore/config.go @@ -10,6 +10,7 @@ type Config struct { WhitelistTables []string BlacklistTables []string Tags []string + Replacements []string Debug bool NoTests bool NoHooks bool diff --git a/boilingcore/templates.go b/boilingcore/templates.go index 9e8695c..32bbe6f 100644 --- a/boilingcore/templates.go +++ b/boilingcore/templates.go @@ -2,11 +2,13 @@ package boilingcore import ( "fmt" + "io/ioutil" "path/filepath" "sort" "strings" "text/template" + "github.com/pkg/errors" "github.com/vattle/sqlboiler/bdb" "github.com/vattle/sqlboiler/queries" "github.com/vattle/sqlboiler/strmangle" @@ -109,16 +111,24 @@ func loadTemplates(dir string) (*templateList, error) { return &templateList{Template: tpl}, err } -// loadTemplate loads a single template file. -func loadTemplate(dir string, filename string) (*template.Template, error) { - pattern := filepath.Join(dir, filename) - tpl, err := template.New("").Funcs(templateFunctions).ParseFiles(pattern) - +// loadTemplate loads a single template, uses tpl as a base template if provided +// and creates a new base template if not. +func loadTemplate(tpl *template.Template, name, filename string) (*template.Template, error) { + b, err := ioutil.ReadFile(filename) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "failed reading template file: %s", filename) } - return tpl.Lookup(filename), err + if tpl == nil { + tpl = template.New(name) + } else { + tpl = tpl.New(name) + } + if tpl, err = tpl.Funcs(templateFunctions).Parse(string(b)); err != nil { + return nil, errors.Wrapf(err, "failed to parse template file: %s", filename) + } + + return tpl, nil } // set is to stop duplication from named enums, allowing a template loop diff --git a/main.go b/main.go index 3ce0514..9666477 100644 --- a/main.go +++ b/main.go @@ -79,6 +79,7 @@ func main() { rootCmd.PersistentFlags().StringSliceP("blacklist", "b", nil, "Do not include these tables in your generated package") rootCmd.PersistentFlags().StringSliceP("whitelist", "w", nil, "Only include these tables in your generated package") rootCmd.PersistentFlags().StringSliceP("tag", "t", nil, "Struct tags to be included on your models in addition to json, yaml, toml") + rootCmd.PersistentFlags().StringSliceP("replace", "", nil, "Replace templates by directory: relpath/to_file.tpl:relpath/to_replacement.tpl") rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug mode prints stack traces on error") rootCmd.PersistentFlags().BoolP("no-tests", "", false, "Disable generated go test files") rootCmd.PersistentFlags().BoolP("no-hooks", "", false, "Disable hooks feature for your models") @@ -163,6 +164,14 @@ func preRun(cmd *cobra.Command, args []string) error { } } + cmdConfig.Replacements = viper.GetStringSlice("replace") + if len(cmdConfig.Replacements) == 1 && strings.ContainsRune(cmdConfig.Replacements[0], ',') { + cmdConfig.Replacements, err = cmd.PersistentFlags().GetStringSlice("replace") + if err != nil { + return err + } + } + if driverName == "postgres" { cmdConfig.Postgres = boilingcore.PostgresConfig{ User: viper.GetString("postgres.user"), From e992e327c2727801cbe050a0df33439ccef083e0 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Tue, 24 Jan 2017 13:31:43 +1000 Subject: [PATCH 027/179] Fix inflection bug for "ss" words (like address) * Adjust inflect rules * Bump version to 2.1.8 --- main.go | 2 +- strmangle/inflect.go | 14 ++++++++++++++ testdata/postgres_test_schema.sql | 11 +++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 9666477..8202ea7 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( "github.com/vattle/sqlboiler/boilingcore" ) -const sqlBoilerVersion = "2.1.7" +const sqlBoilerVersion = "2.1.8" var ( cmdState *boilingcore.State diff --git a/strmangle/inflect.go b/strmangle/inflect.go index 78945b6..6238c39 100644 --- a/strmangle/inflect.go +++ b/strmangle/inflect.go @@ -92,6 +92,10 @@ func newBoilRuleset() *inflect.Ruleset { rs.AddPluralExact("oxen", "oxen", true) rs.AddPluralExact("quiz", "quizzes", true) rs.AddSingular("s", "") + rs.AddSingular("ss", "ss") + rs.AddSingular("as", "as") + rs.AddSingular("us", "us") + rs.AddSingular("is", "is") rs.AddSingular("news", "news") rs.AddSingular("ta", "tum") rs.AddSingular("ia", "ium") @@ -184,5 +188,15 @@ func newBoilRuleset() *inflect.Ruleset { rs.AddIrregular("move", "moves") rs.AddIrregular("zombie", "zombies") rs.AddIrregular("cookie", "cookies") + rs.AddSingularExact("a", "a", true) + rs.AddSingularExact("i", "i", true) + rs.AddSingularExact("is", "is", true) + rs.AddSingularExact("us", "us", true) + rs.AddSingularExact("as", "as", true) + rs.AddPluralExact("a", "a", true) + rs.AddPluralExact("i", "i", true) + rs.AddPluralExact("is", "is", true) + rs.AddPluralExact("us", "us", true) + rs.AddPluralExact("as", "as", true) return rs } diff --git a/testdata/postgres_test_schema.sql b/testdata/postgres_test_schema.sql index e0a043c..3e29c74 100644 --- a/testdata/postgres_test_schema.sql +++ b/testdata/postgres_test_schema.sql @@ -306,6 +306,17 @@ create table worms ( foreign key (tiger_id) references tigers (id) ); +create table addresses ( + id bytea primary key, + name bytea null +); + +create table houses ( + id bytea primary key, + name bytea not null, + address_id bytea not null unique, + foreign key (address_id) references addresses (id) +); create table byte_pilots ( id bytea primary key not null, From c33bb479a13660c848f60d8e51eed4ef74b3c186 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 24 Jan 2017 18:57:45 -0800 Subject: [PATCH 028/179] Fix postgres placeholders for to-many.RemoveX - Help fix #97 --- templates/12_relationship_to_many_setops.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/12_relationship_to_many_setops.tpl b/templates/12_relationship_to_many_setops.tpl index 04c1b97..fee21d3 100644 --- a/templates/12_relationship_to_many_setops.tpl +++ b/templates/12_relationship_to_many_setops.tpl @@ -250,7 +250,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Exec {{if .ToJoinTable -}} query := fmt.Sprintf( "delete from {{.JoinTable | $dot.SchemaTable}} where {{.JoinLocalColumn | $dot.Quotes}} = {{if $dot.Dialect.IndexPlaceholders}}$1{{else}}?{{end}} and {{.JoinForeignColumn | $dot.Quotes}} in (%s)", - strmangle.Placeholders(dialect.IndexPlaceholders, len(related), 1, 1), + strmangle.Placeholders(dialect.IndexPlaceholders, len(related), 2, 1), ) values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}} From bdb0ef1b7ca3b977b23d66d6e760e8057c0a0835 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 24 Jan 2017 22:43:46 -0800 Subject: [PATCH 029/179] Fix missing tests - ToMany(Set|Remove)Op tests were not being generated for join tables. --- templates_test/singleton/boil_suites_test.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates_test/singleton/boil_suites_test.tpl b/templates_test/singleton/boil_suites_test.tpl index 8723da4..08a5e21 100644 --- a/templates_test/singleton/boil_suites_test.tpl +++ b/templates_test/singleton/boil_suites_test.tpl @@ -251,7 +251,7 @@ func TestToManySet(t *testing.T) { {{- if $table.IsJoinTable -}} {{- else -}} {{- range $table.ToManyRelationships -}} - {{- if not .ForeignColumnNullable -}} + {{- if not (or .ForeignColumnNullable .ToJoinTable)}} {{- else -}} {{- $txt := txtsFromToMany $dot.Tables $table . -}} t.Run("{{$txt.LocalTable.NameGo}}To{{$txt.Function.Name}}", test{{$txt.LocalTable.NameGo}}ToManySetOp{{$txt.Function.Name}}) @@ -268,7 +268,7 @@ func TestToManyRemove(t *testing.T) { {{- if $table.IsJoinTable -}} {{- else -}} {{- range $table.ToManyRelationships -}} - {{- if not .ForeignColumnNullable -}} + {{- if not (or .ForeignColumnNullable .ToJoinTable)}} {{- else -}} {{- $txt := txtsFromToMany $dot.Tables $table . -}} t.Run("{{$txt.LocalTable.NameGo}}To{{$txt.Function.Name}}", test{{$txt.LocalTable.NameGo}}ToManyRemoveOp{{$txt.Function.Name}}) From 936df65ebc37b11be324906688bf4fe5481b0922 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 24 Jan 2017 22:45:20 -0800 Subject: [PATCH 030/179] Fix ToManyRemoveOp - This would generate a bad query when run, also the test was failing for this same reason and once enabled it was trivial to fix. --- templates/12_relationship_to_many_setops.tpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/12_relationship_to_many_setops.tpl b/templates/12_relationship_to_many_setops.tpl index fee21d3..bc5e220 100644 --- a/templates/12_relationship_to_many_setops.tpl +++ b/templates/12_relationship_to_many_setops.tpl @@ -253,6 +253,9 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Exec strmangle.Placeholders(dialect.IndexPlaceholders, len(related), 2, 1), ) values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}} + for _, rel := range related { + values = append(values, rel.{{$txt.ForeignTable.ColumnNameGo}}) + } if boil.DebugMode { fmt.Fprintln(boil.DebugWriter, query) From d79f816cc19af0331f759bc364b61ec34b436c1a Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 24 Jan 2017 23:03:59 -0800 Subject: [PATCH 031/179] Fix test failure - Optimistic code removed. It wasn't possible to test for this condition ever. --- templates_test/relationship_to_many_setops.tpl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/templates_test/relationship_to_many_setops.tpl b/templates_test/relationship_to_many_setops.tpl index 01471f4..f66ba8a 100644 --- a/templates_test/relationship_to_many_setops.tpl +++ b/templates_test/relationship_to_many_setops.tpl @@ -159,12 +159,16 @@ func test{{$txt.LocalTable.NameGo}}ToManySetOp{{$txt.Function.Name}}(t *testing. {{- if .ToJoinTable}} - if len(b.R.{{$txt.Function.ForeignName}}) != 0 { - t.Error("relationship was not removed properly from the slice") - } - if len(c.R.{{$txt.Function.ForeignName}}) != 0 { - t.Error("relationship was not removed properly from the slice") - } + // The following checks cannot be implemented since we have no handle + // to these when we call Set(). Leaving them here as wishful thinking + // and to let people know there's dragons. + // + // if len(b.R.{{$txt.Function.ForeignName}}) != 0 { + // t.Error("relationship was not removed properly from the slice") + // } + // if len(c.R.{{$txt.Function.ForeignName}}) != 0 { + // t.Error("relationship was not removed properly from the slice") + // } if d.R.{{$txt.Function.ForeignName}}[0] != &a { t.Error("relationship was not added properly to the slice") } From c02a2f0c2074e8f68b5096291e6d82ff9547bd90 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 24 Jan 2017 23:04:59 -0800 Subject: [PATCH 032/179] Fix null dereference --- templates/12_relationship_to_many_setops.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/12_relationship_to_many_setops.tpl b/templates/12_relationship_to_many_setops.tpl index bc5e220..06159ee 100644 --- a/templates/12_relationship_to_many_setops.tpl +++ b/templates/12_relationship_to_many_setops.tpl @@ -195,7 +195,9 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo {{if .ToJoinTable -}} remove{{$txt.Function.Name}}From{{$txt.Function.ForeignName}}Slice(o, related) - o.R.{{$txt.Function.Name}} = nil + if o.R != nil { + o.R.{{$txt.Function.Name}} = nil + } {{else -}} if o.R != nil { for _, rel := range o.R.{{$txt.Function.Name}} { From ec28cb4148c63f305f4dee350a4b63604ceb1427 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Thu, 26 Jan 2017 20:41:30 -0800 Subject: [PATCH 033/179] Add no-edit disclaimer to all generated files - As discussed in #96 --- boilingcore/output.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/boilingcore/output.go b/boilingcore/output.go index baee236..ffbde94 100644 --- a/boilingcore/output.go +++ b/boilingcore/output.go @@ -14,6 +14,12 @@ import ( "github.com/pkg/errors" ) +var noEditDisclaimer = []byte(`// This file is generated by SQLBoiler (https://github.com/vattle/sqlboiler) +// and is meant to be re-generated in place and/or deleted at any time. +// DO NOT EDIT + +`) + var ( // templateByteBuffer is re-used by all template construction to avoid // allocating more memory than is needed. This will later be a problem for @@ -103,6 +109,7 @@ func executeTemplates(e executeTemplateData) error { imps = combineTypeImports(imps, importsBasedOnType, e.data.Table.Columns) } + writeFileDisclaimer(out) writePackageName(out, e.state.Config.PkgName) writeImports(out, imps) @@ -138,6 +145,7 @@ func executeSingletonTemplates(e executeTemplateData) error { thirdParty: e.importNamedSet[fName].thirdParty, } + writeFileDisclaimer(out) writePackageName(out, e.state.Config.PkgName) writeImports(out, imps) @@ -165,6 +173,7 @@ func generateTestMainOutput(state *State, data *templateData) error { imps.standard = defaultTestMainImports[state.Config.DriverName].standard imps.thirdParty = defaultTestMainImports[state.Config.DriverName].thirdParty + writeFileDisclaimer(out) writePackageName(out, state.Config.PkgName) writeImports(out, imps) @@ -179,6 +188,12 @@ func generateTestMainOutput(state *State, data *templateData) error { return nil } +// writeFileDisclaimer writes the disclaimer at the top with a trailing +// newline so the package name doesn't get attached to it. +func writeFileDisclaimer(out *bytes.Buffer) { + _, _ = out.Write(noEditDisclaimer) +} + // writePackageName writes the package name correctly, ignores errors // since it's to the concrete buffer type which produces none func writePackageName(out *bytes.Buffer, pkgName string) { From 671e1d7ae9064c15a318dade0d5cf27322a37376 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Thu, 26 Jan 2017 20:42:14 -0800 Subject: [PATCH 034/179] Add --wipe flag to rm the output dir pre-generate - As discussed in #96 --- boilingcore/boilingcore.go | 6 ++++++ boilingcore/config.go | 1 + main.go | 2 ++ 3 files changed, 9 insertions(+) diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index 3bd9fdc..1030ddb 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -333,6 +333,12 @@ func (s *State) initTags(tags []string) error { // initOutFolder creates the folder that will hold the generated output. func (s *State) initOutFolder() error { + if s.Config.Wipe { + if err := os.RemoveAll(s.Config.OutFolder); err != nil { + return err + } + } + return os.MkdirAll(s.Config.OutFolder, os.ModePerm) } diff --git a/boilingcore/config.go b/boilingcore/config.go index 0b2f15c..62da693 100644 --- a/boilingcore/config.go +++ b/boilingcore/config.go @@ -15,6 +15,7 @@ type Config struct { NoTests bool NoHooks bool NoAutoTimestamps bool + Wipe bool Postgres PostgresConfig MySQL MySQLConfig diff --git a/main.go b/main.go index 8202ea7..098c9bd 100644 --- a/main.go +++ b/main.go @@ -86,6 +86,7 @@ func main() { rootCmd.PersistentFlags().BoolP("no-auto-timestamps", "", false, "Disable automatic timestamps for created_at/updated_at") rootCmd.PersistentFlags().BoolP("version", "", false, "Print the version") rootCmd.PersistentFlags().BoolP("tinyint-as-bool", "", false, "Map MySQL tinyint(1) in Go to bool instead of int8") + rootCmd.PersistentFlags().BoolP("wipe", "", false, "Delete the output folder (rm -rf) before generation to ensure sanity") viper.SetDefault("postgres.sslmode", "require") viper.SetDefault("postgres.port", "5432") @@ -134,6 +135,7 @@ func preRun(cmd *cobra.Command, args []string) error { NoTests: viper.GetBool("no-tests"), NoHooks: viper.GetBool("no-hooks"), NoAutoTimestamps: viper.GetBool("no-auto-timestamps"), + Wipe: viper.GetBool("wipe"), } // BUG: https://github.com/spf13/viper/issues/200 From 0221da2491f4c4647a32c38ea53c5ebc2de93639 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Thu, 26 Jan 2017 21:16:22 -0800 Subject: [PATCH 035/179] Document: regeneration, extensions, --wipe flag - As discussed in #96 --- README.md | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/README.md b/README.md index 34e6f2e..b2d8ae1 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ Table of Contents * [Download](#download) * [Configuration](#configuration) * [Initial Generation](#initial-generation) + * [Regeneration](#regeneration) + * [Extending Generated Models](#extending-generated-models) * [Diagnosing Problems](#diagnosing-problems) * [Features & Examples](#features--examples) * [Automatic CreatedAt/UpdatedAt](#automatic-createdatupdatedat) @@ -317,6 +319,116 @@ sqlboiler -b goose_migrations postgres go test ./models ``` +You can use `go generate` for SQLBoiler if you want to to make it easy to +run the command. + +It's important to not modify anything in the output folder, which brings us to +the next topic: regeneration. + +#### Regeneration + +When regenerating the models it's recommended that you completely delete the +generated directory in a build script or use the `--wipe` flag in SQLBoiler. +The reasons for this are that sqlboiler doesn't try to diff your files in any +smart way, it simply writes the files it's going to write whether they're there +or not and doesn't delete any files that were added by you or previous runs of +SQLBoiler. In the best case this can cause compilation errors, in the worst case +this may leave extraneous and unusable code that was generated against tables +that are no longer in the database. + +The bottom line is that this tool should always produce the same result from +the same source. And the intention is to always regenerate from a pure state. +The only reason the `--wipe` flag isn't defaulted to on is because we don't +like programs that `rm -rf` things on the filesystem without being asked to. + +#### Extending generated models + +There will probably come a time when you want to extend the generated models +with some kinds of helper functions. A general guideline is to put your +extension functions into a separate package so that your functions aren't +accidentally deleted when regenerating. Past that there are 3 main ways to +extend the models, the first way is the most desirable: + +**Method 1: Simple Functions** + +```go +// Package modext is for SQLBoiler helper methods +package modext + +// UserFirstTimeSetup is an extension of the user model. +func UserFirstTimeSetup(db *sql.DB, u *models.User) error { ... } +``` + +Code organization is accomplished by using multiple files, and everything +is passed as a parameter so these kinds of methods are very easy to test. + +Calling code is also very straightforward: + +```go +user, err := Users(db).One() +// elided error check + +err = modext.UserFirstTimeSetup(db, user) +// elided error check +``` + +**Method 2: Empty struct methods** + +The above is the best way to code extensions for SQLBoiler, however there may +be times when the number of methods grows too large and code completion is +not as helpful anymore. In these cases you may consider structuring the code +like this: + +```go +// Package modext is for SQLBoiler helper methods +package modext + +type users struct {} + +var Users = users{} + +// FirstTimeSetup is an extension of the user model. +func (u users) FirstTimeSetup(db *sql.DB, u *models.User) error { ... } +``` + +Calling code then looks a little bit different: + +```go +user, err := Users(db).One() +// elided error check + +err = modext.Users.FirstTimeSetup(db, user) +// elided error check +``` + +This is almost identical to the method above, but gives slight amounts more +organization at virtually no cost at runtime. It is however not as desirable +as the first method since it does have some runtime cost and doesn't offer that +much benefit over it. + +**Method 3: Embedding** + +This pattern is not for the feint of heart, what it provides in benefits it +more than makes up for in downsides. It's possible to embed the SQLBoiler +structs inside your own to enhance them. However it's subject to easy breakages +and a dependency on these additional objects. It can also introduce +inconsistencies as some objects may have no extended functionality and therefore +have no reason to be embedded so you either have to have a struct for each +generated struct even if it's empty, or have inconsistencies, some places where +you use the enhanced model, and some where you do not. + +```go +user, err := Users(db).One() +// elided error check + +enhUser := modext.User{user} +err = ehnUser.FirstTimeSetup(db) +// elided error check +``` + +I don't recommend this pattern, but included it so that people know it's an +option and also know the problems with it. + ## Diagnosing Problems The most common causes of problems and panics are: From 59c238539ddc3e0ee964e24f0f30910875c88334 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Thu, 26 Jan 2017 22:06:47 -0800 Subject: [PATCH 036/179] Attach all eagerly loaded models in to-one - The problem here is that due to the nature of the relationship and the way the loop was set up it was possible to miss some relationships: A _ C \_ D B _ E \_ F Since we looped over A and B and did a break when we found something to attach it to (in this example A would find C) it would break. What we should be looping through is CDEF and finding a home for each one. Did the same change in to_one though it doesn't matter since it's one-to-one. to-many is untouched because it's already looping over CDEF and finding a home for it because the relationship is reversed. - Fix #98 --- templates/07_relationship_to_one_eager.tpl | 10 +++++++--- templates/08_relationship_one_to_one_eager.tpl | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/templates/07_relationship_to_one_eager.tpl b/templates/07_relationship_to_one_eager.tpl index 43392f0..4af84ca 100644 --- a/templates/07_relationship_to_one_eager.tpl +++ b/templates/07_relationship_to_one_eager.tpl @@ -65,13 +65,17 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula } {{- end}} - if singular && len(resultSlice) != 0 { + if len(resultSlice) == 0 { + return nil + } + + if singular { object.R.{{$txt.Function.Name}} = resultSlice[0] return nil } - for _, foreign := range resultSlice { - for _, local := range slice { + for _, local := range slice { + for _, foreign := range resultSlice { {{if $txt.Function.UsesBytes -}} if 0 == bytes.Compare(local.{{$txt.Function.LocalAssignment}}, foreign.{{$txt.Function.ForeignAssignment}}) { {{else -}} diff --git a/templates/08_relationship_one_to_one_eager.tpl b/templates/08_relationship_one_to_one_eager.tpl index 6603d55..7363b69 100644 --- a/templates/08_relationship_one_to_one_eager.tpl +++ b/templates/08_relationship_one_to_one_eager.tpl @@ -65,13 +65,17 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula } {{- end}} - if singular && len(resultSlice) != 0 { + if len(resultSlice) == 0 { + return nil + } + + if singular { object.R.{{$txt.Function.Name}} = resultSlice[0] return nil } - for _, foreign := range resultSlice { - for _, local := range slice { + for _, local := range slice { + for _, foreign := range resultSlice { {{if $txt.Function.UsesBytes -}} if 0 == bytes.Compare(local.{{$txt.Function.LocalAssignment}}, foreign.{{$txt.Function.ForeignAssignment}}) { {{else -}} From 10ee452032f9f21df31e18f4727feb6edba94449 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 4 Feb 2017 15:51:07 +1000 Subject: [PATCH 037/179] Hide replace flag from help output * Replace flag is a "not recommended for use" flag, and is mainly present to be used with software that is using sqlboiler as a library, such as abcweb --- main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 098c9bd..e8d2969 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( "github.com/vattle/sqlboiler/boilingcore" ) -const sqlBoilerVersion = "2.1.8" +const sqlBoilerVersion = "2.1.9" var ( cmdState *boilingcore.State @@ -88,6 +88,9 @@ func main() { rootCmd.PersistentFlags().BoolP("tinyint-as-bool", "", false, "Map MySQL tinyint(1) in Go to bool instead of int8") rootCmd.PersistentFlags().BoolP("wipe", "", false, "Delete the output folder (rm -rf) before generation to ensure sanity") + // hide flags not recommended for use + rootCmd.PersistentFlags().MarkHidden("replace") + viper.SetDefault("postgres.sslmode", "require") viper.SetDefault("postgres.port", "5432") viper.SetDefault("mysql.sslmode", "true") From acaf94ef5fb8772070ef890487922f04e7753c25 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 4 Feb 2017 23:22:39 +1000 Subject: [PATCH 038/179] Remove println that should not be present --- boilingcore/boilingcore.go | 1 - 1 file changed, 1 deletion(-) diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index 1030ddb..cf8eb95 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -211,7 +211,6 @@ func (s *State) initTemplates() error { // processReplacements loads any replacement templates func (s *State) processReplacements() error { for _, replace := range s.Config.Replacements { - fmt.Println(replace) splits := strings.Split(replace, ":") if len(splits) != 2 { return errors.Errorf("replace parameters must have 2 arguments, given: %s", replace) From 60814eed1fb5dbefeb5e1fb2579ee75fa35c9f0e Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sun, 5 Feb 2017 02:24:20 +1000 Subject: [PATCH 039/179] Fix replace feature --- boilingcore/boilingcore.go | 16 +++++++++------- boilingcore/templates.go | 35 +++++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index cf8eb95..a86e403 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -198,8 +198,7 @@ func (s *State) initTemplates() error { return err } - testMain := s.Config.DriverName + "_main.tpl" - s.TestMainTemplate, err = loadTemplate(nil, testMain, filepath.Join(basePath, templatesTestMainDirectory, testMain)) + s.TestMainTemplate, err = loadTemplate(filepath.Join(basePath, templatesTestMainDirectory), s.Config.DriverName+"_main.tpl") if err != nil { return err } @@ -218,18 +217,21 @@ func (s *State) processReplacements() error { toReplace, replaceWith := splits[0], splits[1] + toReplaceSplit := strings.Split(toReplace, string(filepath.Separator)) + fileName := toReplaceSplit[len(toReplaceSplit)-1] + var err error switch filepath.Dir(toReplace) { case templatesDirectory: - _, err = loadTemplate(s.Templates.Template, toReplace, replaceWith) + err = replaceTemplate(s.Templates.Template, fileName, replaceWith) case templatesSingletonDirectory: - _, err = loadTemplate(s.SingletonTemplates.Template, toReplace, replaceWith) + err = replaceTemplate(s.SingletonTemplates.Template, fileName, replaceWith) case templatesTestDirectory: - _, err = loadTemplate(s.TestTemplates.Template, toReplace, replaceWith) + err = replaceTemplate(s.TestTemplates.Template, fileName, replaceWith) case templatesSingletonTestDirectory: - _, err = loadTemplate(s.SingletonTestTemplates.Template, toReplace, replaceWith) + err = replaceTemplate(s.SingletonTestTemplates.Template, fileName, replaceWith) case templatesTestMainDirectory: - s.TestMainTemplate, err = loadTemplate(nil, toReplace, replaceWith) + err = replaceTemplate(s.TestMainTemplate, fileName, replaceWith) default: return errors.Errorf("replace file's directory not part of any known folder: %s", toReplace) } diff --git a/boilingcore/templates.go b/boilingcore/templates.go index 32bbe6f..13f4142 100644 --- a/boilingcore/templates.go +++ b/boilingcore/templates.go @@ -111,24 +111,35 @@ func loadTemplates(dir string) (*templateList, error) { return &templateList{Template: tpl}, err } -// loadTemplate loads a single template, uses tpl as a base template if provided -// and creates a new base template if not. -func loadTemplate(tpl *template.Template, name, filename string) (*template.Template, error) { +// loadTemplate loads a single template file +func loadTemplate(dir string, filename string) (*template.Template, error) { + pattern := filepath.Join(dir, filename) + tpl, err := template.New("").Funcs(templateFunctions).ParseFiles(pattern) + + if err != nil { + return nil, err + } + + return tpl.Lookup(filename), err +} + +// replaceTemplate finds the template matching with name and replaces its +// contents with the contents of the template located at filename +func replaceTemplate(tpl *template.Template, name, filename string) error { + if tpl == nil { + return fmt.Errorf("template for %s is nil", name) + } + b, err := ioutil.ReadFile(filename) if err != nil { - return nil, errors.Wrapf(err, "failed reading template file: %s", filename) + return errors.Wrapf(err, "failed reading template file: %s", filename) } - if tpl == nil { - tpl = template.New(name) - } else { - tpl = tpl.New(name) - } - if tpl, err = tpl.Funcs(templateFunctions).Parse(string(b)); err != nil { - return nil, errors.Wrapf(err, "failed to parse template file: %s", filename) + if tpl, err = tpl.New(name).Funcs(templateFunctions).Parse(string(b)); err != nil { + return errors.Wrapf(err, "failed to parse template file: %s", filename) } - return tpl, nil + return nil } // set is to stop duplication from named enums, allowing a template loop From e70ed0d070bf860b052868600551738f2b7c92a7 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sun, 5 Feb 2017 02:27:55 +1000 Subject: [PATCH 040/179] Bump version --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index e8d2969..d601d0c 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( "github.com/vattle/sqlboiler/boilingcore" ) -const sqlBoilerVersion = "2.1.9" +const sqlBoilerVersion = "2.1.10" var ( cmdState *boilingcore.State From dea061f571ea9a575683c18b7ebf1e72f23d8b09 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sun, 5 Feb 2017 16:45:41 +1000 Subject: [PATCH 041/179] Use Base and Stat instead of os.Seperator split * Validates we're using filenames and ensures compatibility with all filesystems --- boilingcore/boilingcore.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index a86e403..e2ba606 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -215,23 +215,37 @@ func (s *State) processReplacements() error { return errors.Errorf("replace parameters must have 2 arguments, given: %s", replace) } + var toReplaceFname string toReplace, replaceWith := splits[0], splits[1] - toReplaceSplit := strings.Split(toReplace, string(filepath.Separator)) - fileName := toReplaceSplit[len(toReplaceSplit)-1] + inf, err := os.Stat(toReplace) + if err != nil { + return errors.Errorf("cannot stat %q", toReplace) + } + if inf.IsDir() { + return errors.Errorf("replace argument must be a path to a file not a dir: %q", toReplace) + } + toReplaceFname = inf.Name() + + inf, err = os.Stat(replaceWith) + if err != nil { + return errors.Errorf("cannot stat %q", replaceWith) + } + if inf.IsDir() { + return errors.Errorf("replace argument must be a path to a file not a dir: %q", replaceWith) + } - var err error switch filepath.Dir(toReplace) { case templatesDirectory: - err = replaceTemplate(s.Templates.Template, fileName, replaceWith) + err = replaceTemplate(s.Templates.Template, toReplaceFname, replaceWith) case templatesSingletonDirectory: - err = replaceTemplate(s.SingletonTemplates.Template, fileName, replaceWith) + err = replaceTemplate(s.SingletonTemplates.Template, toReplaceFname, replaceWith) case templatesTestDirectory: - err = replaceTemplate(s.TestTemplates.Template, fileName, replaceWith) + err = replaceTemplate(s.TestTemplates.Template, toReplaceFname, replaceWith) case templatesSingletonTestDirectory: - err = replaceTemplate(s.SingletonTestTemplates.Template, fileName, replaceWith) + err = replaceTemplate(s.SingletonTestTemplates.Template, toReplaceFname, replaceWith) case templatesTestMainDirectory: - err = replaceTemplate(s.TestMainTemplate, fileName, replaceWith) + err = replaceTemplate(s.TestMainTemplate, toReplaceFname, replaceWith) default: return errors.Errorf("replace file's directory not part of any known folder: %s", toReplace) } From e2c53aa3128c69d197aec785a22126c63375f039 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sun, 5 Feb 2017 20:17:11 +1000 Subject: [PATCH 042/179] Add imports library functionality --- boilingcore/boilingcore.go | 4 + boilingcore/imports.go | 465 +++++++++++++++++++++--------------- boilingcore/imports_test.go | 177 +++++++++++++- boilingcore/output.go | 14 +- 4 files changed, 464 insertions(+), 196 deletions(-) diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index e2ba606..e396994 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -43,6 +43,8 @@ type State struct { SingletonTestTemplates *templateList TestMainTemplate *template.Template + + Importer importer } // New creates a new state based off of the config @@ -89,6 +91,8 @@ func New(config *Config) (*State, error) { return nil, errors.Wrap(err, "unable to initialize struct tags") } + s.Importer = newImporter() + return s, nil } diff --git a/boilingcore/imports.go b/boilingcore/imports.go index 236d16d..ec2752e 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -141,211 +141,302 @@ func removeDuplicates(dedup []string) []string { return dedup } -var defaultTemplateImports = imports{ - standard: importList{ - `"bytes"`, - `"database/sql"`, - `"fmt"`, - `"reflect"`, - `"strings"`, - `"sync"`, - `"time"`, - }, - thirdParty: importList{ - `"github.com/pkg/errors"`, - `"github.com/vattle/sqlboiler/boil"`, - `"github.com/vattle/sqlboiler/queries"`, - `"github.com/vattle/sqlboiler/queries/qm"`, - `"github.com/vattle/sqlboiler/strmangle"`, - }, +type mapImports map[string]imports + +type importer struct { + standard imports + testStandard imports + + singleton mapImports + testSingleton mapImports + + testMain mapImports + + basedOnType mapImports } -var defaultSingletonTemplateImports = map[string]imports{ - "boil_queries": { - thirdParty: importList{ - `"github.com/vattle/sqlboiler/boil"`, - `"github.com/vattle/sqlboiler/queries"`, - `"github.com/vattle/sqlboiler/queries/qm"`, - }, - }, - "boil_types": { - thirdParty: importList{ - `"github.com/pkg/errors"`, - `"github.com/vattle/sqlboiler/strmangle"`, - }, - }, -} +// newImporter returns an importer struct with default import values +func newImporter() importer { + var imp importer -var defaultTestTemplateImports = imports{ - standard: importList{ - `"bytes"`, - `"reflect"`, - `"testing"`, - }, - thirdParty: importList{ - `"github.com/vattle/sqlboiler/boil"`, - `"github.com/vattle/sqlboiler/randomize"`, - `"github.com/vattle/sqlboiler/strmangle"`, - }, -} - -var defaultSingletonTestTemplateImports = map[string]imports{ - "boil_main_test": { + imp.standard = imports{ standard: importList{ + `"bytes"`, `"database/sql"`, - `"flag"`, `"fmt"`, - `"math/rand"`, - `"os"`, - `"path/filepath"`, - `"testing"`, + `"reflect"`, + `"strings"`, + `"sync"`, `"time"`, }, thirdParty: importList{ - `"github.com/kat-co/vala"`, `"github.com/pkg/errors"`, - `"github.com/spf13/viper"`, `"github.com/vattle/sqlboiler/boil"`, + `"github.com/vattle/sqlboiler/queries"`, + `"github.com/vattle/sqlboiler/queries/qm"`, + `"github.com/vattle/sqlboiler/strmangle"`, }, - }, - "boil_queries_test": { + } + + imp.singleton = mapImports{ + "boil_queries": { + thirdParty: importList{ + `"github.com/vattle/sqlboiler/boil"`, + `"github.com/vattle/sqlboiler/queries"`, + `"github.com/vattle/sqlboiler/queries/qm"`, + }, + }, + "boil_types": { + thirdParty: importList{ + `"github.com/pkg/errors"`, + `"github.com/vattle/sqlboiler/strmangle"`, + }, + }, + } + + imp.testStandard = imports{ standard: importList{ `"bytes"`, - `"fmt"`, - `"io"`, - `"io/ioutil"`, - `"math/rand"`, - `"regexp"`, - }, - thirdParty: importList{ - `"github.com/vattle/sqlboiler/boil"`, - }, - }, - "boil_suites_test": { - standard: importList{ + `"reflect"`, `"testing"`, }, - }, + thirdParty: importList{ + `"github.com/vattle/sqlboiler/boil"`, + `"github.com/vattle/sqlboiler/randomize"`, + `"github.com/vattle/sqlboiler/strmangle"`, + }, + } + + imp.testSingleton = mapImports{ + "boil_main_test": { + standard: importList{ + `"database/sql"`, + `"flag"`, + `"fmt"`, + `"math/rand"`, + `"os"`, + `"path/filepath"`, + `"testing"`, + `"time"`, + }, + thirdParty: importList{ + `"github.com/kat-co/vala"`, + `"github.com/pkg/errors"`, + `"github.com/spf13/viper"`, + `"github.com/vattle/sqlboiler/boil"`, + }, + }, + "boil_queries_test": { + standard: importList{ + `"bytes"`, + `"fmt"`, + `"io"`, + `"io/ioutil"`, + `"math/rand"`, + `"regexp"`, + }, + thirdParty: importList{ + `"github.com/vattle/sqlboiler/boil"`, + }, + }, + "boil_suites_test": { + standard: importList{ + `"testing"`, + }, + }, + } + + imp.testMain = mapImports{ + "postgres": { + standard: importList{ + `"bytes"`, + `"database/sql"`, + `"fmt"`, + `"io"`, + `"io/ioutil"`, + `"os"`, + `"os/exec"`, + `"strings"`, + }, + thirdParty: importList{ + `"github.com/pkg/errors"`, + `"github.com/spf13/viper"`, + `"github.com/vattle/sqlboiler/bdb/drivers"`, + `"github.com/vattle/sqlboiler/randomize"`, + `_ "github.com/lib/pq"`, + }, + }, + "mysql": { + standard: importList{ + `"bytes"`, + `"database/sql"`, + `"fmt"`, + `"io"`, + `"io/ioutil"`, + `"os"`, + `"os/exec"`, + `"strings"`, + }, + thirdParty: importList{ + `"github.com/pkg/errors"`, + `"github.com/spf13/viper"`, + `"github.com/vattle/sqlboiler/bdb/drivers"`, + `"github.com/vattle/sqlboiler/randomize"`, + `_ "github.com/go-sql-driver/mysql"`, + }, + }, + } + + // basedOnType imports are only included in the template output if the + // database requires one of the following special types. Check + // TranslateColumnType to see the type assignments. + imp.basedOnType = mapImports{ + "null.Float32": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Float64": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Int": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Int8": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Int16": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Int32": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Int64": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Uint": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Uint8": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Uint16": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Uint32": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Uint64": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.String": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Bool": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Time": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.JSON": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "null.Bytes": { + thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + }, + "time.Time": { + standard: importList{`"time"`}, + }, + "types.JSON": { + thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + }, + "types.BytesArray": { + thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + }, + "types.Int64Array": { + thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + }, + "types.Float64Array": { + thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + }, + "types.BoolArray": { + thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + }, + "types.StringArray": { + thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + }, + "types.Hstore": { + thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + }, + } + + return imp } -var defaultTestMainImports = map[string]imports{ - "postgres": { - standard: importList{ - `"bytes"`, - `"database/sql"`, - `"fmt"`, - `"io"`, - `"io/ioutil"`, - `"os"`, - `"os/exec"`, - `"strings"`, - }, - thirdParty: importList{ - `"github.com/pkg/errors"`, - `"github.com/spf13/viper"`, - `"github.com/vattle/sqlboiler/bdb/drivers"`, - `"github.com/vattle/sqlboiler/randomize"`, - `_ "github.com/lib/pq"`, - }, - }, - "mysql": { - standard: importList{ - `"bytes"`, - `"database/sql"`, - `"fmt"`, - `"io"`, - `"io/ioutil"`, - `"os"`, - `"os/exec"`, - `"strings"`, - }, - thirdParty: importList{ - `"github.com/pkg/errors"`, - `"github.com/spf13/viper"`, - `"github.com/vattle/sqlboiler/bdb/drivers"`, - `"github.com/vattle/sqlboiler/randomize"`, - `_ "github.com/go-sql-driver/mysql"`, - }, - }, +// Remove an import matching the match string under the specified key. +// Remove will search both standard and thirdParty import lists for a match. +func (m mapImports) Remove(key string, match string) { + mp := m[key] + for idx := 0; idx < len(mp.standard); idx++ { + if mp.standard[idx] == match { + mp.standard[idx] = mp.standard[len(mp.standard)-1] + mp.standard = mp.standard[:len(mp.standard)-1] + break + } + } + for idx := 0; idx < len(mp.thirdParty); idx++ { + if mp.thirdParty[idx] == match { + mp.thirdParty[idx] = mp.thirdParty[len(mp.thirdParty)-1] + mp.thirdParty = mp.thirdParty[:len(mp.thirdParty)-1] + break + } + } + + // delete the key and return if both import lists are empty + if len(mp.thirdParty) == 0 && len(mp.standard) == 0 { + delete(m, key) + return + } + + m[key] = mp } -// importsBasedOnType imports are only included in the template output if the -// database requires one of the following special types. Check -// TranslateColumnType to see the type assignments. -var importsBasedOnType = map[string]imports{ - "null.Float32": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Float64": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Int": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Int8": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Int16": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Int32": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Int64": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Uint": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Uint8": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Uint16": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Uint32": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Uint64": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.String": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Bool": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Time": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.JSON": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "null.Bytes": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, - }, - "time.Time": { - standard: importList{`"time"`}, - }, - "types.JSON": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, - }, - "types.BytesArray": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, - }, - "types.Int64Array": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, - }, - "types.Float64Array": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, - }, - "types.BoolArray": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, - }, - "types.StringArray": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, - }, - "types.Hstore": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, - }, +// Add an import under the specified key. If the key does not exist, it +// will be created. +func (m mapImports) Add(key string, value string, thirdParty bool) { + mp := m[key] + if thirdParty { + mp.thirdParty = append(mp.thirdParty, value) + } else { + mp.standard = append(mp.standard, value) + } + + m[key] = mp +} + +// Remove an import matching the match string under the specified key. +// Remove will search both standard and thirdParty import lists for a match. +func (i *imports) Remove(match string) { + for idx := 0; idx < len(i.standard); idx++ { + if i.standard[idx] == match { + i.standard[idx] = i.standard[len(i.standard)-1] + i.standard = i.standard[:len(i.standard)-1] + break + } + } + for idx := 0; idx < len(i.thirdParty); idx++ { + if i.thirdParty[idx] == match { + i.thirdParty[idx] = i.thirdParty[len(i.thirdParty)-1] + i.thirdParty = i.thirdParty[:len(i.thirdParty)-1] + break + } + } +} + +// Add an import under the specified key. If the key does not exist, it +// will be created. +func (i *imports) Add(value string, thirdParty bool) { + if thirdParty { + i.thirdParty = append(i.thirdParty, value) + } else { + i.standard = append(i.standard, value) + } } diff --git a/boilingcore/imports_test.go b/boilingcore/imports_test.go index 43c46e5..411408b 100644 --- a/boilingcore/imports_test.go +++ b/boilingcore/imports_test.go @@ -54,6 +54,177 @@ func TestImportsSort(t *testing.T) { } } +func TestImportsAddAndRemove(t *testing.T) { + t.Parallel() + + var imp imports + imp.Add("value", false) + if len(imp.standard) != 1 { + t.Errorf("expected len 1, got %d", len(imp.standard)) + } + if imp.standard[0] != "value" { + t.Errorf("expected %q to be added", "value") + } + imp.Add("value2", true) + if len(imp.thirdParty) != 1 { + t.Errorf("expected len 1, got %d", len(imp.thirdParty)) + } + if imp.thirdParty[0] != "value2" { + t.Errorf("expected %q to be added", "value2") + } + + imp.Remove("value") + if len(imp.standard) != 0 { + t.Errorf("expected len 0, got %d", len(imp.standard)) + } + imp.Remove("value") + if len(imp.standard) != 0 { + t.Errorf("expected len 0, got %d", len(imp.standard)) + } + imp.Remove("value2") + if len(imp.thirdParty) != 0 { + t.Errorf("expected len 0, got %d", len(imp.thirdParty)) + } + + // Test deleting last element in len 2 slice + imp.Add("value3", false) + imp.Add("value4", false) + if len(imp.standard) != 2 { + t.Errorf("expected len 2, got %d", len(imp.standard)) + } + imp.Remove("value4") + if len(imp.standard) != 1 { + t.Errorf("expected len 1, got %d", len(imp.standard)) + } + if imp.standard[0] != "value3" { + t.Errorf("expected %q, got %q", "value3", imp.standard[0]) + } + // Test deleting first element in len 2 slice + imp.Add("value4", false) + imp.Remove("value3") + if len(imp.standard) != 1 { + t.Errorf("expected len 1, got %d", len(imp.standard)) + } + if imp.standard[0] != "value4" { + t.Errorf("expected %q, got %q", "value4", imp.standard[0]) + } + imp.Remove("value2") + if len(imp.thirdParty) != 0 { + t.Errorf("expected len 0, got %d", len(imp.thirdParty)) + } + + // Test deleting last element in len 2 slice + imp.Add("value5", true) + imp.Add("value6", true) + if len(imp.thirdParty) != 2 { + t.Errorf("expected len 2, got %d", len(imp.thirdParty)) + } + imp.Remove("value6") + if len(imp.thirdParty) != 1 { + t.Errorf("expected len 1, got %d", len(imp.thirdParty)) + } + if imp.thirdParty[0] != "value5" { + t.Errorf("expected %q, got %q", "value5", imp.thirdParty[0]) + } + // Test deleting first element in len 2 slice + imp.Add("value6", true) + imp.Remove("value5") + if len(imp.thirdParty) != 1 { + t.Errorf("expected len 1, got %d", len(imp.thirdParty)) + } + if imp.thirdParty[0] != "value6" { + t.Errorf("expected %q, got %q", "value6", imp.thirdParty[0]) + } +} + +func TestMapImportsAddAndRemove(t *testing.T) { + t.Parallel() + + imp := mapImports{} + imp.Add("cat", "value", false) + if len(imp["cat"].standard) != 1 { + t.Errorf("expected len 1, got %d", len(imp["cat"].standard)) + } + if imp["cat"].standard[0] != "value" { + t.Errorf("expected %q to be added", "value") + } + imp.Add("cat", "value2", true) + if len(imp["cat"].thirdParty) != 1 { + t.Errorf("expected len 1, got %d", len(imp["cat"].thirdParty)) + } + if imp["cat"].thirdParty[0] != "value2" { + t.Errorf("expected %q to be added", "value2") + } + + imp.Remove("cat", "value") + if len(imp["cat"].standard) != 0 { + t.Errorf("expected len 0, got %d", len(imp["cat"].standard)) + } + imp.Remove("cat", "value") + if len(imp["cat"].standard) != 0 { + t.Errorf("expected len 0, got %d", len(imp["cat"].standard)) + } + imp.Remove("cat", "value2") + if len(imp["cat"].thirdParty) != 0 { + t.Errorf("expected len 0, got %d", len(imp["cat"].thirdParty)) + } + // If there are no elements left in key, test key is deleted + _, ok := imp["cat"] + if ok { + t.Errorf("expected cat key to be deleted when list empty") + } + + // Test deleting last element in len 2 slice + imp.Add("cat", "value3", false) + imp.Add("cat", "value4", false) + if len(imp["cat"].standard) != 2 { + t.Errorf("expected len 2, got %d", len(imp["cat"].standard)) + } + imp.Remove("cat", "value4") + if len(imp["cat"].standard) != 1 { + t.Errorf("expected len 1, got %d", len(imp["cat"].standard)) + } + if imp["cat"].standard[0] != "value3" { + t.Errorf("expected %q, got %q", "value3", imp["cat"].standard[0]) + } + // Test deleting first element in len 2 slice + imp.Add("cat", "value4", false) + imp.Remove("cat", "value3") + if len(imp["cat"].standard) != 1 { + t.Errorf("expected len 1, got %d", len(imp["cat"].standard)) + } + if imp["cat"].standard[0] != "value4" { + t.Errorf("expected %q, got %q", "value4", imp["cat"].standard[0]) + } + imp.Remove("cat", "value2") + if len(imp["cat"].thirdParty) != 0 { + t.Errorf("expected len 0, got %d", len(imp["cat"].thirdParty)) + } + + // Test deleting last element in len 2 slice + imp.Add("dog", "value5", true) + imp.Add("dog", "value6", true) + if len(imp["dog"].thirdParty) != 2 { + t.Errorf("expected len 2, got %d", len(imp["dog"].thirdParty)) + } + imp.Remove("dog", "value6") + if len(imp["dog"].thirdParty) != 1 { + t.Errorf("expected len 1, got %d", len(imp["dog"].thirdParty)) + } + if imp["dog"].thirdParty[0] != "value5" { + t.Errorf("expected %q, got %q", "value5", imp["dog"].thirdParty[0]) + } + // Test deleting first element in len 2 slice + imp.Add("dog", "value6", true) + imp.Remove("dog", "value5") + if len(imp["dog"].thirdParty) != 1 { + t.Errorf("expected len 1, got %d", len(imp["dog"].thirdParty)) + } + if imp["dog"].thirdParty[0] != "value6" { + t.Errorf("expected %q, got %q", "value6", imp["dog"].thirdParty[0]) + } +} + func TestCombineTypeImports(t *testing.T) { t.Parallel() @@ -94,7 +265,9 @@ func TestCombineTypeImports(t *testing.T) { }, } - res1 := combineTypeImports(imports1, importsBasedOnType, cols) + imps := newImporter() + + res1 := combineTypeImports(imports1, imps.basedOnType, cols) if !reflect.DeepEqual(res1, importsExpected) { t.Errorf("Expected res1 to match importsExpected, got:\n\n%#v\n", res1) @@ -112,7 +285,7 @@ func TestCombineTypeImports(t *testing.T) { }, } - res2 := combineTypeImports(imports2, importsBasedOnType, cols) + res2 := combineTypeImports(imports2, imps.basedOnType, cols) if !reflect.DeepEqual(res2, importsExpected) { t.Errorf("Expected res2 to match importsExpected, got:\n\n%#v\n", res1) diff --git a/boilingcore/output.go b/boilingcore/output.go index ffbde94..e7192f1 100644 --- a/boilingcore/output.go +++ b/boilingcore/output.go @@ -38,7 +38,7 @@ func generateOutput(state *State, data *templateData) error { state: state, data: data, templates: state.Templates, - importSet: defaultTemplateImports, + importSet: state.Importer.standard, combineImportsOnType: true, fileSuffix: ".go", }) @@ -50,7 +50,7 @@ func generateTestOutput(state *State, data *templateData) error { state: state, data: data, templates: state.TestTemplates, - importSet: defaultTestTemplateImports, + importSet: state.Importer.testStandard, combineImportsOnType: false, fileSuffix: "_test.go", }) @@ -63,7 +63,7 @@ func generateSingletonOutput(state *State, data *templateData) error { state: state, data: data, templates: state.SingletonTemplates, - importNamedSet: defaultSingletonTemplateImports, + importNamedSet: state.Importer.singleton, fileSuffix: ".go", }) } @@ -75,7 +75,7 @@ func generateSingletonTestOutput(state *State, data *templateData) error { state: state, data: data, templates: state.SingletonTestTemplates, - importNamedSet: defaultSingletonTestTemplateImports, + importNamedSet: state.Importer.testSingleton, fileSuffix: ".go", }) } @@ -106,7 +106,7 @@ func executeTemplates(e executeTemplateData) error { imps.standard = e.importSet.standard imps.thirdParty = e.importSet.thirdParty if e.combineImportsOnType { - imps = combineTypeImports(imps, importsBasedOnType, e.data.Table.Columns) + imps = combineTypeImports(imps, e.state.Importer.basedOnType, e.data.Table.Columns) } writeFileDisclaimer(out) @@ -170,8 +170,8 @@ func generateTestMainOutput(state *State, data *templateData) error { out.Reset() var imps imports - imps.standard = defaultTestMainImports[state.Config.DriverName].standard - imps.thirdParty = defaultTestMainImports[state.Config.DriverName].thirdParty + imps.standard = state.Importer.testMain[state.Config.DriverName].standard + imps.thirdParty = state.Importer.testMain[state.Config.DriverName].thirdParty writeFileDisclaimer(out) writePackageName(out, state.Config.PkgName) From 37477542361ac2382cf860f06f8b289037b58044 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sun, 5 Feb 2017 21:41:30 +1000 Subject: [PATCH 043/179] export importer members and add basepath for Stat --- boilingcore/boilingcore.go | 7 ++++++- boilingcore/imports.go | 24 ++++++++++++------------ boilingcore/imports_test.go | 4 ++-- boilingcore/output.go | 14 +++++++------- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index e396994..cf33e0e 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -213,6 +213,11 @@ func (s *State) initTemplates() error { // processReplacements loads any replacement templates func (s *State) processReplacements() error { + basePath, err := getBasePath(s.Config.BaseDir) + if err != nil { + return err + } + for _, replace := range s.Config.Replacements { splits := strings.Split(replace, ":") if len(splits) != 2 { @@ -222,7 +227,7 @@ func (s *State) processReplacements() error { var toReplaceFname string toReplace, replaceWith := splits[0], splits[1] - inf, err := os.Stat(toReplace) + inf, err := os.Stat(filepath.Join(basePath, toReplace)) if err != nil { return errors.Errorf("cannot stat %q", toReplace) } diff --git a/boilingcore/imports.go b/boilingcore/imports.go index ec2752e..3ad8411 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -144,22 +144,22 @@ func removeDuplicates(dedup []string) []string { type mapImports map[string]imports type importer struct { - standard imports - testStandard imports + Standard imports + TestStandard imports - singleton mapImports - testSingleton mapImports + Singleton mapImports + TestSingleton mapImports - testMain mapImports + TestMain mapImports - basedOnType mapImports + BasedOnType mapImports } // newImporter returns an importer struct with default import values func newImporter() importer { var imp importer - imp.standard = imports{ + imp.Standard = imports{ standard: importList{ `"bytes"`, `"database/sql"`, @@ -178,7 +178,7 @@ func newImporter() importer { }, } - imp.singleton = mapImports{ + imp.Singleton = mapImports{ "boil_queries": { thirdParty: importList{ `"github.com/vattle/sqlboiler/boil"`, @@ -194,7 +194,7 @@ func newImporter() importer { }, } - imp.testStandard = imports{ + imp.TestStandard = imports{ standard: importList{ `"bytes"`, `"reflect"`, @@ -207,7 +207,7 @@ func newImporter() importer { }, } - imp.testSingleton = mapImports{ + imp.TestSingleton = mapImports{ "boil_main_test": { standard: importList{ `"database/sql"`, @@ -246,7 +246,7 @@ func newImporter() importer { }, } - imp.testMain = mapImports{ + imp.TestMain = mapImports{ "postgres": { standard: importList{ `"bytes"`, @@ -290,7 +290,7 @@ func newImporter() importer { // basedOnType imports are only included in the template output if the // database requires one of the following special types. Check // TranslateColumnType to see the type assignments. - imp.basedOnType = mapImports{ + imp.BasedOnType = mapImports{ "null.Float32": { thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, }, diff --git a/boilingcore/imports_test.go b/boilingcore/imports_test.go index 411408b..6f2cde4 100644 --- a/boilingcore/imports_test.go +++ b/boilingcore/imports_test.go @@ -267,7 +267,7 @@ func TestCombineTypeImports(t *testing.T) { imps := newImporter() - res1 := combineTypeImports(imports1, imps.basedOnType, cols) + res1 := combineTypeImports(imports1, imps.BasedOnType, cols) if !reflect.DeepEqual(res1, importsExpected) { t.Errorf("Expected res1 to match importsExpected, got:\n\n%#v\n", res1) @@ -285,7 +285,7 @@ func TestCombineTypeImports(t *testing.T) { }, } - res2 := combineTypeImports(imports2, imps.basedOnType, cols) + res2 := combineTypeImports(imports2, imps.BasedOnType, cols) if !reflect.DeepEqual(res2, importsExpected) { t.Errorf("Expected res2 to match importsExpected, got:\n\n%#v\n", res1) diff --git a/boilingcore/output.go b/boilingcore/output.go index e7192f1..64f28b5 100644 --- a/boilingcore/output.go +++ b/boilingcore/output.go @@ -38,7 +38,7 @@ func generateOutput(state *State, data *templateData) error { state: state, data: data, templates: state.Templates, - importSet: state.Importer.standard, + importSet: state.Importer.Standard, combineImportsOnType: true, fileSuffix: ".go", }) @@ -50,7 +50,7 @@ func generateTestOutput(state *State, data *templateData) error { state: state, data: data, templates: state.TestTemplates, - importSet: state.Importer.testStandard, + importSet: state.Importer.TestStandard, combineImportsOnType: false, fileSuffix: "_test.go", }) @@ -63,7 +63,7 @@ func generateSingletonOutput(state *State, data *templateData) error { state: state, data: data, templates: state.SingletonTemplates, - importNamedSet: state.Importer.singleton, + importNamedSet: state.Importer.Singleton, fileSuffix: ".go", }) } @@ -75,7 +75,7 @@ func generateSingletonTestOutput(state *State, data *templateData) error { state: state, data: data, templates: state.SingletonTestTemplates, - importNamedSet: state.Importer.testSingleton, + importNamedSet: state.Importer.TestSingleton, fileSuffix: ".go", }) } @@ -106,7 +106,7 @@ func executeTemplates(e executeTemplateData) error { imps.standard = e.importSet.standard imps.thirdParty = e.importSet.thirdParty if e.combineImportsOnType { - imps = combineTypeImports(imps, e.state.Importer.basedOnType, e.data.Table.Columns) + imps = combineTypeImports(imps, e.state.Importer.BasedOnType, e.data.Table.Columns) } writeFileDisclaimer(out) @@ -170,8 +170,8 @@ func generateTestMainOutput(state *State, data *templateData) error { out.Reset() var imps imports - imps.standard = state.Importer.testMain[state.Config.DriverName].standard - imps.thirdParty = state.Importer.testMain[state.Config.DriverName].thirdParty + imps.standard = state.Importer.TestMain[state.Config.DriverName].standard + imps.thirdParty = state.Importer.TestMain[state.Config.DriverName].thirdParty writeFileDisclaimer(out) writePackageName(out, state.Config.PkgName) From 8aa4e2148c0711d872e270dccff9b4a08325e664 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Fri, 24 Feb 2017 22:27:05 -0800 Subject: [PATCH 044/179] Use _ to avoid reserved words in func args - Fix #107 --- boilingcore/templates.go | 3 ++- strmangle/strmangle.go | 37 +++++++++++++++++++++++++++++++++++++ strmangle/strmangle_test.go | 20 ++++++++++++++++++++ templates/14_find.tpl | 2 +- templates/20_exists.tpl | 2 +- 5 files changed, 61 insertions(+), 3 deletions(-) diff --git a/boilingcore/templates.go b/boilingcore/templates.go index 13f4142..dec57bb 100644 --- a/boilingcore/templates.go +++ b/boilingcore/templates.go @@ -168,7 +168,8 @@ func (o once) Put(s string) bool { // stringMap function. var templateStringMappers = map[string]func(string) string{ // String ops - "quoteWrap": func(a string) string { return fmt.Sprintf(`"%s"`, a) }, + "quoteWrap": func(a string) string { return fmt.Sprintf(`"%s"`, a) }, + "replaceReserved": strmangle.ReplaceReservedWords, // Casing "titleCase": strmangle.TitleCase, diff --git a/strmangle/strmangle.go b/strmangle/strmangle.go index ab93d26..316e1b0 100644 --- a/strmangle/strmangle.go +++ b/strmangle/strmangle.go @@ -43,6 +43,34 @@ var uppercaseWords = map[string]struct{}{ "utf8": {}, } +var reservedWords = map[string]struct{}{ + "break": {}, + "case": {}, + "chan": {}, + "const": {}, + "continue": {}, + "default": {}, + "defer": {}, + "else": {}, + "fallthrough": {}, + "for": {}, + "func": {}, + "go": {}, + "goto": {}, + "if": {}, + "import": {}, + "interface": {}, + "map": {}, + "package": {}, + "range": {}, + "return": {}, + "select": {}, + "struct": {}, + "switch": {}, + "type": {}, + "var": {}, +} + func init() { // Our Boil inflection Ruleset does not include uncountable inflections. // This way, people using words like Sheep will not have @@ -630,3 +658,12 @@ func IsEnumNormal(values []string) bool { func ShouldTitleCaseEnum(value string) bool { return rgxEnumShouldTitle.MatchString(value) } + +// ReplaceReservedWords takes a word and replaces it with word_ if it's found +// in the list of reserved words. +func ReplaceReservedWords(word string) string { + if _, ok := reservedWords[word]; ok { + return word + "_" + } + return word +} diff --git a/strmangle/strmangle_test.go b/strmangle/strmangle_test.go index 2a14af5..b07cee6 100644 --- a/strmangle/strmangle_test.go +++ b/strmangle/strmangle_test.go @@ -580,3 +580,23 @@ func TestShouldTitleCaseEnum(t *testing.T) { } } } + +func TestReplaceReservedWords(t *testing.T) { + tests := []struct { + Word string + Replace bool + }{ + {"break", true}, + {"id", false}, + {"type", true}, + } + + for i, test := range tests { + got := ReplaceReservedWords(test.Word) + if test.Replace && !strings.HasSuffix(got, "_") { + t.Errorf("%i) want suffixed (%s), got: %s", i, test.Word, got) + } else if !test.Replace && strings.HasSuffix(got, "_") { + t.Errorf("%i) want normal (%s), got: %s", i, test.Word, got) + } + } +} diff --git a/templates/14_find.tpl b/templates/14_find.tpl index 4ce7b15..097f184 100644 --- a/templates/14_find.tpl +++ b/templates/14_find.tpl @@ -1,7 +1,7 @@ {{- $tableNameSingular := .Table.Name | singular | titleCase -}} {{- $varNameSingular := .Table.Name | singular | camelCase -}} {{- $colDefs := sqlColDefinitions .Table.Columns .Table.PKey.Columns -}} -{{- $pkNames := $colDefs.Names | stringMap .StringFuncs.camelCase -}} +{{- $pkNames := $colDefs.Names | stringMap .StringFuncs.camelCase | stringMap .StringFuncs.replaceReserved -}} {{- $pkArgs := joinSlices " " $pkNames $colDefs.Types | join ", "}} // Find{{$tableNameSingular}}G retrieves a single record by ID. func Find{{$tableNameSingular}}G({{$pkArgs}}, selectCols ...string) (*{{$tableNameSingular}}, error) { diff --git a/templates/20_exists.tpl b/templates/20_exists.tpl index 2e768f1..3a9c693 100644 --- a/templates/20_exists.tpl +++ b/templates/20_exists.tpl @@ -1,6 +1,6 @@ {{- $tableNameSingular := .Table.Name | singular | titleCase -}} {{- $colDefs := sqlColDefinitions .Table.Columns .Table.PKey.Columns -}} -{{- $pkNames := $colDefs.Names | stringMap .StringFuncs.camelCase -}} +{{- $pkNames := $colDefs.Names | stringMap .StringFuncs.camelCase | stringMap .StringFuncs.replaceReserved -}} {{- $pkArgs := joinSlices " " $pkNames $colDefs.Types | join ", " -}} {{- $schemaTable := .Table.Name | .SchemaTable}} // {{$tableNameSingular}}Exists checks if the {{$tableNameSingular}} row exists. From a5cb765403b743ecaa6fc482384a2699c66538be Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Fri, 24 Feb 2017 22:52:09 -0800 Subject: [PATCH 045/179] Ensure created_at isn't updated when unnecessary - When we build the update whitelist (when the user doesn't provide one) we simply say that the whitelist is all columns except pkeys. The reason we do this is because sqlboiler doesn't support dirty tracking and if you want to update just a few fields, it's easier to just explicitly pass a whitelist when you update. However, in the case of created_at - it's a field that's usually managed by us and so we should not be updating it unless a user explicitly requests it via the whitelist. So if the whitelist is empty we prune that column now if it exists from the auto-created whitelist. - The user also reported that upsert has the same fate, but the logic is somewhat different there. It expects that there's a value for created_at or it assumes that one needs to be created. There's no more magic we can do around this one unfortunately. Upsert is a mythical beast that doesn't play as nicely with our model, so users will just have to be more careful. - Fix #106 --- templates/16_update.tpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templates/16_update.tpl b/templates/16_update.tpl index 0f5dad5..f84a35c 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -49,6 +49,11 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string if !cached { wl := strmangle.UpdateColumnSet({{$varNameSingular}}Columns, {{$varNameSingular}}PrimaryKeyColumns, whitelist) + {{- if not .NoAutoTimestamps}} + if len(whitelist) == 0 { + wl = strmangle.SetComplement(wl, []string{"created_at"}) + } + {{end -}} if len(wl) == 0 { return errors.New("{{.PkgName}}: unable to update {{.Table.Name}}, could not build whitelist") } From 8f96ba35d61c6bcfc16b265fe4439dea521a94bc Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Wed, 1 Mar 2017 18:18:47 -0800 Subject: [PATCH 046/179] Allow insertion of only-defaulted tables - Tables that are only comprised of default values and have nothing passed into them on insertion will no longer generate syntax errors. --- queries/query_builders.go | 21 ++++++++++++++++----- templates/15_insert.tpl | 10 +++++++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/queries/query_builders.go b/queries/query_builders.go index 081ca58..683c6b5 100644 --- a/queries/query_builders.go +++ b/queries/query_builders.go @@ -190,12 +190,17 @@ func BuildUpsertQueryMySQL(dia Dialect, tableName string, update, whitelist []st buf := strmangle.GetBuffer() defer strmangle.PutBuffer(buf) + var columns string + if len(whitelist) != 0 { + columns = strings.Join(whitelist, ", ") + } + if len(update) == 0 { fmt.Fprintf( buf, "INSERT IGNORE INTO %s (%s) VALUES (%s)", tableName, - strings.Join(whitelist, ", "), + columns, strmangle.Placeholders(dia.IndexPlaceholders, len(whitelist), 1, 1), ) return buf.String() @@ -205,7 +210,7 @@ func BuildUpsertQueryMySQL(dia Dialect, tableName string, update, whitelist []st buf, "INSERT INTO %s (%s) VALUES (%s) ON DUPLICATE KEY UPDATE ", tableName, - strings.Join(whitelist, ", "), + columns, strmangle.Placeholders(dia.IndexPlaceholders, len(whitelist), 1, 1), ) @@ -232,12 +237,18 @@ func BuildUpsertQueryPostgres(dia Dialect, tableName string, updateOnConflict bo buf := strmangle.GetBuffer() defer strmangle.PutBuffer(buf) + columns := "DEFAULT VALUES" + if len(whitelist) != 0 { + columns = fmt.Sprintf("(%s) VALUES (%s)", + strings.Join(whitelist, ", "), + strmangle.Placeholders(dia.IndexPlaceholders, len(whitelist), 1, 1)) + } + fmt.Fprintf( buf, - "INSERT INTO %s (%s) VALUES (%s) ON CONFLICT ", + "INSERT INTO %s %s ON CONFLICT ", tableName, - strings.Join(whitelist, ", "), - strmangle.Placeholders(dia.IndexPlaceholders, len(whitelist), 1, 1), + columns, ) if !updateOnConflict || len(update) == 0 { diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index adf5f72..5fea2c3 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -65,7 +65,15 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string if err != nil { return err } - cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) VALUES (%s)", strings.Join(wl, "{{.LQ}},{{.RQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) + if len(wl) != 0 { + cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) VALUES (%s)", strings.Join(wl, "{{.LQ}},{{.RQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) + } else { + {{if eq .DriverName "mysql" -}} + cache.query = "INSERT INTO {{$schemaTable}} () VALUES ()" + {{else -}} + cache.query = "INSERT INTO {{$schemaTable}} DEFAULT VALUES" + {{end -}} + } if len(cache.retMapping) != 0 { {{if .UseLastInsertID -}} From 9067237f32b9bab8ed50562c624fa517503a0abb Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Wed, 1 Mar 2017 18:31:16 -0800 Subject: [PATCH 047/179] Bump Version --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index d601d0c..dd2620f 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( "github.com/vattle/sqlboiler/boilingcore" ) -const sqlBoilerVersion = "2.1.10" +const sqlBoilerVersion = "2.2.0" var ( cmdState *boilingcore.State From 60a40eaf48953e1a449a3ce003b963cc0752bcc7 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 4 Mar 2017 12:17:26 +1000 Subject: [PATCH 048/179] Fix randomize vals wrapping around to ngative vals * Use modulus to ensure values are within valid ranges --- randomize/randomize.go | 29 +++++++++++++++-------------- testdata/mysql_test_schema.sql | 13 +++++++++++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/randomize/randomize.go b/randomize/randomize.go index df5d75e..0dd2e42 100644 --- a/randomize/randomize.go +++ b/randomize/randomize.go @@ -4,6 +4,7 @@ package randomize import ( "database/sql" "fmt" + "math" "math/rand" "reflect" "regexp" @@ -71,7 +72,7 @@ func NewSeed() *Seed { } func (s *Seed) nextInt() int { - return int(atomic.AddInt64((*int64)(s), 1)) + return int(atomic.AddInt64((*int64)(s), 1)) % math.MaxInt32 } // Struct gets its fields filled with random data based on the seed. @@ -501,23 +502,23 @@ func getStructRandValue(s *Seed, typ reflect.Type) interface{} { case typeNullFloat64: return null.NewFloat64(float64(s.nextInt()%10)/10.0+float64(s.nextInt()%10), true) case typeNullInt: - return null.NewInt(int(int32(s.nextInt())), true) + return null.NewInt(int(int32(s.nextInt()))%math.MaxInt32, true) case typeNullInt8: - return null.NewInt8(int8(s.nextInt()), true) + return null.NewInt8(int8(s.nextInt())%math.MaxInt8, true) case typeNullInt16: - return null.NewInt16(int16(s.nextInt()), true) + return null.NewInt16(int16(s.nextInt())%math.MaxInt16, true) case typeNullInt32: - return null.NewInt32(int32(s.nextInt()), true) + return null.NewInt32(int32(s.nextInt())%math.MaxInt32, true) case typeNullInt64: return null.NewInt64(int64(s.nextInt()), true) case typeNullUint: return null.NewUint(uint(s.nextInt()), true) case typeNullUint8: - return null.NewUint8(uint8(s.nextInt()), true) + return null.NewUint8(uint8(s.nextInt())%math.MaxUint8, true) case typeNullUint16: - return null.NewUint16(uint16(s.nextInt()), true) + return null.NewUint16(uint16(s.nextInt())%math.MaxUint16, true) case typeNullUint32: - return null.NewUint32(uint32(s.nextInt()), true) + return null.NewUint32(uint32(s.nextInt())%math.MaxUint32, true) case typeNullUint64: return null.NewUint64(uint64(s.nextInt()), true) case typeNullBytes: @@ -590,21 +591,21 @@ func getVariableRandValue(s *Seed, kind reflect.Kind, typ reflect.Type) interfac case reflect.Int: return s.nextInt() case reflect.Int8: - return int8(s.nextInt()) + return int8(s.nextInt() % math.MaxInt8) case reflect.Int16: - return int16(s.nextInt()) + return int16(s.nextInt() % math.MaxInt16) case reflect.Int32: - return int32(s.nextInt()) + return int32(s.nextInt() % math.MaxInt32) case reflect.Int64: return int64(s.nextInt()) case reflect.Uint: return uint(s.nextInt()) case reflect.Uint8: - return uint8(s.nextInt()) + return uint8(s.nextInt() % math.MaxUint8) case reflect.Uint16: - return uint16(s.nextInt()) + return uint16(s.nextInt() % math.MaxUint16) case reflect.Uint32: - return uint32(s.nextInt()) + return uint32(s.nextInt() % math.MaxUint32) case reflect.Uint64: return uint64(s.nextInt()) case reflect.Bool: diff --git a/testdata/mysql_test_schema.sql b/testdata/mysql_test_schema.sql index 4755a63..7ed866e 100644 --- a/testdata/mysql_test_schema.sql +++ b/testdata/mysql_test_schema.sql @@ -352,3 +352,16 @@ CREATE TABLE pilot_languages ( ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pkey PRIMARY KEY (pilot_id, language_id); ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); ALTER TABLE pilot_languages ADD CONSTRAINT languages_fkey FOREIGN KEY (language_id) REFERENCES languages(id); + +CREATE TABLE powers_of_two ( + vid int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(255) NOT NULL DEFAULT '', + machine_name varchar(255) NOT NULL DEFAULT '', + description longtext, + hierarchy tinyint(3) unsigned NOT NULL DEFAULT '0', + module varchar(255) NOT NULL DEFAULT '', + weight int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (vid), + UNIQUE KEY machine_name (machine_name), + KEY list (weight,name) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; From e8723a2797b9608411b5b7649996836cf5514807 Mon Sep 17 00:00:00 2001 From: Patrick O'brien <nullbio@users.noreply.github.com> Date: Sat, 4 Mar 2017 20:15:05 +1000 Subject: [PATCH 049/179] Fix modulus fix --- randomize/randomize.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/randomize/randomize.go b/randomize/randomize.go index 0dd2e42..59f7eff 100644 --- a/randomize/randomize.go +++ b/randomize/randomize.go @@ -72,7 +72,7 @@ func NewSeed() *Seed { } func (s *Seed) nextInt() int { - return int(atomic.AddInt64((*int64)(s), 1)) % math.MaxInt32 + return int(atomic.AddInt64((*int64)(s), 1) % math.MaxInt32) } // Struct gets its fields filled with random data based on the seed. @@ -502,23 +502,23 @@ func getStructRandValue(s *Seed, typ reflect.Type) interface{} { case typeNullFloat64: return null.NewFloat64(float64(s.nextInt()%10)/10.0+float64(s.nextInt()%10), true) case typeNullInt: - return null.NewInt(int(int32(s.nextInt()))%math.MaxInt32, true) + return null.NewInt(int(int32(s.nextInt()%math.MaxInt32)), true) case typeNullInt8: - return null.NewInt8(int8(s.nextInt())%math.MaxInt8, true) + return null.NewInt8(int8(s.nextInt()%math.MaxInt8), true) case typeNullInt16: - return null.NewInt16(int16(s.nextInt())%math.MaxInt16, true) + return null.NewInt16(int16(s.nextInt()%math.MaxInt16), true) case typeNullInt32: - return null.NewInt32(int32(s.nextInt())%math.MaxInt32, true) + return null.NewInt32(int32(s.nextInt()%math.MaxInt32), true) case typeNullInt64: return null.NewInt64(int64(s.nextInt()), true) case typeNullUint: return null.NewUint(uint(s.nextInt()), true) case typeNullUint8: - return null.NewUint8(uint8(s.nextInt())%math.MaxUint8, true) + return null.NewUint8(uint8(s.nextInt()%math.MaxUint8), true) case typeNullUint16: - return null.NewUint16(uint16(s.nextInt())%math.MaxUint16, true) + return null.NewUint16(uint16(s.nextInt()%math.MaxUint16), true) case typeNullUint32: - return null.NewUint32(uint32(s.nextInt())%math.MaxUint32, true) + return null.NewUint32(uint32(s.nextInt()%math.MaxUint32), true) case typeNullUint64: return null.NewUint64(uint64(s.nextInt()), true) case typeNullBytes: From e00ea6067963487bec2995e9bb2bf064a2c5ad05 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Mon, 13 Mar 2017 12:55:26 +0300 Subject: [PATCH 050/179] Initial MSSQL support --- bdb/drivers/mssql.go | 362 ++++++++++++++++++ boilingcore/boilingcore.go | 9 + boilingcore/config.go | 11 + boilingcore/imports.go | 19 + main.go | 36 ++ templates/01_types.tpl | 2 + templates/02_hooks.tpl | 12 + templates/17_upsert.tpl | 2 + templates_test/hooks.tpl | 4 + templates_test/main_test/mssql_main.tpl | 161 ++++++++ templates_test/singleton/boil_main_test.tpl | 16 + .../singleton/boil_queries_test.tpl | 1 + templates_test/singleton/boil_suites_test.tpl | 3 +- templates_test/upsert.tpl | 2 + 14 files changed, 639 insertions(+), 1 deletion(-) create mode 100644 bdb/drivers/mssql.go create mode 100644 templates_test/main_test/mssql_main.tpl diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go new file mode 100644 index 0000000..4d7c0af --- /dev/null +++ b/bdb/drivers/mssql.go @@ -0,0 +1,362 @@ +package drivers + +import ( + "database/sql" + "fmt" + "net/url" + "strings" + + _ "github.com/denisenkom/go-mssqldb" + "github.com/pkg/errors" + "github.com/vattle/sqlboiler/bdb" +) + +// MSSQLDriver holds the database connection string and a handle +// to the database connection. +type MSSQLDriver struct { + connStr string + dbConn *sql.DB +} + +// NewMSSQLDriver takes the database connection details as parameters and +// returns a pointer to a MSSQLDriver object. Note that it is required to +// call MSSQLDriver.Open() and MSSQLDriver.Close() to open and close +// the database connection once an object has been obtained. +func NewMSSQLDriver(user, pass, dbname, host string, port int, sslmode string) *MSSQLDriver { + driver := MSSQLDriver{ + connStr: MSSQLBuildQueryString(user, pass, dbname, host, port, sslmode), + } + + return &driver +} + +// MSSQLBuildQueryString builds a query string for MSSQL. +func MSSQLBuildQueryString(user, pass, dbname, host string, port int, sslmode string) string { + + query := url.Values{} + query.Add("database", dbname) + query.Add("encrypt", sslmode) + + u := &url.URL{ + Scheme: "sqlserver", + User: url.UserPassword(user, pass), + Host: fmt.Sprintf("%s:%d", host, port), + // Path: instance, // if connecting to an instance instead of a port + RawQuery: query.Encode(), + } + + return u.String() +} + +// Open opens the database connection using the connection string +func (m *MSSQLDriver) Open() error { + var err error + m.dbConn, err = sql.Open("mssql", m.connStr) + if err != nil { + return err + } + + return nil +} + +// Close closes the database connection +func (m *MSSQLDriver) Close() { + m.dbConn.Close() +} + +// UseLastInsertID returns false for postgres +func (m *MSSQLDriver) UseLastInsertID() bool { + return true +} + +// TableNames connects to the postgres database and +// retrieves all table names from the information_schema where the +// table schema is public. +func (m *MSSQLDriver) TableNames(schema string, whitelist, blacklist []string) ([]string, error) { + var names []string + + query := fmt.Sprintf(`select table_name from information_schema.tables where table_schema = ? and table_type = 'BASE TABLE'`) + args := []interface{}{schema} + if len(whitelist) > 0 { + query += fmt.Sprintf(" and table_name in (%s);", strings.Repeat(",?", len(whitelist))[1:]) + for _, w := range whitelist { + args = append(args, w) + } + } else if len(blacklist) > 0 { + query += fmt.Sprintf(" and table_name not in (%s);", strings.Repeat(",?", len(blacklist))[1:]) + for _, b := range blacklist { + args = append(args, b) + } + } + + rows, err := m.dbConn.Query(query, args...) + + if err != nil { + return nil, err + } + + defer rows.Close() + for rows.Next() { + var name string + if err := rows.Scan(&name); err != nil { + return nil, err + } + names = append(names, name) + } + + return names, nil +} + +// Columns takes a table name and attempts to retrieve the table information +// from the database information_schema.columns. It retrieves the column names +// and column types and returns those as a []Column after TranslateColumnType() +// converts the SQL types to Go types, for example: "varchar" to "string" +func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { + var columns []bdb.Column + + rows, err := m.dbConn.Query(` + SELECT column_name, + CASE + WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN data_type + ELSE data_type + '(' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + ')' + END AS full_type, + data_type, + column_default, + CASE + WHEN IS_NULLABLE = 'YES' THEN 1 + ELSE 0 + END AS is_nullable, + CASE + WHEN EXISTS (SELECT c.column_name + FROM information_schema.table_constraints tc + INNER JOIN information_schema.key_column_usage kcu + ON tc.constraint_name = kcu.constraint_name + AND tc.table_name = kcu.table_name + AND tc.table_schema = kcu.table_schema + WHERE c.column_name = kcu.column_name + AND tc.table_name = c.table_name + AND (tc.constraint_type = 'PRIMARY KEY' OR tc.constraint_type = 'UNIQUE') + AND (SELECT COUNT(*) + FROM information_schema.key_column_usage + WHERE table_schema = kcu.table_schema + AND table_name = tc.table_name + AND constraint_name = tc.constraint_name) = 1) THEN 1 + ELSE 0 + END AS is_unique + FROM INFORMATION_SCHEMA.columns c + where table_name = ? and table_schema = ?; + `, tableName, schema) + + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var colName, colType, colFullType string + var nullable, unique bool + var defaultValue *string + if err := rows.Scan(&colName, &colFullType, &colType, &defaultValue, &nullable, &unique); err != nil { + return nil, errors.Wrapf(err, "unable to scan for table %s", tableName) + } + + column := bdb.Column{ + Name: colName, + FullDBType: colFullType, // example: tinyint(1) instead of tinyint + DBType: colType, + Nullable: nullable, + Unique: unique, + } + + if defaultValue != nil && *defaultValue != "NULL" { + column.Default = *defaultValue + } + + columns = append(columns, column) + } + + return columns, nil +} + +// PrimaryKeyInfo looks up the primary key for a table. +func (m *MSSQLDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryKey, error) { + pkey := &bdb.PrimaryKey{} + var err error + + query := ` + select tc.constraint_name + from information_schema.table_constraints as tc + where tc.table_name = ? and tc.constraint_type = 'PRIMARY KEY' and tc.table_schema = ?;` + + row := m.dbConn.QueryRow(query, tableName, schema) + if err = row.Scan(&pkey.Name); err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + return nil, err + } + + queryColumns := ` + select kcu.column_name + from information_schema.key_column_usage as kcu + where table_name = ? and constraint_name = ? and table_schema = ?;` + + var rows *sql.Rows + if rows, err = m.dbConn.Query(queryColumns, tableName, pkey.Name, schema); err != nil { + return nil, err + } + defer rows.Close() + + var columns []string + for rows.Next() { + var column string + + err = rows.Scan(&column) + if err != nil { + return nil, err + } + + columns = append(columns, column) + } + + if err = rows.Err(); err != nil { + return nil, err + } + + pkey.Columns = columns + + return pkey, nil +} + +// ForeignKeyInfo retrieves the foreign keys for a given table name. +func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) { + var fkeys []bdb.ForeignKey + + query := ` + SELECT + ccu.constraint_name AS SourceConstraint + ,ccu.table_name AS SourceTable + ,ccu.column_name AS SourceColumn + ,kcu.table_name AS TargetTable + ,kcu.column_name AS TargetColumn + FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu + INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc + ON ccu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu + ON kcu.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME + where ccu.table_schema = ? and ccu.constraint_schema = ? and ccu.table_name = ? + ` + + var rows *sql.Rows + var err error + if rows, err = m.dbConn.Query(query, schema, schema, tableName); err != nil { + return nil, err + } + + for rows.Next() { + var fkey bdb.ForeignKey + var sourceTable string + + fkey.Table = tableName + err = rows.Scan(&fkey.Name, &sourceTable, &fkey.Column, &fkey.ForeignTable, &fkey.ForeignColumn) + if err != nil { + return nil, err + } + + fkeys = append(fkeys, fkey) + } + + if err = rows.Err(); err != nil { + return nil, err + } + + return fkeys, nil +} + +// TranslateColumnType converts postgres database types to Go types, for example +// "varchar" to "string" and "bigint" to "int64". It returns this parsed data +// as a Column object. +func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { + if c.Nullable { + switch c.DBType { + case "tinyint": + // map tinyint(1) to bool if TinyintAsBool is true + if TinyintAsBool && c.FullDBType == "tinyint(1)" { + c.Type = "null.Bool" + } else { + c.Type = "null.Int8" + } + case "smallint": + c.Type = "null.Int16" + case "mediumint": + c.Type = "null.Int32" + case "int", "integer": + c.Type = "null.Int" + case "bigint": + c.Type = "null.Int64" + case "float": + c.Type = "null.Float32" + case "double", "double precision", "real": + c.Type = "null.Float64" + case "boolean", "bool": + c.Type = "null.Bool" + case "date", "datetime", "timestamp", "time": + c.Type = "null.Time" + case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob": + c.Type = "null.Bytes" + case "json": + c.Type = "types.JSON" + default: + c.Type = "null.String" + } + } else { + switch c.DBType { + case "tinyint": + // map tinyint(1) to bool if TinyintAsBool is true + if TinyintAsBool && c.FullDBType == "tinyint(1)" { + c.Type = "bool" + } else { + c.Type = "int8" + } + case "smallint": + c.Type = "int16" + case "mediumint": + c.Type = "int32" + case "int", "integer": + c.Type = "int" + case "bigint": + c.Type = "int64" + case "float": + c.Type = "float32" + case "double", "double precision", "real": + c.Type = "float64" + case "boolean", "bool": + c.Type = "bool" + case "date", "datetime", "timestamp", "time": + c.Type = "time.Time" + case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob": + c.Type = "[]byte" + case "json": + c.Type = "types.JSON" + default: + c.Type = "string" + } + } + + return c +} + +// RightQuote is the quoting character for the right side of the identifier +func (m *MSSQLDriver) RightQuote() byte { + return '`' +} + +// LeftQuote is the quoting character for the left side of the identifier +func (m *MSSQLDriver) LeftQuote() byte { + return '`' +} + +// IndexPlaceholders returns false to indicate MSSQL doesnt support indexed placeholders +func (m *MSSQLDriver) IndexPlaceholders() bool { + return false +} diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index cf33e0e..e6aeefb 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -305,6 +305,15 @@ func (s *State) initDriver(driverName string) error { s.Config.MySQL.Port, s.Config.MySQL.SSLMode, ) + case "mssql": + s.Driver = drivers.NewMSSQLDriver( + s.Config.MSSQL.User, + s.Config.MSSQL.Pass, + s.Config.MSSQL.DBName, + s.Config.MSSQL.Host, + s.Config.MSSQL.Port, + s.Config.MSSQL.SSLMode, + ) case "mock": s.Driver = &drivers.MockDriver{} } diff --git a/boilingcore/config.go b/boilingcore/config.go index 62da693..63b0552 100644 --- a/boilingcore/config.go +++ b/boilingcore/config.go @@ -19,6 +19,7 @@ type Config struct { Postgres PostgresConfig MySQL MySQLConfig + MSSQL MSSQLConfig } // PostgresConfig configures a postgres database @@ -40,3 +41,13 @@ type MySQLConfig struct { DBName string SSLMode string } + +// MSSQLConfig configures a mysql database +type MSSQLConfig struct { + User string + Pass string + Host string + Port int + DBName string + SSLMode string +} diff --git a/boilingcore/imports.go b/boilingcore/imports.go index 3ad8411..dba9143 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -285,6 +285,25 @@ func newImporter() importer { `_ "github.com/go-sql-driver/mysql"`, }, }, + "mssql": { + standard: importList{ + `"bytes"`, + `"database/sql"`, + `"fmt"`, + `"io"`, + `"io/ioutil"`, + `"os"`, + `"os/exec"`, + `"strings"`, + }, + thirdParty: importList{ + `"github.com/pkg/errors"`, + `"github.com/spf13/viper"`, + `"github.com/vattle/sqlboiler/bdb/drivers"`, + `"github.com/vattle/sqlboiler/randomize"`, + `_ "github.com/denisenkom/go-mssqldb"`, + }, + }, } // basedOnType imports are only included in the template output if the diff --git a/main.go b/main.go index dd2620f..3f6601e 100644 --- a/main.go +++ b/main.go @@ -255,6 +255,42 @@ func preRun(cmd *cobra.Command, args []string) error { } } + if driverName == "mssql" { + cmdConfig.MSSQL = boilingcore.MSSQLConfig{ + User: viper.GetString("mssql.user"), + Pass: viper.GetString("mssql.pass"), + Host: viper.GetString("mssql.host"), + Port: viper.GetInt("mssql.port"), + DBName: viper.GetString("mssql.dbname"), + SSLMode: viper.GetString("mssql.sslmode"), + } + + // BUG: https://github.com/spf13/viper/issues/71 + // Despite setting defaults, nested values don't get defaults + // Set them manually + if cmdConfig.MSSQL.SSLMode == "" { + cmdConfig.MSSQL.SSLMode = "true" + viper.Set("mssql.sslmode", cmdConfig.MSSQL.SSLMode) + } + + if cmdConfig.MSSQL.Port == 0 { + cmdConfig.MSSQL.Port = 3306 + viper.Set("mssql.port", cmdConfig.MSSQL.Port) + } + + err = vala.BeginValidation().Validate( + vala.StringNotEmpty(cmdConfig.MSSQL.User, "mssql.user"), + vala.StringNotEmpty(cmdConfig.MSSQL.Host, "mssql.host"), + vala.Not(vala.Equals(cmdConfig.MSSQL.Port, 0, "mssql.port")), + vala.StringNotEmpty(cmdConfig.MSSQL.DBName, "mssql.dbname"), + vala.StringNotEmpty(cmdConfig.MSSQL.SSLMode, "mssql.sslmode"), + ).Check() + + if err != nil { + return commandFailure(err.Error()) + } + } + cmdState, err = boilingcore.New(cmdConfig) return err } diff --git a/templates/01_types.tpl b/templates/01_types.tpl index edf742f..ff497df 100644 --- a/templates/01_types.tpl +++ b/templates/01_types.tpl @@ -32,8 +32,10 @@ var ( {{$varNameSingular}}InsertCache = make(map[string]insertCache) {{$varNameSingular}}UpdateCacheMut sync.RWMutex {{$varNameSingular}}UpdateCache = make(map[string]updateCache) + {{if ne .DriverName "mssql"}} {{$varNameSingular}}UpsertCacheMut sync.RWMutex {{$varNameSingular}}UpsertCache = make(map[string]insertCache) + {{end}} ) var ( diff --git a/templates/02_hooks.tpl b/templates/02_hooks.tpl index 9815639..073c8e9 100644 --- a/templates/02_hooks.tpl +++ b/templates/02_hooks.tpl @@ -4,13 +4,17 @@ var {{$varNameSingular}}BeforeInsertHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}BeforeUpdateHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}BeforeDeleteHooks []{{$tableNameSingular}}Hook +{{- if ne .DriverName "mssql" -}} var {{$varNameSingular}}BeforeUpsertHooks []{{$tableNameSingular}}Hook +{{- end}} var {{$varNameSingular}}AfterInsertHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}AfterSelectHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}AfterUpdateHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}AfterDeleteHooks []{{$tableNameSingular}}Hook +{{- if ne .DriverName "mssql" -}} var {{$varNameSingular}}AfterUpsertHooks []{{$tableNameSingular}}Hook +{{- end}} // doBeforeInsertHooks executes all "before insert" hooks. func (o *{{$tableNameSingular}}) doBeforeInsertHooks(exec boil.Executor) (err error) { @@ -45,6 +49,7 @@ func (o *{{$tableNameSingular}}) doBeforeDeleteHooks(exec boil.Executor) (err er return nil } +{{- if ne .DriverName "mssql" -}} // doBeforeUpsertHooks executes all "before Upsert" hooks. func (o *{{$tableNameSingular}}) doBeforeUpsertHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}BeforeUpsertHooks { @@ -55,6 +60,7 @@ func (o *{{$tableNameSingular}}) doBeforeUpsertHooks(exec boil.Executor) (err er return nil } +{{- end}} // doAfterInsertHooks executes all "after Insert" hooks. func (o *{{$tableNameSingular}}) doAfterInsertHooks(exec boil.Executor) (err error) { @@ -100,6 +106,7 @@ func (o *{{$tableNameSingular}}) doAfterDeleteHooks(exec boil.Executor) (err err return nil } +{{- if ne .DriverName "mssql" -}} // doAfterUpsertHooks executes all "after Upsert" hooks. func (o *{{$tableNameSingular}}) doAfterUpsertHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}AfterUpsertHooks { @@ -110,6 +117,7 @@ func (o *{{$tableNameSingular}}) doAfterUpsertHooks(exec boil.Executor) (err err return nil } +{{- end}} // Add{{$tableNameSingular}}Hook registers your hook function for all future operations. func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular}}Hook {{$tableNameSingular}}Hook) { @@ -120,8 +128,10 @@ func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular} {{$varNameSingular}}BeforeUpdateHooks = append({{$varNameSingular}}BeforeUpdateHooks, {{$varNameSingular}}Hook) case boil.BeforeDeleteHook: {{$varNameSingular}}BeforeDeleteHooks = append({{$varNameSingular}}BeforeDeleteHooks, {{$varNameSingular}}Hook) + {{if ne .DriverName "mssql" -}} case boil.BeforeUpsertHook: {{$varNameSingular}}BeforeUpsertHooks = append({{$varNameSingular}}BeforeUpsertHooks, {{$varNameSingular}}Hook) + {{- end}} case boil.AfterInsertHook: {{$varNameSingular}}AfterInsertHooks = append({{$varNameSingular}}AfterInsertHooks, {{$varNameSingular}}Hook) case boil.AfterSelectHook: @@ -130,8 +140,10 @@ func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular} {{$varNameSingular}}AfterUpdateHooks = append({{$varNameSingular}}AfterUpdateHooks, {{$varNameSingular}}Hook) case boil.AfterDeleteHook: {{$varNameSingular}}AfterDeleteHooks = append({{$varNameSingular}}AfterDeleteHooks, {{$varNameSingular}}Hook) + {{- if ne .DriverName "mssql" -}} case boil.AfterUpsertHook: {{$varNameSingular}}AfterUpsertHooks = append({{$varNameSingular}}AfterUpsertHooks, {{$varNameSingular}}Hook) + {{- end}} } } {{- end}} diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index cf3b635..5bc9d3e 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -1,3 +1,4 @@ +{{- if ne .DriverName "mssql" -}} {{- $tableNameSingular := .Table.Name | singular | titleCase -}} {{- $varNameSingular := .Table.Name | singular | camelCase -}} {{- $schemaTable := .Table.Name | .SchemaTable}} @@ -207,3 +208,4 @@ CacheNoHooks: return nil {{- end}} } +{{- end}} \ No newline at end of file diff --git a/templates_test/hooks.tpl b/templates_test/hooks.tpl index 2cdca44..9f11698 100644 --- a/templates_test/hooks.tpl +++ b/templates_test/hooks.tpl @@ -38,6 +38,7 @@ func {{$varNameSingular}}AfterDeleteHook(e boil.Executor, o *{{$tableNameSingula return nil } +{{if ne .DriverName "mssql" -}} func {{$varNameSingular}}BeforeUpsertHook(e boil.Executor, o *{{$tableNameSingular}}) error { *o = {{$tableNameSingular}}{} return nil @@ -47,6 +48,7 @@ func {{$varNameSingular}}AfterUpsertHook(e boil.Executor, o *{{$tableNameSingula *o = {{$tableNameSingular}}{} return nil } +{{- end}} func test{{$tableNamePlural}}Hooks(t *testing.T) { t.Parallel() @@ -124,6 +126,7 @@ func test{{$tableNamePlural}}Hooks(t *testing.T) { } {{$varNameSingular}}AfterDeleteHooks = []{{$tableNameSingular}}Hook{} + {{- if ne .DriverName "mssql" -}} Add{{$tableNameSingular}}Hook(boil.BeforeUpsertHook, {{$varNameSingular}}BeforeUpsertHook) if err = o.doBeforeUpsertHooks(nil); err != nil { t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err) @@ -141,5 +144,6 @@ func test{{$tableNamePlural}}Hooks(t *testing.T) { t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o) } {{$varNameSingular}}AfterUpsertHooks = []{{$tableNameSingular}}Hook{} + {{- end}} } {{- end}} diff --git a/templates_test/main_test/mssql_main.tpl b/templates_test/main_test/mssql_main.tpl new file mode 100644 index 0000000..fab1afc --- /dev/null +++ b/templates_test/main_test/mssql_main.tpl @@ -0,0 +1,161 @@ +type mssqlTester struct { + dbConn *sql.DB + dbName string + host string + user string + pass string + sslmode string + port int + optionFile string + testDBName string +} + +func init() { + dbMain = &mssqlTester{} +} + +func (m *mssqlTester) setup() error { + var err error + m.dbName = viper.GetString("mssql.dbname") + m.host = viper.GetString("mssql.host") + m.user = viper.GetString("mssql.user") + m.pass = viper.GetString("mssql.pass") + m.port = viper.GetInt("mssql.port") + m.sslmode = viper.GetString("mssql.sslmode") + // Create a randomized db name. + m.testDBName = randomize.StableDBName(m.dbName) + if err = m.makeOptionFile(); err != nil { + return errors.Wrap(err, "couldn't make option file") + } + + if err = m.dropTestDB(); err != nil { + return err + } + if err = m.createTestDB(); err != nil { + return err + } + + dumpCmd := exec.Command("mssqldump", m.defaultsFile(), "--no-data", m.dbName) + createCmd := exec.Command("mssql", m.defaultsFile(), "--database", m.testDBName) + + r, w := io.Pipe() + dumpCmd.Stdout = w + createCmd.Stdin = newFKeyDestroyer(rgxMSSQLkey, r) + + if err = dumpCmd.Start(); err != nil { + return errors.Wrap(err, "failed to start mssqldump command") + } + if err = createCmd.Start(); err != nil { + return errors.Wrap(err, "failed to start mssql command") + } + + if err = dumpCmd.Wait(); err != nil { + fmt.Println(err) + return errors.Wrap(err, "failed to wait for mssqldump command") + } + + w.Close() // After dumpCmd is done, close the write end of the pipe + + if err = createCmd.Wait(); err != nil { + fmt.Println(err) + return errors.Wrap(err, "failed to wait for mssql command") + } + + return nil +} + +func (m *mssqlTester) sslMode(mode string) string { + switch mode { + case "true": + return "true" + case "false": + return "false" + default: + return "disable" + } +} + +func (m *mssqlTester) defaultsFile() string { + return fmt.Sprintf("--defaults-file=%s", m.optionFile) +} + +func (m *mssqlTester) makeOptionFile() error { + tmp, err := ioutil.TempFile("", "optionfile") + if err != nil { + return errors.Wrap(err, "failed to create option file") + } + + fmt.Fprintln(tmp, "[client]") + fmt.Fprintf(tmp, "host=%s\n", m.host) + fmt.Fprintf(tmp, "port=%d\n", m.port) + fmt.Fprintf(tmp, "user=%s\n", m.user) + fmt.Fprintf(tmp, "password=%s\n", m.pass) + fmt.Fprintf(tmp, "ssl-mode=%s\n", m.sslMode(m.sslmode)) + + fmt.Fprintln(tmp, "[mssqldump]") + fmt.Fprintf(tmp, "host=%s\n", m.host) + fmt.Fprintf(tmp, "port=%d\n", m.port) + fmt.Fprintf(tmp, "user=%s\n", m.user) + fmt.Fprintf(tmp, "password=%s\n", m.pass) + fmt.Fprintf(tmp, "ssl-mode=%s\n", m.sslMode(m.sslmode)) + + m.optionFile = tmp.Name() + + return tmp.Close() +} + +func (m *mssqlTester) createTestDB() error { + sql := fmt.Sprintf("create database %s;", m.testDBName) + return m.runCmd(sql, "mssql") +} + +func (m *mssqlTester) dropTestDB() error { + sql := fmt.Sprintf("drop database if exists %s;", m.testDBName) + return m.runCmd(sql, "mssql") +} + +func (m *mssqlTester) teardown() error { + if m.dbConn != nil { + m.dbConn.Close() + } + + if err := m.dropTestDB(); err != nil { + return err + } + + return os.Remove(m.optionFile) +} + +func (m *mssqlTester) runCmd(stdin, command string, args ...string) error { + args = append([]string{m.defaultsFile()}, args...) + + cmd := exec.Command(command, args...) + cmd.Stdin = strings.NewReader(stdin) + + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + cmd.Stdout = stdout + cmd.Stderr = stderr + if err := cmd.Run(); err != nil { + fmt.Println("failed running:", command, args) + fmt.Println(stdout.String()) + fmt.Println(stderr.String()) + return err + } + + return nil +} + +func (m *mssqlTester) conn() (*sql.DB, error) { + if m.dbConn != nil { + return m.dbConn, nil + } + + var err error + m.dbConn, err = sql.Open("mssql", drivers.MSSQLBuildQueryString(m.user, m.pass, m.testDBName, m.host, m.port, m.sslmode)) + if err != nil { + return nil, err + } + + return m.dbConn, nil +} \ No newline at end of file diff --git a/templates_test/singleton/boil_main_test.tpl b/templates_test/singleton/boil_main_test.tpl index 0014a1e..ebb4758 100644 --- a/templates_test/singleton/boil_main_test.tpl +++ b/templates_test/singleton/boil_main_test.tpl @@ -104,6 +104,12 @@ func setConfigDefaults() { if viper.GetInt("mysql.port") == 0 { viper.Set("mysql.port", 3306) } + if viper.GetString("mssql.sslmode") == "" { + viper.Set("mssql.sslmode", "true") + } + if viper.GetInt("mssql.port") == 0 { + viper.Set("mssql.port", 1433) + } } func validateConfig(driverName string) error { @@ -127,5 +133,15 @@ func validateConfig(driverName string) error { ).Check() } + if driverName == "mssql" { + return vala.BeginValidation().Validate( + vala.StringNotEmpty(viper.GetString("mssql.user"), "mssql.user"), + vala.StringNotEmpty(viper.GetString("mssql.host"), "mssql.host"), + vala.Not(vala.Equals(viper.GetInt("mssql.port"), 0, "mssql.port")), + vala.StringNotEmpty(viper.GetString("mssql.dbname"), "mssql.dbname"), + vala.StringNotEmpty(viper.GetString("mssql.sslmode"), "mssql.sslmode"), + ).Check() + } + return errors.New("not a valid driver name") } diff --git a/templates_test/singleton/boil_queries_test.tpl b/templates_test/singleton/boil_queries_test.tpl index bd41389..f9c56b7 100644 --- a/templates_test/singleton/boil_queries_test.tpl +++ b/templates_test/singleton/boil_queries_test.tpl @@ -9,6 +9,7 @@ func MustTx(transactor boil.Transactor, err error) boil.Transactor { var rgxPGFkey = regexp.MustCompile(`(?m)^ALTER TABLE ONLY .*\n\s+ADD CONSTRAINT .*? FOREIGN KEY .*?;\n`) var rgxMySQLkey = regexp.MustCompile(`(?m)((,\n)?\s+CONSTRAINT.*?FOREIGN KEY.*?\n)+`) +var rgxMSSQLkey = regexp.MustCompile(`(?m)^ALTER TABLE .*\n\s+ADD CONSTRAINT .*? FOREIGN KEY .*?;\n`) func newFKeyDestroyer(regex *regexp.Regexp, reader io.Reader) io.Reader { return &fKeyDestroyer{ diff --git a/templates_test/singleton/boil_suites_test.tpl b/templates_test/singleton/boil_suites_test.tpl index 08a5e21..c1c3516 100644 --- a/templates_test/singleton/boil_suites_test.tpl +++ b/templates_test/singleton/boil_suites_test.tpl @@ -327,7 +327,7 @@ func TestSliceUpdateAll(t *testing.T) { {{end -}} {{- end -}} } - +{{- if ne .DriverName "mssql" -}} func TestUpsert(t *testing.T) { {{- range $index, $table := .Tables}} {{- if $table.IsJoinTable -}} @@ -337,3 +337,4 @@ func TestUpsert(t *testing.T) { {{end -}} {{- end -}} } +{{- end -}} \ No newline at end of file diff --git a/templates_test/upsert.tpl b/templates_test/upsert.tpl index f9c351e..68fc435 100644 --- a/templates_test/upsert.tpl +++ b/templates_test/upsert.tpl @@ -1,3 +1,4 @@ +{{- if ne .DriverName "mssql" -}} {{- $tableNameSingular := .Table.Name | singular | titleCase -}} {{- $tableNamePlural := .Table.Name | plural | titleCase -}} {{- $varNamePlural := .Table.Name | plural | camelCase -}} @@ -48,3 +49,4 @@ func test{{$tableNamePlural}}Upsert(t *testing.T) { t.Error("want one record, got:", count) } } +{{- end}} \ No newline at end of file From 132339026ada1d2349441d283e51d44d937355b4 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Mon, 13 Mar 2017 16:24:08 +0300 Subject: [PATCH 051/179] Regular expression for FK destroyer --- templates_test/singleton/boil_queries_test.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates_test/singleton/boil_queries_test.tpl b/templates_test/singleton/boil_queries_test.tpl index f9c56b7..45ff788 100644 --- a/templates_test/singleton/boil_queries_test.tpl +++ b/templates_test/singleton/boil_queries_test.tpl @@ -9,7 +9,7 @@ func MustTx(transactor boil.Transactor, err error) boil.Transactor { var rgxPGFkey = regexp.MustCompile(`(?m)^ALTER TABLE ONLY .*\n\s+ADD CONSTRAINT .*? FOREIGN KEY .*?;\n`) var rgxMySQLkey = regexp.MustCompile(`(?m)((,\n)?\s+CONSTRAINT.*?FOREIGN KEY.*?\n)+`) -var rgxMSSQLkey = regexp.MustCompile(`(?m)^ALTER TABLE .*\n\s+ADD CONSTRAINT .*? FOREIGN KEY .*?;\n`) +var rgxMSSQLkey = regexp.MustCompile(`(?m)^ALTER TABLE .*ADD\s+CONSTRAINT .* FOREIGN KEY.*?.*\n?REFERENCES.*`) func newFKeyDestroyer(regex *regexp.Regexp, reader io.Reader) io.Reader { return &fKeyDestroyer{ From 54af37ef9a77eb78912b5137734638383e9947f7 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Mon, 13 Mar 2017 16:25:12 +0300 Subject: [PATCH 052/179] SchemaTable with MSSQL support --- strmangle/strmangle.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strmangle/strmangle.go b/strmangle/strmangle.go index 316e1b0..4dc0b1b 100644 --- a/strmangle/strmangle.go +++ b/strmangle/strmangle.go @@ -85,7 +85,7 @@ func init() { // for Postgres: "schema_name"."table_name", versus // simply "table_name" for MySQL (because it does not support real schemas) func SchemaTable(lq, rq string, driver string, schema string, table string) string { - if driver == "postgres" && schema != "public" { + if (driver == "postgres" && schema != "public") || driver == "mssql" { return fmt.Sprintf(`%s%s%s.%s%s%s`, lq, schema, rq, lq, table, rq) } From 2d3983b992db39d691f149d45eb684da839e313f Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Mon, 13 Mar 2017 16:27:16 +0300 Subject: [PATCH 053/179] Bit and tinyint data types support --- bdb/drivers/mssql.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 4d7c0af..e918a58 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -279,13 +279,10 @@ func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { if c.Nullable { switch c.DBType { + case "bit": + c.Type = "bool" case "tinyint": - // map tinyint(1) to bool if TinyintAsBool is true - if TinyintAsBool && c.FullDBType == "tinyint(1)" { - c.Type = "null.Bool" - } else { - c.Type = "null.Int8" - } + c.Type = "int8" case "smallint": c.Type = "null.Int16" case "mediumint": @@ -311,13 +308,10 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { } } else { switch c.DBType { + case "bit": + c.Type = "bool" case "tinyint": - // map tinyint(1) to bool if TinyintAsBool is true - if TinyintAsBool && c.FullDBType == "tinyint(1)" { - c.Type = "bool" - } else { - c.Type = "int8" - } + c.Type = "int8" case "smallint": c.Type = "int16" case "mediumint": From b4c9af72e7ace67efbc40a16660a10edf8f0b658 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Mon, 13 Mar 2017 16:28:23 +0300 Subject: [PATCH 054/179] Correct quote symbols --- bdb/drivers/mssql.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index e918a58..4c17c5d 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -342,12 +342,12 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { // RightQuote is the quoting character for the right side of the identifier func (m *MSSQLDriver) RightQuote() byte { - return '`' + return '\'' } // LeftQuote is the quoting character for the left side of the identifier func (m *MSSQLDriver) LeftQuote() byte { - return '`' + return '\'' } // IndexPlaceholders returns false to indicate MSSQL doesnt support indexed placeholders From e2f32e746eac40e18f1dffda75534d4f2b34fece Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Mon, 13 Mar 2017 19:11:34 +0300 Subject: [PATCH 055/179] Main MSSQL test template --- boilingcore/imports.go | 2 - templates_test/main_test/mssql_main.tpl | 101 ++++++++---------------- 2 files changed, 31 insertions(+), 72 deletions(-) diff --git a/boilingcore/imports.go b/boilingcore/imports.go index dba9143..eafcf28 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -290,8 +290,6 @@ func newImporter() importer { `"bytes"`, `"database/sql"`, `"fmt"`, - `"io"`, - `"io/ioutil"`, `"os"`, `"os/exec"`, `"strings"`, diff --git a/templates_test/main_test/mssql_main.tpl b/templates_test/main_test/mssql_main.tpl index fab1afc..82acbfc 100644 --- a/templates_test/main_test/mssql_main.tpl +++ b/templates_test/main_test/mssql_main.tpl @@ -1,12 +1,11 @@ type mssqlTester struct { - dbConn *sql.DB - dbName string - host string - user string - pass string - sslmode string - port int - optionFile string + dbConn *sql.DB + dbName string + host string + user string + pass string + sslmode string + port int testDBName string } @@ -24,9 +23,6 @@ func (m *mssqlTester) setup() error { m.sslmode = viper.GetString("mssql.sslmode") // Create a randomized db name. m.testDBName = randomize.StableDBName(m.dbName) - if err = m.makeOptionFile(); err != nil { - return errors.Wrap(err, "couldn't make option file") - } if err = m.dropTestDB(); err != nil { return err @@ -35,30 +31,20 @@ func (m *mssqlTester) setup() error { return err } - dumpCmd := exec.Command("mssqldump", m.defaultsFile(), "--no-data", m.dbName) - createCmd := exec.Command("mssql", m.defaultsFile(), "--database", m.testDBName) + createCmd := exec.Command("sqlcmd", "-S", m.host, "-U", m.user, "-P", m.pass, "-d", m.testDBName) - r, w := io.Pipe() - dumpCmd.Stdout = w - createCmd.Stdin = newFKeyDestroyer(rgxMSSQLkey, r) + f, err := os.Open("tables.sql") + defer f.Close() + + createCmd.Stdin = newFKeyDestroyer(rgxMSSQLkey, f) - if err = dumpCmd.Start(); err != nil { - return errors.Wrap(err, "failed to start mssqldump command") - } if err = createCmd.Start(); err != nil { - return errors.Wrap(err, "failed to start mssql command") + return errors.Wrap(err, "failed to start sqlcmd command") } - if err = dumpCmd.Wait(); err != nil { - fmt.Println(err) - return errors.Wrap(err, "failed to wait for mssqldump command") - } - - w.Close() // After dumpCmd is done, close the write end of the pipe - if err = createCmd.Wait(); err != nil { fmt.Println(err) - return errors.Wrap(err, "failed to wait for mssql command") + return errors.Wrap(err, "failed to wait for sqlcmd command") } return nil @@ -75,43 +61,20 @@ func (m *mssqlTester) sslMode(mode string) string { } } -func (m *mssqlTester) defaultsFile() string { - return fmt.Sprintf("--defaults-file=%s", m.optionFile) -} - -func (m *mssqlTester) makeOptionFile() error { - tmp, err := ioutil.TempFile("", "optionfile") - if err != nil { - return errors.Wrap(err, "failed to create option file") - } - - fmt.Fprintln(tmp, "[client]") - fmt.Fprintf(tmp, "host=%s\n", m.host) - fmt.Fprintf(tmp, "port=%d\n", m.port) - fmt.Fprintf(tmp, "user=%s\n", m.user) - fmt.Fprintf(tmp, "password=%s\n", m.pass) - fmt.Fprintf(tmp, "ssl-mode=%s\n", m.sslMode(m.sslmode)) - - fmt.Fprintln(tmp, "[mssqldump]") - fmt.Fprintf(tmp, "host=%s\n", m.host) - fmt.Fprintf(tmp, "port=%d\n", m.port) - fmt.Fprintf(tmp, "user=%s\n", m.user) - fmt.Fprintf(tmp, "password=%s\n", m.pass) - fmt.Fprintf(tmp, "ssl-mode=%s\n", m.sslMode(m.sslmode)) - - m.optionFile = tmp.Name() - - return tmp.Close() -} - func (m *mssqlTester) createTestDB() error { sql := fmt.Sprintf("create database %s;", m.testDBName) - return m.runCmd(sql, "mssql") + return m.runCmd(sql, "sqlcmd", "-S", m.host, "-U", m.user, "-P", m.pass) } func (m *mssqlTester) dropTestDB() error { - sql := fmt.Sprintf("drop database if exists %s;", m.testDBName) - return m.runCmd(sql, "mssql") + // Since MS SQL 2016 it can be done with + // DROP DATABASE [ IF EXISTS ] { database_name | database_snapshot_name } [ ,...n ] [;] + sql := fmt.Sprintf(` + IF EXISTS(SELECT name FROM sys.databases + WHERE name = '%s') + DROP DATABASE %s + GO`, m.testDBName, m.testDBName) + return m.runCmd(sql, "sqlcmd", "-S", m.host, "-U", m.user, "-P", m.pass) } func (m *mssqlTester) teardown() error { @@ -123,12 +86,10 @@ func (m *mssqlTester) teardown() error { return err } - return os.Remove(m.optionFile) + return nil } func (m *mssqlTester) runCmd(stdin, command string, args ...string) error { - args = append([]string{m.defaultsFile()}, args...) - cmd := exec.Command(command, args...) cmd.Stdin = strings.NewReader(stdin) @@ -137,10 +98,10 @@ func (m *mssqlTester) runCmd(stdin, command string, args ...string) error { cmd.Stdout = stdout cmd.Stderr = stderr if err := cmd.Run(); err != nil { - fmt.Println("failed running:", command, args) - fmt.Println(stdout.String()) - fmt.Println(stderr.String()) - return err + fmt.Println("failed running:", command, args) + fmt.Println(stdout.String()) + fmt.Println(stderr.String()) + return err } return nil @@ -148,14 +109,14 @@ func (m *mssqlTester) runCmd(stdin, command string, args ...string) error { func (m *mssqlTester) conn() (*sql.DB, error) { if m.dbConn != nil { - return m.dbConn, nil + return m.dbConn, nil } var err error m.dbConn, err = sql.Open("mssql", drivers.MSSQLBuildQueryString(m.user, m.pass, m.testDBName, m.host, m.port, m.sslmode)) if err != nil { - return nil, err + return nil, err } return m.dbConn, nil -} \ No newline at end of file +} From cbdbe4891a53b0ba9bdb955e51eba6890dadd8d8 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 11:13:56 +0300 Subject: [PATCH 056/179] MSSQL rows limit/offset via TOP or OFFSET ... FETCH NEXT ... ROWS --- bdb/drivers/mock.go | 3 +++ bdb/drivers/mssql.go | 7 ++++++- bdb/drivers/mysql.go | 5 +++++ bdb/drivers/postgres.go | 5 +++++ bdb/interface.go | 4 ++++ bdb/interface_test.go | 1 + boilingcore/boilingcore.go | 1 + queries/query.go | 3 +++ queries/query_builders.go | 31 ++++++++++++++++++++++++++----- 9 files changed, 54 insertions(+), 6 deletions(-) diff --git a/bdb/drivers/mock.go b/bdb/drivers/mock.go index abe85a4..1c91d0e 100644 --- a/bdb/drivers/mock.go +++ b/bdb/drivers/mock.go @@ -118,6 +118,9 @@ func (m *MockDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryKey, // UseLastInsertID returns a database mock LastInsertID compatibility flag func (m *MockDriver) UseLastInsertID() bool { return false } +// UseTopClause returns a database mock SQL TOP clause compatibility flag +func (m *MockDriver) UseTopClause() bool { return false } + // Open mimics a database open call and returns nil for no error func (m *MockDriver) Open() error { return nil } diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 4c17c5d..4e99e8c 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -64,8 +64,13 @@ func (m *MSSQLDriver) Close() { m.dbConn.Close() } -// UseLastInsertID returns false for postgres +// UseLastInsertID returns false for mssql func (m *MSSQLDriver) UseLastInsertID() bool { + return false +} + +// UseTopClause returns true to indicate MS SQL supports SQL TOP clause +func (m *MSSQLDriver) UseTopClause() bool { return true } diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index 1d80634..ea24f1b 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -81,6 +81,11 @@ func (m *MySQLDriver) UseLastInsertID() bool { return true } +// UseTopClause returns false to indicate MySQL doesnt support SQL TOP clause +func (m *MySQLDriver) UseTopClause() bool { + return false +} + // TableNames connects to the postgres database and // retrieves all table names from the information_schema where the // table schema is public. diff --git a/bdb/drivers/postgres.go b/bdb/drivers/postgres.go index 5ae0283..ec1aede 100644 --- a/bdb/drivers/postgres.go +++ b/bdb/drivers/postgres.go @@ -78,6 +78,11 @@ func (p *PostgresDriver) UseLastInsertID() bool { return false } +// UseTopClause returns false to indicate PSQL doesnt support SQL TOP clause +func (m *PostgresDriver) UseTopClause() bool { + return false +} + // TableNames connects to the postgres database and // retrieves all table names from the information_schema where the // table schema is schema. It uses a whitelist and blacklist. diff --git a/bdb/interface.go b/bdb/interface.go index 2d90a1d..dfb33bc 100644 --- a/bdb/interface.go +++ b/bdb/interface.go @@ -18,6 +18,10 @@ type Interface interface { // the sql.Exec result's LastInsertId UseLastInsertID() bool + // UseTopClause should return true if the Database is capable of using + // the SQL TOP clause + UseTopClause() bool + // Open the database connection Open() error // Close the database connection diff --git a/bdb/interface_test.go b/bdb/interface_test.go index effeecf..4d429c6 100644 --- a/bdb/interface_test.go +++ b/bdb/interface_test.go @@ -10,6 +10,7 @@ type testMockDriver struct{} func (m testMockDriver) TranslateColumnType(c Column) Column { return c } func (m testMockDriver) UseLastInsertID() bool { return false } +func (m testMockDriver) UseTopClause() bool { return false } func (m testMockDriver) Open() error { return nil } func (m testMockDriver) Close() {} diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index e6aeefb..5e00687 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -325,6 +325,7 @@ func (s *State) initDriver(driverName string) error { s.Dialect.LQ = s.Driver.LeftQuote() s.Dialect.RQ = s.Driver.RightQuote() s.Dialect.IndexPlaceholders = s.Driver.IndexPlaceholders() + s.Dialect.UseTopClause = s.Driver.UseTopClause() return nil } diff --git a/queries/query.go b/queries/query.go index 5c2f3c9..8c2abdb 100644 --- a/queries/query.go +++ b/queries/query.go @@ -52,6 +52,9 @@ type Dialect struct { // Bool flag indicating whether indexed // placeholders ($1) are used, or ? placeholders. IndexPlaceholders bool + // Bool flag indicating whether "TOP" or "LIMIT" clause + // must be used for rows limitation + UseTopClause bool } type where struct { diff --git a/queries/query_builders.go b/queries/query_builders.go index 683c6b5..1d22440 100644 --- a/queries/query_builders.go +++ b/queries/query_builders.go @@ -46,6 +46,12 @@ func buildSelectQuery(q *Query) (*bytes.Buffer, []interface{}) { buf.WriteString("SELECT ") + if q.dialect.UseTopClause { + if q.limit != 0 && q.offset == 0 { + fmt.Fprintf(buf, " TOP (%d)", q.limit) + } + } + if q.count { buf.WriteString("COUNT(") } @@ -308,11 +314,26 @@ func writeModifiers(q *Query, buf *bytes.Buffer, args *[]interface{}) { buf.WriteString(strings.Join(q.orderBy, ", ")) } - if q.limit != 0 { - fmt.Fprintf(buf, " LIMIT %d", q.limit) - } - if q.offset != 0 { - fmt.Fprintf(buf, " OFFSET %d", q.offset) + if !q.dialect.UseTopClause { + if q.limit != 0 { + fmt.Fprintf(buf, " LIMIT %d", q.limit) + } + + if q.offset != 0 { + fmt.Fprintf(buf, " OFFSET %d", q.offset) + } + } else { + // From MS SQL 2012 and above: https://technet.microsoft.com/en-us/library/ms188385(v=sql.110).aspx + // ORDER BY ... + // OFFSET N ROWS + // FETCH NEXT M ROWS ONLY + if q.offset != 0 { + fmt.Fprintf(buf, " OFFSET %d", q.offset) + + if q.limit != 0 { + fmt.Fprintf(buf, " FETCH NEXT %d ROWS ONLY", q.limit) + } + } } if len(q.forlock) != 0 { From b3c4580efafc99bfb25f6e9f51553db33d9a11a5 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 12:45:50 +0300 Subject: [PATCH 057/179] Fixed comment for SchemaTable --- strmangle/strmangle.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/strmangle/strmangle.go b/strmangle/strmangle.go index 4dc0b1b..7881275 100644 --- a/strmangle/strmangle.go +++ b/strmangle/strmangle.go @@ -82,7 +82,8 @@ func init() { // SchemaTable returns a table name with a schema prefixed if // using a database that supports real schemas, for example, -// for Postgres: "schema_name"."table_name", versus +// for Postgres: "schema_name"."table_name", +// for MS SQL: [schema_name].[table_name], versus // simply "table_name" for MySQL (because it does not support real schemas) func SchemaTable(lq, rq string, driver string, schema string, table string) string { if (driver == "postgres" && schema != "public") || driver == "mssql" { From 454a3a816a1a7a9e478cdd305084b97b73aaea01 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 13:46:35 +0300 Subject: [PATCH 058/179] Fixed quote symbols --- bdb/drivers/mssql.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 4e99e8c..271b337 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -347,15 +347,15 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { // RightQuote is the quoting character for the right side of the identifier func (m *MSSQLDriver) RightQuote() byte { - return '\'' + return '"' } // LeftQuote is the quoting character for the left side of the identifier func (m *MSSQLDriver) LeftQuote() byte { - return '\'' + return '"' } -// IndexPlaceholders returns false to indicate MSSQL doesnt support indexed placeholders +// IndexPlaceholders returns false to indicate MS SQL doesnt support indexed placeholders func (m *MSSQLDriver) IndexPlaceholders() bool { return false } From 83d90f3f28e63fd172fe8cb0ced83b6cc2341731 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 13:48:38 +0300 Subject: [PATCH 059/179] Fixed quote symbols order --- templates/15_insert.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 5fea2c3..4833bef 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -66,7 +66,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string return err } if len(wl) != 0 { - cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) VALUES (%s)", strings.Join(wl, "{{.LQ}},{{.RQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) + cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) VALUES (%s)", strings.Join(wl, "{{.RQ}},{{.LQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) } else { {{if eq .DriverName "mysql" -}} cache.query = "INSERT INTO {{$schemaTable}} () VALUES ()" @@ -77,9 +77,9 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string if len(cache.retMapping) != 0 { {{if .UseLastInsertID -}} - cache.retQuery = fmt.Sprintf("SELECT {{.LQ}}%s{{.RQ}} FROM {{$schemaTable}} WHERE %s", strings.Join(returnColumns, "{{.LQ}},{{.RQ}}"), strmangle.WhereClause("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns)) + cache.retQuery = fmt.Sprintf("SELECT {{.LQ}}%s{{.RQ}} FROM {{$schemaTable}} WHERE %s", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}"), strmangle.WhereClause("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns)) {{else -}} - cache.query += fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.LQ}},{{.RQ}}")) + cache.query += fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) {{end -}} } } From ed6dad7eedd3e3b84f88a5291ce76c774f9821fd Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 13:53:35 +0300 Subject: [PATCH 060/179] MS SQL compatible query for checks if the row exists --- templates/20_exists.tpl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/20_exists.tpl b/templates/20_exists.tpl index 3a9c693..22a67a0 100644 --- a/templates/20_exists.tpl +++ b/templates/20_exists.tpl @@ -6,8 +6,11 @@ // {{$tableNameSingular}}Exists checks if the {{$tableNameSingular}} row exists. func {{$tableNameSingular}}Exists(exec boil.Executor, {{$pkArgs}}) (bool, error) { var exists bool - + {{if eq .DriverName "mssql" -}} + sql := "select case when exists(select top(1) 1 from {{$schemaTable}} where {{if .Dialect.IndexPlaceholders}}{{whereClause .LQ .RQ 1 .Table.PKey.Columns}}{{else}}{{whereClause .LQ .RQ 0 .Table.PKey.Columns}}{{end}}) then 1 else 0 end" + {{- else -}} sql := "select exists(select 1 from {{$schemaTable}} where {{if .Dialect.IndexPlaceholders}}{{whereClause .LQ .RQ 1 .Table.PKey.Columns}}{{else}}{{whereClause .LQ .RQ 0 .Table.PKey.Columns}}{{end}} limit 1)" + {{- end}} if boil.DebugMode { fmt.Fprintln(boil.DebugWriter, sql) From aa01f21b5c5f2e4e34df2238488564f28206f499 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 13:59:05 +0300 Subject: [PATCH 061/179] Pass UseTopClause flag into the Dialect object --- templates/singleton/boil_queries.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index d0879cb..3883e8e 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -2,6 +2,7 @@ var dialect = queries.Dialect{ LQ: 0x{{printf "%x" .Dialect.LQ}}, RQ: 0x{{printf "%x" .Dialect.RQ}}, IndexPlaceholders: {{.Dialect.IndexPlaceholders}}, + UseTopClause: {{.Dialect.UseTopClause}}, } // NewQueryG initializes a new Query using the passed in QueryMods From e3254c185114c45c627e6f0f22ab552db04d2884 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 14:01:53 +0300 Subject: [PATCH 062/179] Allow MS SQL driver to use indexed placeholders --- bdb/drivers/mssql.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 271b337..fce6227 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -355,7 +355,7 @@ func (m *MSSQLDriver) LeftQuote() byte { return '"' } -// IndexPlaceholders returns false to indicate MS SQL doesnt support indexed placeholders +// IndexPlaceholders returns true to indicate MS SQL supports indexed placeholders func (m *MSSQLDriver) IndexPlaceholders() bool { - return false + return true } From b78331124809a3aa900da1116f67452182ccaa9f Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 14:24:03 +0300 Subject: [PATCH 063/179] Hack for MS SQL limit/offset query without ORDER BY clause --- queries/query_builders.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/queries/query_builders.go b/queries/query_builders.go index 1d22440..6d798b3 100644 --- a/queries/query_builders.go +++ b/queries/query_builders.go @@ -328,6 +328,16 @@ func writeModifiers(q *Query, buf *bytes.Buffer, args *[]interface{}) { // OFFSET N ROWS // FETCH NEXT M ROWS ONLY if q.offset != 0 { + + // Hack from https://www.microsoftpressstore.com/articles/article.aspx?p=2314819 + // ... + // As mentioned, the OFFSET-FETCH filter requires an ORDER BY clause. If you want to use arbitrary order, + // like TOP without an ORDER BY clause, you can use the trick with ORDER BY (SELECT NULL) + // ... + if len(q.orderBy) == 0 { + buf.WriteString(" ORDER BY (SELECT NULL)") + } + fmt.Fprintf(buf, " OFFSET %d", q.offset) if q.limit != 0 { From 4f02886c2b875d3e0d9869e0b77d59be2648591d Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 14:31:52 +0300 Subject: [PATCH 064/179] Added space in SELECT TOP clause --- queries/query_builders.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queries/query_builders.go b/queries/query_builders.go index 6d798b3..3136693 100644 --- a/queries/query_builders.go +++ b/queries/query_builders.go @@ -48,7 +48,7 @@ func buildSelectQuery(q *Query) (*bytes.Buffer, []interface{}) { if q.dialect.UseTopClause { if q.limit != 0 && q.offset == 0 { - fmt.Fprintf(buf, " TOP (%d)", q.limit) + fmt.Fprintf(buf, " TOP (%d) ", q.limit) } } From 0bdbee86f1eb0a0a493d3de75570cfb0b4bb2c43 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 14:58:32 +0300 Subject: [PATCH 065/179] Queries refactoring --- bdb/drivers/mssql.go | 54 ++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index fce6227..afdff66 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -80,15 +80,19 @@ func (m *MSSQLDriver) UseTopClause() bool { func (m *MSSQLDriver) TableNames(schema string, whitelist, blacklist []string) ([]string, error) { var names []string - query := fmt.Sprintf(`select table_name from information_schema.tables where table_schema = ? and table_type = 'BASE TABLE'`) + query := ` + SELECT table_name + FROM information_schema.tables + WHERE table_schema = ? AND table_type = 'BASE TABLE'` + args := []interface{}{schema} if len(whitelist) > 0 { - query += fmt.Sprintf(" and table_name in (%s);", strings.Repeat(",?", len(whitelist))[1:]) + query += fmt.Sprintf(" AND table_name IN (%s);", strings.Repeat(",?", len(whitelist))[1:]) for _, w := range whitelist { args = append(args, w) } } else if len(blacklist) > 0 { - query += fmt.Sprintf(" and table_name not in (%s);", strings.Repeat(",?", len(blacklist))[1:]) + query += fmt.Sprintf(" AND table_name not IN (%s);", strings.Repeat(",?", len(blacklist))[1:]) for _, b := range blacklist { args = append(args, b) } @@ -122,13 +126,13 @@ func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { rows, err := m.dbConn.Query(` SELECT column_name, CASE - WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN data_type - ELSE data_type + '(' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + ')' + WHEN character_maximum_length IS NULL THEN data_type + ELSE data_type + '(' + CAST(character_maximum_length AS VARCHAR) + ')' END AS full_type, data_type, column_default, CASE - WHEN IS_NULLABLE = 'YES' THEN 1 + WHEN is_nullable = 'YES' THEN 1 ELSE 0 END AS is_nullable, CASE @@ -148,8 +152,8 @@ func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { AND constraint_name = tc.constraint_name) = 1) THEN 1 ELSE 0 END AS is_unique - FROM INFORMATION_SCHEMA.columns c - where table_name = ? and table_schema = ?; + FROM information_schema.columns c + WHERE table_name = ? AND table_schema = ?; `, tableName, schema) if err != nil { @@ -189,9 +193,9 @@ func (m *MSSQLDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryKey, var err error query := ` - select tc.constraint_name - from information_schema.table_constraints as tc - where tc.table_name = ? and tc.constraint_type = 'PRIMARY KEY' and tc.table_schema = ?;` + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = ? AND constraint_type = 'PRIMARY KEY' AND table_schema = ?;` row := m.dbConn.QueryRow(query, tableName, schema) if err = row.Scan(&pkey.Name); err != nil { @@ -202,9 +206,9 @@ func (m *MSSQLDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryKey, } queryColumns := ` - select kcu.column_name - from information_schema.key_column_usage as kcu - where table_name = ? and constraint_name = ? and table_schema = ?;` + SELECT column_name + FROM information_schema.key_column_usage + WHERE table_name = ? AND constraint_name = ? AND table_schema = ?;` var rows *sql.Rows if rows, err = m.dbConn.Query(queryColumns, tableName, pkey.Name, schema); err != nil { @@ -239,17 +243,17 @@ func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey query := ` SELECT - ccu.constraint_name AS SourceConstraint - ,ccu.table_name AS SourceTable - ,ccu.column_name AS SourceColumn - ,kcu.table_name AS TargetTable - ,kcu.column_name AS TargetColumn - FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu - INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc - ON ccu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME - INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu - ON kcu.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME - where ccu.table_schema = ? and ccu.constraint_schema = ? and ccu.table_name = ? + ccu.constraint_name + ,ccu.table_name AS local_table + ,ccu.column_name AS local_column + ,kcu.table_name AS foreign_table + ,kcu.column_name AS foreign_column + FROM information_schema.constraint_column_usage ccu + INNER JOIN information_schema.referential_constraints rc + ON ccu.constraint_name = rc.constraint_name + INNER JOIN information_schema.key_column_usage kcu + ON kcu.constraint_name = rc.unique_constraint_name + WHERE ccu.table_schema = ? AND ccu.constraint_schema = ? AND ccu.table_name = ? ` var rows *sql.Rows From e2e619dfb2af0773ef721a2f54e16808cda80656 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 16:51:56 +0300 Subject: [PATCH 066/179] Check for error on tables schema file opening --- templates_test/main_test/mssql_main.tpl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/templates_test/main_test/mssql_main.tpl b/templates_test/main_test/mssql_main.tpl index 82acbfc..a164819 100644 --- a/templates_test/main_test/mssql_main.tpl +++ b/templates_test/main_test/mssql_main.tpl @@ -33,7 +33,12 @@ func (m *mssqlTester) setup() error { createCmd := exec.Command("sqlcmd", "-S", m.host, "-U", m.user, "-P", m.pass, "-d", m.testDBName) - f, err := os.Open("tables.sql") + f, err := os.Open("tables_schema.sql") + + if err != nil { + return errors.Wrap(err, "failed to open tables_schema.sql file") + } + defer f.Close() createCmd.Stdin = newFKeyDestroyer(rgxMSSQLkey, f) From a48e5f7f82bb51bd6eea068ff1821b1b04a6ab85 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 16:52:54 +0300 Subject: [PATCH 067/179] MS SQL compatible query for RETURNING clause --- templates/15_insert.tpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 4833bef..4906875 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -79,7 +79,11 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string {{if .UseLastInsertID -}} cache.retQuery = fmt.Sprintf("SELECT {{.LQ}}%s{{.RQ}} FROM {{$schemaTable}} WHERE %s", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}"), strmangle.WhereClause("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns)) {{else -}} + {{if ne .DriverName "mssql" -}} cache.query += fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) + {{- else -}} + cache.query += fmt.Sprintf(" OUTPUT {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) + {{- end}} {{end -}} } } From 813fd43e035a593e3a01dde9077218ae6307b906 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 17:41:45 +0300 Subject: [PATCH 068/179] Ignore columns with defaults on Insert --- templates_test/delete.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates_test/delete.tpl b/templates_test/delete.tpl index f745ea4..d548efa 100644 --- a/templates_test/delete.tpl +++ b/templates_test/delete.tpl @@ -8,7 +8,7 @@ func test{{$tableNamePlural}}Delete(t *testing.T) { seed := randomize.NewSeed() var err error {{$varNameSingular}} := &{{$tableNameSingular}}{} - if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true); err != nil { + if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true, {{$varNameSingular}}ColumnsWithDefault...); err != nil { t.Errorf("Unable to randomize {{$tableNameSingular}} struct: %s", err) } @@ -38,7 +38,7 @@ func test{{$tableNamePlural}}QueryDeleteAll(t *testing.T) { seed := randomize.NewSeed() var err error {{$varNameSingular}} := &{{$tableNameSingular}}{} - if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true); err != nil { + if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true, {{$varNameSingular}}ColumnsWithDefault...); err != nil { t.Errorf("Unable to randomize {{$tableNameSingular}} struct: %s", err) } @@ -68,7 +68,7 @@ func test{{$tableNamePlural}}SliceDeleteAll(t *testing.T) { seed := randomize.NewSeed() var err error {{$varNameSingular}} := &{{$tableNameSingular}}{} - if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true); err != nil { + if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true, {{$varNameSingular}}ColumnsWithDefault...); err != nil { t.Errorf("Unable to randomize {{$tableNameSingular}} struct: %s", err) } From 5d725696357fb3752dde791677cb2d405d0ce282 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 18:06:12 +0300 Subject: [PATCH 069/179] Fixed Insert and Select for timestamp, rowversion --- bdb/drivers/mssql.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index afdff66..9b14e38 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -177,10 +177,13 @@ func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { Unique: unique, } - if defaultValue != nil && *defaultValue != "NULL" { + // Hack to exclude columns with types timestamp and rowversion from inserts + // Values for this columns provides by SQL Server on Insert clause + if (strings.EqualFold(colType, "timestamp") || strings.EqualFold(colType, "rowversion")) && defaultValue == nil { + column.Default = colType + } else if defaultValue != nil && *defaultValue != "NULL" { column.Default = *defaultValue } - columns = append(columns, column) } @@ -306,12 +309,12 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "null.Float64" case "boolean", "bool": c.Type = "null.Bool" - case "date", "datetime", "timestamp", "time": + case "date", "datetime", "time": c.Type = "null.Time" case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob": c.Type = "null.Bytes" - case "json": - c.Type = "types.JSON" + case "timestamp", "rowversion": + c.Type = "null.Bytes" default: c.Type = "null.String" } @@ -335,12 +338,12 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "float64" case "boolean", "bool": c.Type = "bool" - case "date", "datetime", "timestamp", "time": + case "date", "datetime", "time": c.Type = "time.Time" case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob": c.Type = "[]byte" - case "json": - c.Type = "types.JSON" + case "timestamp", "rowversion": + c.Type = "[]byte" default: c.Type = "string" } From 51e9961f0a7918a296277a17c2a6f55d8ac1e590 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 18:06:33 +0300 Subject: [PATCH 070/179] Fixed OUTPUT values on Insert --- templates/15_insert.tpl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 4906875..53c2677 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -66,7 +66,15 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string return err } if len(wl) != 0 { + {{if ne .DriverName "mssql" -}} cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) VALUES (%s)", strings.Join(wl, "{{.RQ}},{{.LQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) + {{- else -}} + if len(cache.retMapping) == 0 { + cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) VALUES (%s)", strings.Join(wl, "{{.RQ}},{{.LQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) + } else { + cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) OUTPUT INSERTED.{{.LQ}}%s{{.RQ}} VALUES (%s)", strings.Join(wl, "{{.RQ}},{{.LQ}}"), strings.Join(returnColumns, "{{.RQ}},INSERTED.{{.LQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) + } + {{- end}} } else { {{if eq .DriverName "mysql" -}} cache.query = "INSERT INTO {{$schemaTable}} () VALUES ()" @@ -75,17 +83,15 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string {{end -}} } + {{if ne .DriverName "mssql" -}} if len(cache.retMapping) != 0 { {{if .UseLastInsertID -}} cache.retQuery = fmt.Sprintf("SELECT {{.LQ}}%s{{.RQ}} FROM {{$schemaTable}} WHERE %s", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}"), strmangle.WhereClause("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns)) - {{else -}} - {{if ne .DriverName "mssql" -}} + {{else -}} cache.query += fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) - {{- else -}} - cache.query += fmt.Sprintf(" OUTPUT {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) - {{- end}} - {{end -}} + {{- end}} } + {{end -}} } value := reflect.Indirect(reflect.ValueOf(o)) From 6aadf439c9d5dc1ffda6e1619827d8fef6714e56 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 18:45:41 +0300 Subject: [PATCH 071/179] Fixed column types mapping --- bdb/drivers/mssql.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 9b14e38..a36a3eb 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -305,13 +305,13 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "null.Int64" case "float": c.Type = "null.Float32" - case "double", "double precision", "real": + case "real": c.Type = "null.Float64" case "boolean", "bool": c.Type = "null.Bool" - case "date", "datetime", "time": + case "date", "datetime", "datetime2", "smalldatetime", "time": c.Type = "null.Time" - case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob": + case "binary", "varbinary": c.Type = "null.Bytes" case "timestamp", "rowversion": c.Type = "null.Bytes" @@ -334,13 +334,13 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "int64" case "float": c.Type = "float32" - case "double", "double precision", "real": + case "real": c.Type = "float64" case "boolean", "bool": c.Type = "bool" - case "date", "datetime", "time": + case "date", "datetime", "datetime2", "smalldatetime", "time": c.Type = "time.Time" - case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob": + case "binary", "varbinary": c.Type = "[]byte" case "timestamp", "rowversion": c.Type = "[]byte" From 85fc6e546a009e8ef8cf420a8918ea732970a6cb Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 14 Mar 2017 18:49:40 +0300 Subject: [PATCH 072/179] Fixed float and real mapping --- bdb/drivers/mssql.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index a36a3eb..502ff51 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -303,9 +303,9 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "null.Int" case "bigint": c.Type = "null.Int64" - case "float": - c.Type = "null.Float32" case "real": + c.Type = "null.Float32" + case "float": c.Type = "null.Float64" case "boolean", "bool": c.Type = "null.Bool" @@ -332,9 +332,9 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "int" case "bigint": c.Type = "int64" - case "float": - c.Type = "float32" case "real": + c.Type = "float32" + case "float": c.Type = "float64" case "boolean", "bool": c.Type = "bool" From 705befef070ac934034e984166dd7a71fd4d281d Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Wed, 15 Mar 2017 14:12:21 +0300 Subject: [PATCH 073/179] Fixed types mapping --- bdb/drivers/mssql.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 502ff51..920ce09 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -291,15 +291,13 @@ func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { if c.Nullable { switch c.DBType { - case "bit": - c.Type = "bool" case "tinyint": - c.Type = "int8" + c.Type = "null.Int8" case "smallint": c.Type = "null.Int16" case "mediumint": c.Type = "null.Int32" - case "int", "integer": + case "int": c.Type = "null.Int" case "bigint": c.Type = "null.Int64" @@ -307,7 +305,7 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "null.Float32" case "float": c.Type = "null.Float64" - case "boolean", "bool": + case "boolean", "bool", "bit": c.Type = "null.Bool" case "date", "datetime", "datetime2", "smalldatetime", "time": c.Type = "null.Time" @@ -320,15 +318,13 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { } } else { switch c.DBType { - case "bit": - c.Type = "bool" case "tinyint": c.Type = "int8" case "smallint": c.Type = "int16" case "mediumint": c.Type = "int32" - case "int", "integer": + case "int": c.Type = "int" case "bigint": c.Type = "int64" @@ -336,7 +332,7 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "float32" case "float": c.Type = "float64" - case "boolean", "bool": + case "boolean", "bool", "bit": c.Type = "bool" case "date", "datetime", "datetime2", "smalldatetime", "time": c.Type = "time.Time" From 6fad1bd148b9231c075ba7249b3ce77a05b2715c Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Wed, 15 Mar 2017 15:31:09 +0300 Subject: [PATCH 074/179] Added column flag for autogenerated values like IDENTITY | TIMESTAMP | ROWVERSION --- bdb/column.go | 7 ++++++- bdb/drivers/mssql.go | 30 +++++++++++++++--------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/bdb/column.go b/bdb/column.go index 5ed7a8b..9ce4bc4 100644 --- a/bdb/column.go +++ b/bdb/column.go @@ -29,6 +29,11 @@ type Column struct { // tinyint(1) instead of tinyint // Used for "tinyint-as-bool" flag FullDBType string + + // MS SQL only bits + // Used to indicate that the value + // for this column is auto generated by database on insert (i.e. - timestamp (old) or rowversion (new)) + AutoGenerated bool } // ColumnNames of the columns. @@ -57,7 +62,7 @@ func FilterColumnsByDefault(defaults bool, columns []Column) []Column { var cols []Column for _, c := range columns { - if (defaults && len(c.Default) != 0) || (!defaults && len(c.Default) == 0) { + if (defaults && (len(c.Default) != 0 || c.AutoGenerated)) || (!defaults && len(c.Default) == 0 && !c.AutoGenerated) { cols = append(cols, c) } } diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 920ce09..254f68d 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -151,10 +151,11 @@ func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { AND table_name = tc.table_name AND constraint_name = tc.constraint_name) = 1) THEN 1 ELSE 0 - END AS is_unique + END AS is_unique, + COLUMNPROPERTY(object_id($1 + '.' + $2), c.column_name, 'IsIdentity') as is_identity FROM information_schema.columns c - WHERE table_name = ? AND table_schema = ?; - `, tableName, schema) + WHERE table_schema = $1 AND table_name = $2; + `, schema, tableName) if err != nil { return nil, err @@ -163,25 +164,24 @@ func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { for rows.Next() { var colName, colType, colFullType string - var nullable, unique bool + var nullable, unique, identity, auto bool var defaultValue *string - if err := rows.Scan(&colName, &colFullType, &colType, &defaultValue, &nullable, &unique); err != nil { + if err := rows.Scan(&colName, &colFullType, &colType, &defaultValue, &nullable, &unique, &identity); err != nil { return nil, errors.Wrapf(err, "unable to scan for table %s", tableName) } + auto = identity || strings.EqualFold(colType, "timestamp") || strings.EqualFold(colType, "rowversion") + column := bdb.Column{ - Name: colName, - FullDBType: colFullType, // example: tinyint(1) instead of tinyint - DBType: colType, - Nullable: nullable, - Unique: unique, + Name: colName, + FullDBType: colFullType, + DBType: colType, + Nullable: nullable, + Unique: unique, + AutoGenerated: auto, } - // Hack to exclude columns with types timestamp and rowversion from inserts - // Values for this columns provides by SQL Server on Insert clause - if (strings.EqualFold(colType, "timestamp") || strings.EqualFold(colType, "rowversion")) && defaultValue == nil { - column.Default = colType - } else if defaultValue != nil && *defaultValue != "NULL" { + if defaultValue != nil && *defaultValue != "NULL" { column.Default = *defaultValue } columns = append(columns, column) From 54f960659b66215ed88ebda07aef50bb1cbe6c9b Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Wed, 15 Mar 2017 15:37:04 +0300 Subject: [PATCH 075/179] Ignore columns with defaults in randomize --- templates_test/update.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates_test/update.tpl b/templates_test/update.tpl index b33854d..2612a87 100644 --- a/templates_test/update.tpl +++ b/templates_test/update.tpl @@ -12,7 +12,7 @@ func test{{$tableNamePlural}}Update(t *testing.T) { seed := randomize.NewSeed() var err error {{$varNameSingular}} := &{{$tableNameSingular}}{} - if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true); err != nil { + if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true, {{$varNameSingular}}ColumnsWithDefault...); err != nil { t.Errorf("Unable to randomize {{$tableNameSingular}} struct: %s", err) } @@ -50,7 +50,7 @@ func test{{$tableNamePlural}}SliceUpdateAll(t *testing.T) { seed := randomize.NewSeed() var err error {{$varNameSingular}} := &{{$tableNameSingular}}{} - if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true); err != nil { + if err = randomize.Struct(seed, {{$varNameSingular}}, {{$varNameSingular}}DBTypes, true, {{$varNameSingular}}ColumnsWithDefault...); err != nil { t.Errorf("Unable to randomize {{$tableNameSingular}} struct: %s", err) } From bfab02bde166b8c972f5a066ea1ace8988d821f7 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Wed, 15 Mar 2017 18:02:56 +0300 Subject: [PATCH 076/179] WhereClauseRepeated func for repeated Where clause --- strmangle/strmangle.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/strmangle/strmangle.go b/strmangle/strmangle.go index 7881275..b5b68cb 100644 --- a/strmangle/strmangle.go +++ b/strmangle/strmangle.go @@ -521,6 +521,37 @@ func WhereClause(lq, rq string, start int, cols []string) string { return buf.String() } +// WhereClauseRepeated returns the where clause repeated with OR clause using start as the $ flag index +// For example, if start was 2 output would be: "(colthing=$2 AND colstuff=$3) OR (colthing=$4 AND colstuff=$5)" +func WhereClauseRepeated(lq, rq string, start int, cols []string, count int) string { + + var startIndex int + + buf := GetBuffer() + defer PutBuffer(buf) + + buf.WriteByte('(') + + for i := 0; i < count; i++ { + + if i != 0 { + buf.WriteString(") OR (") + } + + if start > 0 { + startIndex = start + i*len(cols) + } else { + startIndex = 0 + } + + buf.WriteString(WhereClause(lq, rq, startIndex, cols)) + } + + buf.WriteByte(')') + + return buf.String() +} + // JoinSlices merges two string slices of equal length func JoinSlices(sep string, a, b []string) []string { lna, lnb := len(a), len(b) From ad15668462c060cdfb7e10ae2fc1dc5d3a678eda Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Wed, 15 Mar 2017 19:04:13 +0300 Subject: [PATCH 077/179] Fixed code generation --- templates/02_hooks.tpl | 6 +++--- templates/18_delete.tpl | 11 +++++++++++ templates_test/hooks.tpl | 2 +- templates_test/singleton/boil_suites_test.tpl | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/templates/02_hooks.tpl b/templates/02_hooks.tpl index 073c8e9..073f8ec 100644 --- a/templates/02_hooks.tpl +++ b/templates/02_hooks.tpl @@ -4,7 +4,7 @@ var {{$varNameSingular}}BeforeInsertHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}BeforeUpdateHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}BeforeDeleteHooks []{{$tableNameSingular}}Hook -{{- if ne .DriverName "mssql" -}} +{{if ne .DriverName "mssql" -}} var {{$varNameSingular}}BeforeUpsertHooks []{{$tableNameSingular}}Hook {{- end}} @@ -12,7 +12,7 @@ var {{$varNameSingular}}AfterInsertHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}AfterSelectHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}AfterUpdateHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}AfterDeleteHooks []{{$tableNameSingular}}Hook -{{- if ne .DriverName "mssql" -}} +{{if ne .DriverName "mssql" -}} var {{$varNameSingular}}AfterUpsertHooks []{{$tableNameSingular}}Hook {{- end}} @@ -140,7 +140,7 @@ func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular} {{$varNameSingular}}AfterUpdateHooks = append({{$varNameSingular}}AfterUpdateHooks, {{$varNameSingular}}Hook) case boil.AfterDeleteHook: {{$varNameSingular}}AfterDeleteHooks = append({{$varNameSingular}}AfterDeleteHooks, {{$varNameSingular}}Hook) - {{- if ne .DriverName "mssql" -}} + {{if ne .DriverName "mssql" -}} case boil.AfterUpsertHook: {{$varNameSingular}}AfterUpsertHooks = append({{$varNameSingular}}AfterUpsertHooks, {{$varNameSingular}}Hook) {{- end}} diff --git a/templates/18_delete.tpl b/templates/18_delete.tpl index c34822e..317ae19 100644 --- a/templates/18_delete.tpl +++ b/templates/18_delete.tpl @@ -135,11 +135,22 @@ func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error { args = append(args, pkeyArgs...) } + {{if ne .DriverName "mssql" -}} sql := fmt.Sprintf( "DELETE FROM {{$schemaTable}} WHERE (%s) IN (%s)", strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, {{$varNameSingular}}PrimaryKeyColumns), ","), strmangle.Placeholders(dialect.IndexPlaceholders, len(o) * len({{$varNameSingular}}PrimaryKeyColumns), 1, len({{$varNameSingular}}PrimaryKeyColumns)), ) + {{- else -}} + startIndex := 1 + + if dialect.IndexPlaceholders { + startIndex = 0 + } + + sql := "DELETE FROM {{$schemaTable}} WHERE " + + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), startIndex, {{$varNameSingular}}PrimaryKeyColumns, len(o)) + {{- end}} if boil.DebugMode { fmt.Fprintln(boil.DebugWriter, sql) diff --git a/templates_test/hooks.tpl b/templates_test/hooks.tpl index 9f11698..412ab0c 100644 --- a/templates_test/hooks.tpl +++ b/templates_test/hooks.tpl @@ -126,7 +126,7 @@ func test{{$tableNamePlural}}Hooks(t *testing.T) { } {{$varNameSingular}}AfterDeleteHooks = []{{$tableNameSingular}}Hook{} - {{- if ne .DriverName "mssql" -}} + {{if ne .DriverName "mssql" -}} Add{{$tableNameSingular}}Hook(boil.BeforeUpsertHook, {{$varNameSingular}}BeforeUpsertHook) if err = o.doBeforeUpsertHooks(nil); err != nil { t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err) diff --git a/templates_test/singleton/boil_suites_test.tpl b/templates_test/singleton/boil_suites_test.tpl index c1c3516..022d7be 100644 --- a/templates_test/singleton/boil_suites_test.tpl +++ b/templates_test/singleton/boil_suites_test.tpl @@ -327,7 +327,7 @@ func TestSliceUpdateAll(t *testing.T) { {{end -}} {{- end -}} } -{{- if ne .DriverName "mssql" -}} +{{if ne .DriverName "mssql" -}} func TestUpsert(t *testing.T) { {{- range $index, $table := .Tables}} {{- if $table.IsJoinTable -}} From f45f98ef03c2ede2c2107c1244f17fd6841540ac Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Wed, 15 Mar 2017 19:13:20 +0300 Subject: [PATCH 078/179] Fixed placeholders --- templates/18_delete.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/18_delete.tpl b/templates/18_delete.tpl index 317ae19..61282aa 100644 --- a/templates/18_delete.tpl +++ b/templates/18_delete.tpl @@ -144,7 +144,7 @@ func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error { {{- else -}} startIndex := 1 - if dialect.IndexPlaceholders { + if !dialect.IndexPlaceholders { startIndex = 0 } From 4c2062caee7d29bb7342ad6f13d6512e95ca2fc5 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Wed, 15 Mar 2017 19:35:44 +0300 Subject: [PATCH 079/179] Fixed code generation for Update and Reload --- templates/16_update.tpl | 13 ++++++++++++- templates/19_reload.tpl | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/templates/16_update.tpl b/templates/16_update.tpl index f84a35c..6dd8690 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -157,12 +157,23 @@ func (o {{$tableNameSingular}}Slice) UpdateAll(exec boil.Executor, cols M) error pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), {{$varNameSingular}}PrimaryKeyMapping) args = append(args, pkeyArgs...) } - + {{if ne .DriverName "mssql" -}} sql := fmt.Sprintf( "UPDATE {{$schemaTable}} SET %s WHERE ({{.LQ}}{{.Table.PKey.Columns | join (printf "%s,%s" .LQ .RQ)}}{{.RQ}}) IN (%s)", strmangle.SetParamNames("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, colNames), strmangle.Placeholders(dialect.IndexPlaceholders, len(o) * len({{$varNameSingular}}PrimaryKeyColumns), len(colNames)+1, len({{$varNameSingular}}PrimaryKeyColumns)), ) + {{- else -}} + startIndex := len(colNames) + 1 + + if !dialect.IndexPlaceholders { + startIndex = 0 + } + + sql := fmt.Sprintf("UPDATE {{$schemaTable}} SET %s WHERE %s", + strmangle.SetParamNames("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, colNames), + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), startIndex, {{$varNameSingular}}PrimaryKeyColumns, len(o))) + {{- end}} if boil.DebugMode { fmt.Fprintln(boil.DebugWriter, sql) diff --git a/templates/19_reload.tpl b/templates/19_reload.tpl index 45c0e61..ff656f1 100644 --- a/templates/19_reload.tpl +++ b/templates/19_reload.tpl @@ -79,11 +79,22 @@ func (o *{{$tableNameSingular}}Slice) ReloadAll(exec boil.Executor) error { args = append(args, pkeyArgs...) } + {{if ne .DriverName "mssql" -}} sql := fmt.Sprintf( "SELECT {{$schemaTable}}.* FROM {{$schemaTable}} WHERE (%s) IN (%s)", strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, {{$varNameSingular}}PrimaryKeyColumns), ","), strmangle.Placeholders(dialect.IndexPlaceholders, len(*o) * len({{$varNameSingular}}PrimaryKeyColumns), 1, len({{$varNameSingular}}PrimaryKeyColumns)), ) + {{- else -}} + startIndex := 1 + + if !dialect.IndexPlaceholders { + startIndex = 0 + } + + sql := "SELECT {{$schemaTable}}.* FROM {{$schemaTable}} WHERE " + + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), startIndex, {{$varNameSingular}}PrimaryKeyColumns, len(*o)) + {{- end}} q := queries.Raw(exec, sql, args...) From 0a50c90a58a68f3014068477d2bab5d99d4b69f4 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Thu, 16 Mar 2017 09:55:50 +0300 Subject: [PATCH 080/179] Fixed test for MS SQL for columns with IDENTITY --- templates_test/insert.tpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates_test/insert.tpl b/templates_test/insert.tpl index d14a0c8..82f87b6 100644 --- a/templates_test/insert.tpl +++ b/templates_test/insert.tpl @@ -41,7 +41,11 @@ func test{{$tableNamePlural}}InsertWhitelist(t *testing.T) { tx := MustTx(boil.Begin()) defer tx.Rollback() + {{if ne .DriverName "mssql"}} if err = {{$varNameSingular}}.Insert(tx, {{$varNameSingular}}Columns...); err != nil { + {{- else -}} + if err = {{$varNameSingular}}.Insert(tx, {{$varNameSingular}}ColumnsWithoutDefault...); err != nil { + {{- end}} t.Error(err) } From e943c37a9929603def8e4fcfdac98ef900545507 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Thu, 16 Mar 2017 10:26:05 +0300 Subject: [PATCH 081/179] Update and Insert only for columns without autogenerated --- bdb/column.go | 13 +++++++++++++ boilingcore/templates.go | 1 + templates/01_types.tpl | 1 + templates/15_insert.tpl | 2 +- templates/16_update.tpl | 2 +- 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/bdb/column.go b/bdb/column.go index 9ce4bc4..d33e0cb 100644 --- a/bdb/column.go +++ b/bdb/column.go @@ -57,6 +57,19 @@ func ColumnDBTypes(cols []Column) map[string]string { return types } +// FilterColumnsByAuto generates the list of columns that have autogenerated values +func FilterColumnsByAuto(auto bool, columns []Column) []Column { + var cols []Column + + for _, c := range columns { + if (auto && c.AutoGenerated) || (!auto && !c.AutoGenerated) { + cols = append(cols, c) + } + } + + return cols +} + // FilterColumnsByDefault generates the list of columns that have default values func FilterColumnsByDefault(defaults bool, columns []Column) []Column { var cols []Column diff --git a/boilingcore/templates.go b/boilingcore/templates.go index dec57bb..9c60739 100644 --- a/boilingcore/templates.go +++ b/boilingcore/templates.go @@ -225,6 +225,7 @@ var templateFunctions = template.FuncMap{ "txtsFromToMany": txtsFromToMany, // dbdrivers ops + "filterColumnsByAuto": bdb.FilterColumnsByAuto, "filterColumnsByDefault": bdb.FilterColumnsByDefault, "filterColumnsByEnum": bdb.FilterColumnsByEnum, "sqlColDefinitions": bdb.SQLColDefinitions, diff --git a/templates/01_types.tpl b/templates/01_types.tpl index ff497df..2631593 100644 --- a/templates/01_types.tpl +++ b/templates/01_types.tpl @@ -4,6 +4,7 @@ {{- $tableNameSingular := .Table.Name | singular | titleCase -}} var ( {{$varNameSingular}}Columns = []string{{"{"}}{{.Table.Columns | columnNames | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}} + {{$varNameSingular}}ColumnsWithoutAuto = []string{{"{"}}{{.Table.Columns | filterColumnsByAuto false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}ColumnsWithoutDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}ColumnsWithDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault true | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}PrimaryKeyColumns = []string{{"{"}}{{.Table.PKey.Columns | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}} diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 53c2677..b07ef90 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -50,7 +50,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string if !cached { wl, returnColumns := strmangle.InsertColumnSet( - {{$varNameSingular}}Columns, + {{$varNameSingular}}ColumnsWithoutAuto, {{$varNameSingular}}ColumnsWithDefault, {{$varNameSingular}}ColumnsWithoutDefault, nzDefaults, diff --git a/templates/16_update.tpl b/templates/16_update.tpl index 6dd8690..aa3a3d9 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -48,7 +48,7 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string {{$varNameSingular}}UpdateCacheMut.RUnlock() if !cached { - wl := strmangle.UpdateColumnSet({{$varNameSingular}}Columns, {{$varNameSingular}}PrimaryKeyColumns, whitelist) + wl := strmangle.UpdateColumnSet({{$varNameSingular}}ColumnsWithoutAuto, {{$varNameSingular}}PrimaryKeyColumns, whitelist) {{- if not .NoAutoTimestamps}} if len(whitelist) == 0 { wl = strmangle.SetComplement(wl, []string{"created_at"}) From 8de60ee26a4ff5877123de18730f4d54facbaa69 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Thu, 16 Mar 2017 10:54:55 +0300 Subject: [PATCH 082/179] Update and Insert for columns with autogenerated values --- templates/01_types.tpl | 2 ++ templates/15_insert.tpl | 4 ++++ templates/16_update.tpl | 10 +++++++++- templates_test/update.tpl | 4 ++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/templates/01_types.tpl b/templates/01_types.tpl index 2631593..32a5d41 100644 --- a/templates/01_types.tpl +++ b/templates/01_types.tpl @@ -4,7 +4,9 @@ {{- $tableNameSingular := .Table.Name | singular | titleCase -}} var ( {{$varNameSingular}}Columns = []string{{"{"}}{{.Table.Columns | columnNames | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}} + {{if eq .DriverName "mssql" -}} {{$varNameSingular}}ColumnsWithoutAuto = []string{{"{"}}{{.Table.Columns | filterColumnsByAuto false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} + {{- end}} {{$varNameSingular}}ColumnsWithoutDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}ColumnsWithDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault true | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}PrimaryKeyColumns = []string{{"{"}}{{.Table.PKey.Columns | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}} diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index b07ef90..ade915a 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -50,7 +50,11 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string if !cached { wl, returnColumns := strmangle.InsertColumnSet( + {{if ne .DriverName "mssql" -}} + {{$varNameSingular}}Columns, + {{- else -}} {{$varNameSingular}}ColumnsWithoutAuto, + {{- end}} {{$varNameSingular}}ColumnsWithDefault, {{$varNameSingular}}ColumnsWithoutDefault, nzDefaults, diff --git a/templates/16_update.tpl b/templates/16_update.tpl index aa3a3d9..f9eef64 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -48,7 +48,15 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string {{$varNameSingular}}UpdateCacheMut.RUnlock() if !cached { - wl := strmangle.UpdateColumnSet({{$varNameSingular}}ColumnsWithoutAuto, {{$varNameSingular}}PrimaryKeyColumns, whitelist) + wl := strmangle.UpdateColumnSet( + {{if ne .DriverName "mssql" -}} + {{$varNameSingular}}Columns, + {{- else -}} + {{$varNameSingular}}ColumnsWithoutAuto, + {{- end}} + {{$varNameSingular}}PrimaryKeyColumns, + whitelist, + ) {{- if not .NoAutoTimestamps}} if len(whitelist) == 0 { wl = strmangle.SetComplement(wl, []string{"created_at"}) diff --git a/templates_test/update.tpl b/templates_test/update.tpl index 2612a87..ad98e33 100644 --- a/templates_test/update.tpl +++ b/templates_test/update.tpl @@ -79,7 +79,11 @@ func test{{$tableNamePlural}}SliceUpdateAll(t *testing.T) { fields = {{$varNameSingular}}Columns } else { fields = strmangle.SetComplement( + {{if ne .DriverName "mssql" -}} {{$varNameSingular}}Columns, + {{- else -}} + {{$varNameSingular}}ColumnsWithoutAuto, + {{- end}} {{$varNameSingular}}PrimaryKeyColumns, ) } From b1b0249898300f4215d90df42ab8a3570da26893 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Thu, 16 Mar 2017 11:01:49 +0300 Subject: [PATCH 083/179] New line at end of file --- templates/17_upsert.tpl | 2 +- templates_test/upsert.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index 5bc9d3e..0aeb479 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -208,4 +208,4 @@ CacheNoHooks: return nil {{- end}} } -{{- end}} \ No newline at end of file +{{- end}} diff --git a/templates_test/upsert.tpl b/templates_test/upsert.tpl index 68fc435..25807cf 100644 --- a/templates_test/upsert.tpl +++ b/templates_test/upsert.tpl @@ -49,4 +49,4 @@ func test{{$tableNamePlural}}Upsert(t *testing.T) { t.Error("want one record, got:", count) } } -{{- end}} \ No newline at end of file +{{- end}} From 2e9535d56e585524669b99bf05e36af453fdd02f Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Thu, 16 Mar 2017 11:29:31 +0300 Subject: [PATCH 084/179] Added uniqueidentifier and xml types (as string) --- bdb/drivers/mssql.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 254f68d..ec3dc4b 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -313,6 +313,8 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "null.Bytes" case "timestamp", "rowversion": c.Type = "null.Bytes" + case "uniqueidentifier", "xml": + c.Type = "null.String" default: c.Type = "null.String" } @@ -340,6 +342,8 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "[]byte" case "timestamp", "rowversion": c.Type = "[]byte" + case "uniqueidentifier", "xml": + c.Type = "null.String" default: c.Type = "string" } From 5bc7addb8ac25d720a1fc8a5aca2ca877dea5c63 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Thu, 16 Mar 2017 11:30:07 +0300 Subject: [PATCH 085/179] Randomize for uniqueidentifier data type --- randomize/randomize.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/randomize/randomize.go b/randomize/randomize.go index 59f7eff..2af945d 100644 --- a/randomize/randomize.go +++ b/randomize/randomize.go @@ -52,7 +52,7 @@ var ( rgxValidTime = regexp.MustCompile(`[2-9]+`) validatedTypes = []string{ - "inet", "line", "uuid", "interval", "mediumint", + "inet", "line", "uuid", "uniqueidentifier", "interval", "mediumint", "json", "jsonb", "box", "cidr", "circle", "lseg", "macaddr", "path", "pg_lsn", "point", "polygon", "txid_snapshot", "money", "hstore", @@ -195,7 +195,7 @@ func randomizeField(s *Seed, field reflect.Value, fieldType string, canBeNull bo field.Set(reflect.ValueOf(value)) return nil } - if fieldType == "uuid" { + if fieldType == "uuid" || fieldType == "uniqueidentifier" { value = null.NewString(uuid.NewV4().String(), true) field.Set(reflect.ValueOf(value)) return nil @@ -268,7 +268,7 @@ func randomizeField(s *Seed, field reflect.Value, fieldType string, canBeNull bo field.Set(reflect.ValueOf(value)) return nil } - if fieldType == "uuid" { + if fieldType == "uuid" || fieldType == "uniqueidentifier" { value = uuid.NewV4().String() field.Set(reflect.ValueOf(value)) return nil @@ -390,7 +390,7 @@ func getArrayRandValue(s *Seed, typ reflect.Type, fieldType string) interface{} value := strconv.Itoa((s.nextInt()%26)+2) + " days" return types.StringArray{value, value} } - if fieldType == "uuid" { + if fieldType == "uuid" || fieldType == "uniqueidentifier" { value := uuid.NewV4().String() return types.StringArray{value, value} } From 79f6c344c1e38e53a4fc1fce923dd18f56085ff0 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Thu, 16 Mar 2017 11:30:57 +0300 Subject: [PATCH 086/179] Updates for MS SQL support --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index b2d8ae1..00fd5ff 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,12 @@ Table of Contents - PostgreSQL - MySQL +- Microsoft SQL Server *Note: Seeking contributors for other database engines.* +*Microsoft SQL Server: Limit with offset support only for SQL Server 2012 and above.* + ### A Small Taste For a comprehensive list of available operations and examples please see [Features & Examples](#features--examples). @@ -273,6 +276,13 @@ schema="myschema" user="dbusername" pass="dbpassword" sslmode="false" +[mssql] + dbname="dbname" + host="localhost" + port=1433 + user="dbusername" + pass="dbpassword" + sslmode="disable" ``` #### Initial Generation @@ -319,6 +329,9 @@ sqlboiler -b goose_migrations postgres go test ./models ``` +*Note: No `mysqldump` or `pg_dump` equivalent for Microsoft SQL Server, so generated tests must be supplemented by `tables_schema.sql` with `CREATE TABLE ...` queries* + + You can use `go generate` for SQLBoiler if you want to to make it easy to run the command. From 35eefdda41f03ac54d39109109d36b827e662cf1 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Thu, 16 Mar 2017 11:32:24 +0300 Subject: [PATCH 087/179] MS SQL test schema --- testdata/mssql_test_schema.sql | 370 +++++++++++++++++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 testdata/mssql_test_schema.sql diff --git a/testdata/mssql_test_schema.sql b/testdata/mssql_test_schema.sql new file mode 100644 index 0000000..5a529b8 --- /dev/null +++ b/testdata/mssql_test_schema.sql @@ -0,0 +1,370 @@ +CREATE TABLE magic +( + id int NOT NULL IDENTITY (1,1) PRIMARY KEY, + id_two int NOT NULL, + id_three int, + bit_zero bit, + bit_one bit NULL, + bit_two bit NOT NULL, + bit_three bit NULL DEFAULT 0, + bit_four bit NULL DEFAULT 1, + bit_five bit NOT NULL DEFAULT 0, + bit_six bit NOT NULL DEFAULT 1, + string_zero VARCHAR(1), + string_one VARCHAR(1) NULL, + string_two VARCHAR(1) NOT NULL, + string_three VARCHAR(1) NULL DEFAULT 'a', + string_four VARCHAR(1) NOT NULL DEFAULT 'b', + string_five VARCHAR(1000), + string_six VARCHAR(1000) NULL, + string_seven VARCHAR(1000) NOT NULL, + string_eight VARCHAR(1000) NULL DEFAULT 'abcdefgh', + string_nine VARCHAR(1000) NOT NULL DEFAULT 'abcdefgh', + string_ten VARCHAR(1000) NULL DEFAULT '', + string_eleven VARCHAR(1000) NOT NULL DEFAULT '', + big_int_zero bigint, + big_int_one bigint NULL, + big_int_two bigint NOT NULL, + big_int_three bigint NULL DEFAULT 111111, + big_int_four bigint NOT NULL DEFAULT 222222, + big_int_five bigint NULL DEFAULT 0, + big_int_six bigint NOT NULL DEFAULT 0, + int_zero int, + int_one int NULL, + int_two int NOT NULL, + int_three int NULL DEFAULT 333333, + int_four int NOT NULL DEFAULT 444444, + int_five int NULL DEFAULT 0, + int_six int NOT NULL DEFAULT 0, + float_zero float, + float_one float, + float_two float(24), + float_three float(24), + float_four float(24) NULL, + float_five float(24) NOT NULL, + float_six float(24) NULL DEFAULT 1.1, + float_seven float(24) NOT NULL DEFAULT 1.1, + float_eight float(24) NULL DEFAULT 0.0, + float_nine float(24) NULL DEFAULT 0.0, + bytea_zero binary NOT NULL, + bytea_one binary NOT NULL, + bytea_two binary NOT NULL, + bytea_three binary NOT NULL DEFAULT CONVERT(VARBINARY(MAX),'a'), + bytea_four binary NOT NULL DEFAULT CONVERT(VARBINARY(MAX),'b'), + bytea_five binary(100) NOT NULL DEFAULT CONVERT(VARBINARY(MAX),'abcdefghabcdefghabcdefgh'), + bytea_six binary(100) NOT NULL DEFAULT CONVERT(VARBINARY(MAX),'hgfedcbahgfedcbahgfedcba'), + bytea_seven binary NOT NULL DEFAULT CONVERT(VARBINARY(MAX),''), + bytea_eight binary NOT NULL DEFAULT CONVERT(VARBINARY(MAX),''), + time_zero timestamp NOT NULL, + time_one date, + time_eleven date NULL, + time_twelve date NOT NULL, + time_fifteen date NULL DEFAULT '19990108', + time_sixteen date NOT NULL DEFAULT '1999-01-08' +); + +CREATE TABLE magicest +( + id int NOT NULL IDENTITY (1,1) PRIMARY KEY, + kk float NULL, + ll float NOT NULL, + mm tinyint NULL, + nn tinyint NOT NULL, + oo bit NULL, + pp bit NOT NULL, + qq smallint NULL, + rr smallint NOT NULL, + ss int NULL, + tt int NOT NULL, + uu bigint NULL, + vv bigint NOT NULL, + ww float NULL, + xx float NOT NULL, + yy float NULL, + zz float NOT NULL, + aaa double precision NULL, + bbb double precision NOT NULL, + ccc real NULL, + ddd real NOT NULL, + ggg date NULL, + hhh date NOT NULL, + iii datetime NULL, + jjj datetime NOT NULL, + kkk timestamp NOT NULL, + mmm binary NOT NULL, + nnn binary NOT NULL, + ooo varbinary(100) NOT NULL, + ppp varbinary(100) NOT NULL, + qqq varbinary NOT NULL, + rrr varbinary NOT NULL, + www varbinary(max) NOT NULL, + xxx varbinary(max) NOT NULL, + yyy varchar(100) NULL, + zzz varchar(100) NOT NULL, + aaaa char NULL, + bbbb char NOT NULL, + cccc VARCHAR(MAX) NULL, + dddd VARCHAR(MAX) NOT NULL, + eeee tinyint NULL, + ffff tinyint NOT NULL +); + +create table owner +( + id int NOT NULL IDENTITY (1,1) PRIMARY KEY, + name varchar(255) not null +); + +create table cats +( + id int NOT NULL IDENTITY (1,1) PRIMARY KEY, + name varchar(255) not null, + owner_id int references owner (id) +); + +create table toys +( + id int NOT NULL IDENTITY (1,1) PRIMARY KEY, + name varchar(255) not null +); + +create table cat_toys +( + cat_id int not null references cats (id), + toy_id int not null references toys (id), + primary key (cat_id, toy_id) +); + +create table dog_toys +( + dog_id int not null, + toy_id int not null, + primary key (dog_id, toy_id) +); + +create table dragon_toys +( + dragon_id varchar(100), + toy_id varchar(100), + primary key (dragon_id, toy_id) +); + +create table spider_toys +( + spider_id varchar(100) primary key, + name varchar(100) +); + +create table pals +( + pal varchar(100) primary key, + name varchar(100) +); + +create table friend +( + friend varchar(100) primary key, + name varchar(100) +); + +create table bro +( + bros varchar(100) primary key, + name varchar(100) +); + +create table enemies +( + enemies varchar(100) primary key, + name varchar(100) +); + +create table chocolate +( + dog varchar(100) primary key +); + +create table waffles +( + cat varchar(100) primary key +); + +create table tigers +( + id binary primary key, + name binary NOT NULL +); + +create table elephants +( + id binary primary key, + name binary not null, + tiger_id binary NOT NULL unique, + foreign key (tiger_id) references tigers (id) +); + +create table wolves +( + id binary primary key, + name binary not null, + tiger_id binary not null unique, + foreign key (tiger_id) references tigers (id) +); + +create table ants +( + id binary primary key, + name binary not null, + tiger_id binary not null, + foreign key (tiger_id) references tigers (id) +); + +create table worms +( + id binary primary key, + name binary not null, + tiger_id binary NOT NULL, + foreign key (tiger_id) references tigers (id) +); + +create table byte_pilots +( + id binary primary key not null, + name varchar(255) +); + +create table byte_airports +( + id binary primary key not null, + name varchar(255) +); + +create table byte_languages +( + id binary primary key not null, + name varchar(255) +); + +create table byte_jets +( + id binary primary key not null, + name varchar(255), + byte_pilot_id binary unique NOT NULL, + byte_airport_id binary NOT NULL, + + foreign key (byte_pilot_id) references byte_pilots (id), + foreign key (byte_airport_id) references byte_airports (id) +); + +create table byte_pilot_languages +( + byte_pilot_id binary not null, + byte_language_id binary not null, + + primary key (byte_pilot_id, byte_language_id), + foreign key (byte_pilot_id) references byte_pilots (id), + foreign key (byte_language_id) references byte_languages (id) +); + +create table cars +( + id integer not null, + name VARCHAR(MAX), + primary key (id) +); + +create table car_cars +( + car_id integer not null, + awesome_car_id integer not null, + relation VARCHAR(MAX) not null, + primary key (car_id, awesome_car_id), + foreign key (car_id) references cars(id), + foreign key (awesome_car_id) references cars(id) +); + +create table trucks +( + id integer not null, + parent_id integer, + name VARCHAR(MAX), + primary key (id), + foreign key (parent_id) references trucks(id) +); + +CREATE TABLE race +( + id integer PRIMARY KEY NOT NULL, + race_date datetime, + track VARCHAR(MAX) +); + +CREATE TABLE race_results +( + id integer PRIMARY KEY NOT NULL, + race_id integer, + name VARCHAR(MAX), + foreign key (race_id) references race(id) +); + +CREATE TABLE race_result_scratchings +( + id integer PRIMARY KEY NOT NULL, + results_id integer NOT NULL, + name VARCHAR(MAX) NOT NULL, + foreign key (results_id) references race_results(id) +); + +CREATE TABLE pilots +( + id integer NOT NULL, + name VARCHAR(MAX) NOT NULL +); + +ALTER TABLE pilots ADD CONSTRAINT pilot_pkey PRIMARY KEY (id); + +CREATE TABLE jets +( + id integer NOT NULL, + pilot_id integer NOT NULL, + age integer NOT NULL, + name VARCHAR(MAX) NOT NULL, + color VARCHAR(MAX) NOT NULL +); + +ALTER TABLE jets ADD CONSTRAINT jet_pkey PRIMARY KEY (id); +ALTER TABLE jets ADD CONSTRAINT pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); + +CREATE TABLE languages +( + id integer NOT NULL, + language VARCHAR(MAX) NOT NULL +); + +ALTER TABLE languages ADD CONSTRAINT language_pkey PRIMARY KEY (id); + +-- Join table +CREATE TABLE pilot_languages +( + pilot_id integer NOT NULL, + language_id integer NOT NULL, + uniqueid uniqueidentifier NOT NULL, +); + +-- Composite primary key +ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pkey PRIMARY KEY (pilot_id, language_id); +ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); +ALTER TABLE pilot_languages ADD CONSTRAINT languages_fkey FOREIGN KEY (language_id) REFERENCES languages(id); + +CREATE TABLE powers_of_two +( + vid int NOT NULL IDENTITY(1,1), + name varchar(255) NOT NULL DEFAULT '', + machine_name varchar(255) NOT NULL, + description VARCHAR(MAX), + hierarchy tinyint NOT NULL DEFAULT '0', + module varchar(255) NOT NULL DEFAULT '', + weight int NOT NULL DEFAULT '0', + PRIMARY KEY (vid), + CONSTRAINT machine_name UNIQUE(machine_name) +); From 4718e72b6df505b6cb1a5655422bce68c367c9dd Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Thu, 16 Mar 2017 11:43:51 +0300 Subject: [PATCH 088/179] Idiomatic quotes --- bdb/drivers/mssql.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index ec3dc4b..aaa9ea9 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -354,12 +354,12 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { // RightQuote is the quoting character for the right side of the identifier func (m *MSSQLDriver) RightQuote() byte { - return '"' + return ']' } // LeftQuote is the quoting character for the left side of the identifier func (m *MSSQLDriver) LeftQuote() byte { - return '"' + return '[' } // IndexPlaceholders returns true to indicate MS SQL supports indexed placeholders From f3f8074833b085ccefb8a09540285d7d53f63619 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 18 Mar 2017 15:18:04 +0300 Subject: [PATCH 089/179] Removed blank lines and spaces --- bdb/drivers/mssql.go | 1 - strmangle/strmangle.go | 6 ------ templates/15_insert.tpl | 2 +- templates_test/main_test/mssql_main.tpl | 1 - 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index aaa9ea9..7998128 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -32,7 +32,6 @@ func NewMSSQLDriver(user, pass, dbname, host string, port int, sslmode string) * // MSSQLBuildQueryString builds a query string for MSSQL. func MSSQLBuildQueryString(user, pass, dbname, host string, port int, sslmode string) string { - query := url.Values{} query.Add("database", dbname) query.Add("encrypt", sslmode) diff --git a/strmangle/strmangle.go b/strmangle/strmangle.go index b5b68cb..018ffc7 100644 --- a/strmangle/strmangle.go +++ b/strmangle/strmangle.go @@ -524,16 +524,11 @@ func WhereClause(lq, rq string, start int, cols []string) string { // WhereClauseRepeated returns the where clause repeated with OR clause using start as the $ flag index // For example, if start was 2 output would be: "(colthing=$2 AND colstuff=$3) OR (colthing=$4 AND colstuff=$5)" func WhereClauseRepeated(lq, rq string, start int, cols []string, count int) string { - var startIndex int - buf := GetBuffer() defer PutBuffer(buf) - buf.WriteByte('(') - for i := 0; i < count; i++ { - if i != 0 { buf.WriteString(") OR (") } @@ -546,7 +541,6 @@ func WhereClauseRepeated(lq, rq string, start int, cols []string, count int) str buf.WriteString(WhereClause(lq, rq, startIndex, cols)) } - buf.WriteByte(')') return buf.String() diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index ade915a..1c4be56 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -91,7 +91,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string if len(cache.retMapping) != 0 { {{if .UseLastInsertID -}} cache.retQuery = fmt.Sprintf("SELECT {{.LQ}}%s{{.RQ}} FROM {{$schemaTable}} WHERE %s", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}"), strmangle.WhereClause("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns)) - {{else -}} + {{else -}} cache.query += fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) {{- end}} } diff --git a/templates_test/main_test/mssql_main.tpl b/templates_test/main_test/mssql_main.tpl index a164819..da96993 100644 --- a/templates_test/main_test/mssql_main.tpl +++ b/templates_test/main_test/mssql_main.tpl @@ -34,7 +34,6 @@ func (m *mssqlTester) setup() error { createCmd := exec.Command("sqlcmd", "-S", m.host, "-U", m.user, "-P", m.pass, "-d", m.testDBName) f, err := os.Open("tables_schema.sql") - if err != nil { return errors.Wrap(err, "failed to open tables_schema.sql file") } From 21917fcfc6481f848b234ffd1a580108e513b3e4 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 18 Mar 2017 15:32:39 +0300 Subject: [PATCH 090/179] Refactored if-else in WhereClauseRepeated --- strmangle/strmangle.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/strmangle/strmangle.go b/strmangle/strmangle.go index 018ffc7..b330ac0 100644 --- a/strmangle/strmangle.go +++ b/strmangle/strmangle.go @@ -533,10 +533,9 @@ func WhereClauseRepeated(lq, rq string, start int, cols []string, count int) str buf.WriteString(") OR (") } + startIndex = 0 if start > 0 { startIndex = start + i*len(cols) - } else { - startIndex = 0 } buf.WriteString(WhereClause(lq, rq, startIndex, cols)) From 8874738f730516bc37dea4928c833b34a3ad0998 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 18 Mar 2017 15:40:17 +0300 Subject: [PATCH 091/179] Fixed query formatting --- bdb/drivers/mssql.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 7998128..587d761 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -244,18 +244,17 @@ func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey var fkeys []bdb.ForeignKey query := ` - SELECT - ccu.constraint_name - ,ccu.table_name AS local_table - ,ccu.column_name AS local_column - ,kcu.table_name AS foreign_table - ,kcu.column_name AS foreign_column + SELECT ccu.constraint_name , + ccu.table_name AS local_table , + ccu.column_name AS local_column , + kcu.table_name AS foreign_table , + kcu.column_name AS foreign_column FROM information_schema.constraint_column_usage ccu - INNER JOIN information_schema.referential_constraints rc - ON ccu.constraint_name = rc.constraint_name - INNER JOIN information_schema.key_column_usage kcu - ON kcu.constraint_name = rc.unique_constraint_name - WHERE ccu.table_schema = ? AND ccu.constraint_schema = ? AND ccu.table_name = ? + INNER JOIN information_schema.referential_constraints rc ON ccu.constraint_name = rc.constraint_name + INNER JOIN information_schema.key_column_usage kcu ON kcu.constraint_name = rc.unique_constraint_name + WHERE ccu.table_schema = ? + AND ccu.constraint_schema = ? + AND ccu.table_name = ` var rows *sql.Rows From 7162e9e92754afaa30618586db1143157b8a996e Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 18 Mar 2017 15:47:52 +0300 Subject: [PATCH 092/179] Fixed default port for MS SQL --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 3f6601e..db9cc07 100644 --- a/main.go +++ b/main.go @@ -274,7 +274,7 @@ func preRun(cmd *cobra.Command, args []string) error { } if cmdConfig.MSSQL.Port == 0 { - cmdConfig.MSSQL.Port = 3306 + cmdConfig.MSSQL.Port = 1433 viper.Set("mssql.port", cmdConfig.MSSQL.Port) } From 97e32c21be0e9e13465d157f4c5540f4a9249c22 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 18 Mar 2017 15:50:03 +0300 Subject: [PATCH 093/179] Default MS SQL sslmode and port for viper --- main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.go b/main.go index db9cc07..a2f9396 100644 --- a/main.go +++ b/main.go @@ -95,6 +95,8 @@ func main() { viper.SetDefault("postgres.port", "5432") viper.SetDefault("mysql.sslmode", "true") viper.SetDefault("mysql.port", "3306") + viper.SetDefault("mssql.sslmode", "true") + viper.SetDefault("mssql.port", "1433") viper.BindPFlags(rootCmd.PersistentFlags()) viper.AutomaticEnv() From ccb0c9f6c872341fef8e347b06c7fd7a97a0c501 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 18 Mar 2017 16:22:31 +0300 Subject: [PATCH 094/179] Missed placeholder --- bdb/drivers/mssql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 587d761..380b016 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -254,7 +254,7 @@ func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey INNER JOIN information_schema.key_column_usage kcu ON kcu.constraint_name = rc.unique_constraint_name WHERE ccu.table_schema = ? AND ccu.constraint_schema = ? - AND ccu.table_name = + AND ccu.table_name = ? ` var rows *sql.Rows From ff5957e4e80b09cd40597fb56fa3eda13fb7322a Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 18 Mar 2017 17:30:35 +0300 Subject: [PATCH 095/179] Fixed queries --- testdata/mssql_test_schema.sql | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/testdata/mssql_test_schema.sql b/testdata/mssql_test_schema.sql index 5a529b8..f519680 100644 --- a/testdata/mssql_test_schema.sql +++ b/testdata/mssql_test_schema.sql @@ -62,6 +62,7 @@ CREATE TABLE magic time_fifteen date NULL DEFAULT '19990108', time_sixteen date NOT NULL DEFAULT '1999-01-08' ); +GO CREATE TABLE magicest ( @@ -108,12 +109,14 @@ CREATE TABLE magicest eeee tinyint NULL, ffff tinyint NOT NULL ); +GO create table owner ( id int NOT NULL IDENTITY (1,1) PRIMARY KEY, name varchar(255) not null ); +GO create table cats ( @@ -121,12 +124,14 @@ create table cats name varchar(255) not null, owner_id int references owner (id) ); +GO create table toys ( id int NOT NULL IDENTITY (1,1) PRIMARY KEY, name varchar(255) not null ); +GO create table cat_toys ( @@ -134,6 +139,7 @@ create table cat_toys toy_id int not null references toys (id), primary key (cat_id, toy_id) ); +GO create table dog_toys ( @@ -141,6 +147,7 @@ create table dog_toys toy_id int not null, primary key (dog_id, toy_id) ); +GO create table dragon_toys ( @@ -148,52 +155,61 @@ create table dragon_toys toy_id varchar(100), primary key (dragon_id, toy_id) ); +GO create table spider_toys ( spider_id varchar(100) primary key, name varchar(100) ); +GO create table pals ( pal varchar(100) primary key, name varchar(100) ); +GO create table friend ( friend varchar(100) primary key, name varchar(100) ); +GO create table bro ( bros varchar(100) primary key, name varchar(100) ); +GO create table enemies ( enemies varchar(100) primary key, name varchar(100) ); +GO create table chocolate ( dog varchar(100) primary key ); +GO create table waffles ( cat varchar(100) primary key ); +GO create table tigers ( id binary primary key, name binary NOT NULL ); +GO create table elephants ( @@ -202,6 +218,7 @@ create table elephants tiger_id binary NOT NULL unique, foreign key (tiger_id) references tigers (id) ); +GO create table wolves ( @@ -210,6 +227,7 @@ create table wolves tiger_id binary not null unique, foreign key (tiger_id) references tigers (id) ); +GO create table ants ( @@ -218,6 +236,7 @@ create table ants tiger_id binary not null, foreign key (tiger_id) references tigers (id) ); +GO create table worms ( @@ -226,24 +245,28 @@ create table worms tiger_id binary NOT NULL, foreign key (tiger_id) references tigers (id) ); +GO create table byte_pilots ( id binary primary key not null, name varchar(255) ); +GO create table byte_airports ( id binary primary key not null, name varchar(255) ); +GO create table byte_languages ( id binary primary key not null, name varchar(255) ); +GO create table byte_jets ( @@ -255,6 +278,7 @@ create table byte_jets foreign key (byte_pilot_id) references byte_pilots (id), foreign key (byte_airport_id) references byte_airports (id) ); +GO create table byte_pilot_languages ( @@ -265,6 +289,7 @@ create table byte_pilot_languages foreign key (byte_pilot_id) references byte_pilots (id), foreign key (byte_language_id) references byte_languages (id) ); +GO create table cars ( @@ -272,6 +297,7 @@ create table cars name VARCHAR(MAX), primary key (id) ); +GO create table car_cars ( @@ -282,6 +308,7 @@ create table car_cars foreign key (car_id) references cars(id), foreign key (awesome_car_id) references cars(id) ); +GO create table trucks ( @@ -291,6 +318,7 @@ create table trucks primary key (id), foreign key (parent_id) references trucks(id) ); +GO CREATE TABLE race ( @@ -298,6 +326,7 @@ CREATE TABLE race race_date datetime, track VARCHAR(MAX) ); +GO CREATE TABLE race_results ( @@ -306,6 +335,7 @@ CREATE TABLE race_results name VARCHAR(MAX), foreign key (race_id) references race(id) ); +GO CREATE TABLE race_result_scratchings ( @@ -314,14 +344,17 @@ CREATE TABLE race_result_scratchings name VARCHAR(MAX) NOT NULL, foreign key (results_id) references race_results(id) ); +GO CREATE TABLE pilots ( id integer NOT NULL, name VARCHAR(MAX) NOT NULL ); +GO ALTER TABLE pilots ADD CONSTRAINT pilot_pkey PRIMARY KEY (id); +GO CREATE TABLE jets ( @@ -331,17 +364,22 @@ CREATE TABLE jets name VARCHAR(MAX) NOT NULL, color VARCHAR(MAX) NOT NULL ); +GO ALTER TABLE jets ADD CONSTRAINT jet_pkey PRIMARY KEY (id); +GO ALTER TABLE jets ADD CONSTRAINT pilots_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); +GO CREATE TABLE languages ( id integer NOT NULL, language VARCHAR(MAX) NOT NULL ); +GO ALTER TABLE languages ADD CONSTRAINT language_pkey PRIMARY KEY (id); +GO -- Join table CREATE TABLE pilot_languages @@ -350,11 +388,15 @@ CREATE TABLE pilot_languages language_id integer NOT NULL, uniqueid uniqueidentifier NOT NULL, ); +GO -- Composite primary key ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_pkey PRIMARY KEY (pilot_id, language_id); +GO ALTER TABLE pilot_languages ADD CONSTRAINT pilot_language_fkey FOREIGN KEY (pilot_id) REFERENCES pilots(id); +GO ALTER TABLE pilot_languages ADD CONSTRAINT languages_fkey FOREIGN KEY (language_id) REFERENCES languages(id); +GO CREATE TABLE powers_of_two ( @@ -368,3 +410,4 @@ CREATE TABLE powers_of_two PRIMARY KEY (vid), CONSTRAINT machine_name UNIQUE(machine_name) ); +GO From e6c6056353beb19753e445418bbc4d67ff16f728 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sun, 19 Mar 2017 00:02:14 +0300 Subject: [PATCH 096/179] Fixed CREATE DATABASE query --- templates_test/main_test/mssql_main.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates_test/main_test/mssql_main.tpl b/templates_test/main_test/mssql_main.tpl index da96993..c1c76f2 100644 --- a/templates_test/main_test/mssql_main.tpl +++ b/templates_test/main_test/mssql_main.tpl @@ -66,7 +66,9 @@ func (m *mssqlTester) sslMode(mode string) string { } func (m *mssqlTester) createTestDB() error { - sql := fmt.Sprintf("create database %s;", m.testDBName) + sql := fmt.Sprintf(` + CREATE DATABASE %s; + GO`, m.testDBName) return m.runCmd(sql, "sqlcmd", "-S", m.host, "-U", m.user, "-P", m.pass) } From 49d052af53f1497f18dee5ba7a26a1aea3866a09 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sun, 19 Mar 2017 12:34:19 +0300 Subject: [PATCH 097/179] FKs moved to alter caluse --- testdata/mssql_test_schema.sql | 80 ++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/testdata/mssql_test_schema.sql b/testdata/mssql_test_schema.sql index f519680..94ff5dc 100644 --- a/testdata/mssql_test_schema.sql +++ b/testdata/mssql_test_schema.sql @@ -122,10 +122,13 @@ create table cats ( id int NOT NULL IDENTITY (1,1) PRIMARY KEY, name varchar(255) not null, - owner_id int references owner (id) + owner_id int ); GO +ALTER TABLE cats ADD CONSTRAINT cats_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES owner(id); +GO + create table toys ( id int NOT NULL IDENTITY (1,1) PRIMARY KEY, @@ -215,38 +218,46 @@ create table elephants ( id binary primary key, name binary not null, - tiger_id binary NOT NULL unique, - foreign key (tiger_id) references tigers (id) + tiger_id binary NOT NULL unique ); GO +ALTER TABLE elephants ADD CONSTRAINT elephants_tiger_id_fkey FOREIGN KEY (tiger_id) REFERENCES tigers(id); +GO + create table wolves ( id binary primary key, name binary not null, - tiger_id binary not null unique, - foreign key (tiger_id) references tigers (id) + tiger_id binary not null unique ); GO +ALTER TABLE wolves ADD CONSTRAINT wolves_tiger_id_fkey FOREIGN KEY (tiger_id) REFERENCES tigers(id); +GO + create table ants ( id binary primary key, name binary not null, - tiger_id binary not null, - foreign key (tiger_id) references tigers (id) + tiger_id binary not null ); GO +ALTER TABLE ants ADD CONSTRAINT ants_tiger_id_fkey FOREIGN KEY (tiger_id) REFERENCES tigers(id); +GO + create table worms ( id binary primary key, name binary not null, - tiger_id binary NOT NULL, - foreign key (tiger_id) references tigers (id) + tiger_id binary NOT NULL ); GO +ALTER TABLE worms ADD CONSTRAINT worms_tiger_id_fkey FOREIGN KEY (tiger_id) REFERENCES tigers(id); +GO + create table byte_pilots ( id binary primary key not null, @@ -273,24 +284,30 @@ create table byte_jets id binary primary key not null, name varchar(255), byte_pilot_id binary unique NOT NULL, - byte_airport_id binary NOT NULL, - - foreign key (byte_pilot_id) references byte_pilots (id), - foreign key (byte_airport_id) references byte_airports (id) + byte_airport_id binary NOT NULL ); GO +ALTER TABLE byte_jets ADD CONSTRAINT byte_pilot_id_fkey FOREIGN KEY (byte_pilot_id) REFERENCES byte_pilots(id); +GO +ALTER TABLE byte_jets ADD CONSTRAINT byte_airport_id_fkey FOREIGN KEY (byte_airport_id) REFERENCES byte_airports(id); +GO + create table byte_pilot_languages ( byte_pilot_id binary not null, - byte_language_id binary not null, - - primary key (byte_pilot_id, byte_language_id), - foreign key (byte_pilot_id) references byte_pilots (id), - foreign key (byte_language_id) references byte_languages (id) + byte_language_id binary not null ); GO +ALTER TABLE byte_pilot_languages ADD CONSTRAINT byte_pilot_languages_pkey PRIMARY KEY (byte_pilot_id,byte_language_id); +GO + +ALTER TABLE byte_pilot_languages ADD CONSTRAINT byte_pilot_id_fkey FOREIGN KEY (byte_pilot_id) REFERENCES byte_pilots(id); +GO +ALTER TABLE byte_pilot_languages ADD CONSTRAINT byte_language_id_fkey FOREIGN KEY (byte_language_id) REFERENCES byte_languages(id); +GO + create table cars ( id integer not null, @@ -304,22 +321,27 @@ create table car_cars car_id integer not null, awesome_car_id integer not null, relation VARCHAR(MAX) not null, - primary key (car_id, awesome_car_id), - foreign key (car_id) references cars(id), - foreign key (awesome_car_id) references cars(id) + primary key (car_id, awesome_car_id) ); GO +ALTER TABLE car_cars ADD CONSTRAINT car_id_fkey FOREIGN KEY (car_id) REFERENCES cars(id); +GO +ALTER TABLE car_cars ADD CONSTRAINT awesome_car_id_fkey FOREIGN KEY (awesome_car_id) REFERENCES cars(id); +GO + create table trucks ( id integer not null, parent_id integer, name VARCHAR(MAX), - primary key (id), - foreign key (parent_id) references trucks(id) + primary key (id) ); GO +ALTER TABLE trucks ADD CONSTRAINT parent_id_fkey FOREIGN KEY (parent_id) REFERENCES trucks(id); +GO + CREATE TABLE race ( id integer PRIMARY KEY NOT NULL, @@ -332,20 +354,24 @@ CREATE TABLE race_results ( id integer PRIMARY KEY NOT NULL, race_id integer, - name VARCHAR(MAX), - foreign key (race_id) references race(id) + name VARCHAR(MAX) ); GO +ALTER TABLE race_results ADD CONSTRAINT race_id_fkey FOREIGN KEY (race_id) REFERENCES race(id); +GO + CREATE TABLE race_result_scratchings ( id integer PRIMARY KEY NOT NULL, results_id integer NOT NULL, - name VARCHAR(MAX) NOT NULL, - foreign key (results_id) references race_results(id) + name VARCHAR(MAX) NOT NULL ); GO +ALTER TABLE race_result_scratchings ADD CONSTRAINT results_id_fkey FOREIGN KEY (results_id) REFERENCES race_results(id); +GO + CREATE TABLE pilots ( id integer NOT NULL, From a508530f6304d60ba5b49f79dda230fcdfed94a5 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sun, 19 Mar 2017 12:39:59 +0300 Subject: [PATCH 098/179] Allow UseLastInsertID --- bdb/drivers/mssql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 380b016..a3a6a34 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -65,7 +65,7 @@ func (m *MSSQLDriver) Close() { // UseLastInsertID returns false for mssql func (m *MSSQLDriver) UseLastInsertID() bool { - return false + return true } // UseTopClause returns true to indicate MS SQL supports SQL TOP clause From 12dce9d9868f1b1114349e230deafe5355176430 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sun, 19 Mar 2017 12:42:55 +0300 Subject: [PATCH 099/179] Auto columns in Inserts removed --- bdb/column.go | 2 +- bdb/drivers/mssql.go | 4 +++- templates/01_types.tpl | 2 +- templates/15_insert.tpl | 6 ------ 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/bdb/column.go b/bdb/column.go index d33e0cb..a3632fc 100644 --- a/bdb/column.go +++ b/bdb/column.go @@ -75,7 +75,7 @@ func FilterColumnsByDefault(defaults bool, columns []Column) []Column { var cols []Column for _, c := range columns { - if (defaults && (len(c.Default) != 0 || c.AutoGenerated)) || (!defaults && len(c.Default) == 0 && !c.AutoGenerated) { + if (defaults && len(c.Default) != 0) || (!defaults && len(c.Default) == 0) { cols = append(cols, c) } } diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index a3a6a34..6ffee08 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -169,7 +169,7 @@ func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { return nil, errors.Wrapf(err, "unable to scan for table %s", tableName) } - auto = identity || strings.EqualFold(colType, "timestamp") || strings.EqualFold(colType, "rowversion") + auto = strings.EqualFold(colType, "timestamp") || strings.EqualFold(colType, "rowversion") column := bdb.Column{ Name: colName, @@ -182,6 +182,8 @@ func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) { if defaultValue != nil && *defaultValue != "NULL" { column.Default = *defaultValue + } else if identity || auto { + column.Default = "auto" } columns = append(columns, column) } diff --git a/templates/01_types.tpl b/templates/01_types.tpl index 32a5d41..bcf0a94 100644 --- a/templates/01_types.tpl +++ b/templates/01_types.tpl @@ -5,7 +5,7 @@ var ( {{$varNameSingular}}Columns = []string{{"{"}}{{.Table.Columns | columnNames | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}} {{if eq .DriverName "mssql" -}} - {{$varNameSingular}}ColumnsWithoutAuto = []string{{"{"}}{{.Table.Columns | filterColumnsByAuto false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} + {{$varNameSingular}}ColumnsWithAuto = []string{{"{"}}{{.Table.Columns | filterColumnsByAuto true | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{- end}} {{$varNameSingular}}ColumnsWithoutDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}ColumnsWithDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault true | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 1c4be56..3b66281 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -50,11 +50,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string if !cached { wl, returnColumns := strmangle.InsertColumnSet( - {{if ne .DriverName "mssql" -}} {{$varNameSingular}}Columns, - {{- else -}} - {{$varNameSingular}}ColumnsWithoutAuto, - {{- end}} {{$varNameSingular}}ColumnsWithDefault, {{$varNameSingular}}ColumnsWithoutDefault, nzDefaults, @@ -87,7 +83,6 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string {{end -}} } - {{if ne .DriverName "mssql" -}} if len(cache.retMapping) != 0 { {{if .UseLastInsertID -}} cache.retQuery = fmt.Sprintf("SELECT {{.LQ}}%s{{.RQ}} FROM {{$schemaTable}} WHERE %s", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}"), strmangle.WhereClause("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns)) @@ -95,7 +90,6 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string cache.query += fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) {{- end}} } - {{end -}} } value := reflect.Indirect(reflect.ValueOf(o)) From d40d074320d11b1f133b393eeb78104d79298ca0 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sun, 19 Mar 2017 12:44:15 +0300 Subject: [PATCH 100/179] Filtering columns with auto values --- templates/16_update.tpl | 7 +++---- templates_test/update.tpl | 10 ++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/templates/16_update.tpl b/templates/16_update.tpl index f9eef64..2ee87cc 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -49,14 +49,13 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string if !cached { wl := strmangle.UpdateColumnSet( - {{if ne .DriverName "mssql" -}} {{$varNameSingular}}Columns, - {{- else -}} - {{$varNameSingular}}ColumnsWithoutAuto, - {{- end}} {{$varNameSingular}}PrimaryKeyColumns, whitelist, ) + {{if eq .DriverName "mssql"}} + wl = strmangle.SetComplement(wl, {{$varNameSingular}}ColumnsWithAuto) + {{end}} {{- if not .NoAutoTimestamps}} if len(whitelist) == 0 { wl = strmangle.SetComplement(wl, []string{"created_at"}) diff --git a/templates_test/update.tpl b/templates_test/update.tpl index ad98e33..d04401a 100644 --- a/templates_test/update.tpl +++ b/templates_test/update.tpl @@ -79,13 +79,15 @@ func test{{$tableNamePlural}}SliceUpdateAll(t *testing.T) { fields = {{$varNameSingular}}Columns } else { fields = strmangle.SetComplement( - {{if ne .DriverName "mssql" -}} {{$varNameSingular}}Columns, - {{- else -}} - {{$varNameSingular}}ColumnsWithoutAuto, - {{- end}} {{$varNameSingular}}PrimaryKeyColumns, ) + {{- if eq .DriverName "mssql"}} + fields = strmangle.SetComplement( + fields, + {{$varNameSingular}}ColumnsWithAuto, + ) + {{- end}} } value := reflect.Indirect(reflect.ValueOf({{$varNameSingular}})) From 68ac8a3c344a7062828dfd8331066d1c61b9e8e9 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sun, 19 Mar 2017 18:05:55 +0300 Subject: [PATCH 101/179] Disabled UseLastInsertID in favor of query output params --- bdb/drivers/mssql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 6ffee08..9d20d0f 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -65,7 +65,7 @@ func (m *MSSQLDriver) Close() { // UseLastInsertID returns false for mssql func (m *MSSQLDriver) UseLastInsertID() bool { - return true + return false } // UseTopClause returns true to indicate MS SQL supports SQL TOP clause From 13bdda4e20f3791984406e247e8d38b476b55788 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sun, 19 Mar 2017 18:12:06 +0300 Subject: [PATCH 102/179] Simplified insert query --- templates/15_insert.tpl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 3b66281..bb72404 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -66,15 +66,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string return err } if len(wl) != 0 { - {{if ne .DriverName "mssql" -}} - cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) VALUES (%s)", strings.Join(wl, "{{.RQ}},{{.LQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) - {{- else -}} - if len(cache.retMapping) == 0 { - cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) VALUES (%s)", strings.Join(wl, "{{.RQ}},{{.LQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) - } else { - cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) OUTPUT INSERTED.{{.LQ}}%s{{.RQ}} VALUES (%s)", strings.Join(wl, "{{.RQ}},{{.LQ}}"), strings.Join(returnColumns, "{{.RQ}},INSERTED.{{.LQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) - } - {{- end}} + cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) %%sVALUES (%s)%%s", strings.Join(wl, "{{.RQ}},{{.LQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) } else { {{if eq .DriverName "mysql" -}} cache.query = "INSERT INTO {{$schemaTable}} () VALUES ()" @@ -83,13 +75,21 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string {{end -}} } + queryOutput := "" + qyeryReturning := "" + if len(cache.retMapping) != 0 { {{if .UseLastInsertID -}} cache.retQuery = fmt.Sprintf("SELECT {{.LQ}}%s{{.RQ}} FROM {{$schemaTable}} WHERE %s", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}"), strmangle.WhereClause("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns)) {{else -}} - cache.query += fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) + {{if ne .DriverName "mssql" -}} + qyeryReturning = fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) + {{else -}} + queryOutput = fmt.Sprintf("OUTPUT INSERTED.{{.LQ}}%s{{.RQ}} ", strings.Join(returnColumns, "{{.RQ}},INSERTED.{{.LQ}}")) + {{end -}} {{- end}} } + cache.query = fmt.Sprintf(cache.query, queryOutput, qyeryReturning) } value := reflect.Indirect(reflect.ValueOf(o)) From 6bc6b1690cead722833e80b5c7a24b4f6f4323dc Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Mon, 20 Mar 2017 12:50:33 +0300 Subject: [PATCH 103/179] Fixed FK constraints --- testdata/mssql_test_schema.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testdata/mssql_test_schema.sql b/testdata/mssql_test_schema.sql index 94ff5dc..0995aa1 100644 --- a/testdata/mssql_test_schema.sql +++ b/testdata/mssql_test_schema.sql @@ -288,9 +288,9 @@ create table byte_jets ); GO -ALTER TABLE byte_jets ADD CONSTRAINT byte_pilot_id_fkey FOREIGN KEY (byte_pilot_id) REFERENCES byte_pilots(id); +ALTER TABLE byte_jets ADD CONSTRAINT byte_jets_byte_pilot_id_fkey FOREIGN KEY (byte_pilot_id) REFERENCES byte_pilots(id); GO -ALTER TABLE byte_jets ADD CONSTRAINT byte_airport_id_fkey FOREIGN KEY (byte_airport_id) REFERENCES byte_airports(id); +ALTER TABLE byte_jets ADD CONSTRAINT byte_jets_byte_airport_id_fkey FOREIGN KEY (byte_airport_id) REFERENCES byte_airports(id); GO create table byte_pilot_languages @@ -303,9 +303,9 @@ GO ALTER TABLE byte_pilot_languages ADD CONSTRAINT byte_pilot_languages_pkey PRIMARY KEY (byte_pilot_id,byte_language_id); GO -ALTER TABLE byte_pilot_languages ADD CONSTRAINT byte_pilot_id_fkey FOREIGN KEY (byte_pilot_id) REFERENCES byte_pilots(id); +ALTER TABLE byte_pilot_languages ADD CONSTRAINT byte_pilot_languages_byte_pilot_id_fkey FOREIGN KEY (byte_pilot_id) REFERENCES byte_pilots(id); GO -ALTER TABLE byte_pilot_languages ADD CONSTRAINT byte_language_id_fkey FOREIGN KEY (byte_language_id) REFERENCES byte_languages(id); +ALTER TABLE byte_pilot_languages ADD CONSTRAINT byte_pilot_languages_byte_language_id_fkey FOREIGN KEY (byte_language_id) REFERENCES byte_languages(id); GO create table cars From 9bafa2f1586c2fe51671fea6831958e5875b26c8 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 21 Mar 2017 13:04:07 +0300 Subject: [PATCH 104/179] MSSQL Upsert statement --- queries/query_builders.go | 43 +++++++++++++ templates/01_types.tpl | 2 - templates/02_hooks.tpl | 12 ---- templates/17_upsert.tpl | 60 +++++++++++++------ templates_test/hooks.tpl | 4 -- templates_test/singleton/boil_suites_test.tpl | 5 +- templates_test/upsert.tpl | 4 +- 7 files changed, 89 insertions(+), 41 deletions(-) diff --git a/queries/query_builders.go b/queries/query_builders.go index 3136693..7cf753f 100644 --- a/queries/query_builders.go +++ b/queries/query_builders.go @@ -283,6 +283,49 @@ func BuildUpsertQueryPostgres(dia Dialect, tableName string, updateOnConflict bo return buf.String() } +// BuildUpsertQueryMSSQL builds a SQL statement string using the upsertData provided. +func BuildUpsertQueryMSSQL(dia Dialect, tableName string, primary, update, insert []string, output []string) string { + insert = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, insert) + + buf := strmangle.GetBuffer() + defer strmangle.PutBuffer(buf) + + startIndex := 1 + + fmt.Fprintf(buf, "MERGE INTO %s as [t]\n", tableName) + fmt.Fprintf(buf, "USING (SELECT %s) as [s] ([%s])\n", + strmangle.Placeholders(dia.IndexPlaceholders, len(primary), startIndex, 1), + strings.Join(primary, string(dia.RQ)+","+string(dia.LQ))) + fmt.Fprint(buf, "ON (") + for i, v := range primary { + if i != 0 { + fmt.Fprint(buf, " AND ") + } + fmt.Fprintf(buf, "[s].[%s] = [t].[%s]", v, v) + } + fmt.Fprint(buf, ")\n") + + startIndex = len(primary) + 1 + + fmt.Fprint(buf, "WHEN MATCHED THEN ") + fmt.Fprintf(buf, "UPDATE SET %s\n", strmangle.SetParamNames(string(dia.LQ), string(dia.RQ), startIndex, update)) + + startIndex = len(primary) + 1 + len(update) + + fmt.Fprint(buf, "WHEN NOT MATCHED THEN ") + fmt.Fprintf(buf, "INSERT (%s) VALUES (%s)", + strings.Join(insert, ", "), + strmangle.Placeholders(dia.IndexPlaceholders, len(insert), startIndex, 1)) + + if len(output) > 0 { + fmt.Fprintf(buf, "\nOUTPUT INSERTED.[%s];", strings.Join(output, "],INSERTED.[")) + } else { + fmt.Fprint(buf, ";") + } + + return buf.String() +} + func writeModifiers(q *Query, buf *bytes.Buffer, args *[]interface{}) { if len(q.groupBy) != 0 { fmt.Fprintf(buf, " GROUP BY %s", strings.Join(q.groupBy, ", ")) diff --git a/templates/01_types.tpl b/templates/01_types.tpl index bcf0a94..1f8d015 100644 --- a/templates/01_types.tpl +++ b/templates/01_types.tpl @@ -35,10 +35,8 @@ var ( {{$varNameSingular}}InsertCache = make(map[string]insertCache) {{$varNameSingular}}UpdateCacheMut sync.RWMutex {{$varNameSingular}}UpdateCache = make(map[string]updateCache) - {{if ne .DriverName "mssql"}} {{$varNameSingular}}UpsertCacheMut sync.RWMutex {{$varNameSingular}}UpsertCache = make(map[string]insertCache) - {{end}} ) var ( diff --git a/templates/02_hooks.tpl b/templates/02_hooks.tpl index 073f8ec..9815639 100644 --- a/templates/02_hooks.tpl +++ b/templates/02_hooks.tpl @@ -4,17 +4,13 @@ var {{$varNameSingular}}BeforeInsertHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}BeforeUpdateHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}BeforeDeleteHooks []{{$tableNameSingular}}Hook -{{if ne .DriverName "mssql" -}} var {{$varNameSingular}}BeforeUpsertHooks []{{$tableNameSingular}}Hook -{{- end}} var {{$varNameSingular}}AfterInsertHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}AfterSelectHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}AfterUpdateHooks []{{$tableNameSingular}}Hook var {{$varNameSingular}}AfterDeleteHooks []{{$tableNameSingular}}Hook -{{if ne .DriverName "mssql" -}} var {{$varNameSingular}}AfterUpsertHooks []{{$tableNameSingular}}Hook -{{- end}} // doBeforeInsertHooks executes all "before insert" hooks. func (o *{{$tableNameSingular}}) doBeforeInsertHooks(exec boil.Executor) (err error) { @@ -49,7 +45,6 @@ func (o *{{$tableNameSingular}}) doBeforeDeleteHooks(exec boil.Executor) (err er return nil } -{{- if ne .DriverName "mssql" -}} // doBeforeUpsertHooks executes all "before Upsert" hooks. func (o *{{$tableNameSingular}}) doBeforeUpsertHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}BeforeUpsertHooks { @@ -60,7 +55,6 @@ func (o *{{$tableNameSingular}}) doBeforeUpsertHooks(exec boil.Executor) (err er return nil } -{{- end}} // doAfterInsertHooks executes all "after Insert" hooks. func (o *{{$tableNameSingular}}) doAfterInsertHooks(exec boil.Executor) (err error) { @@ -106,7 +100,6 @@ func (o *{{$tableNameSingular}}) doAfterDeleteHooks(exec boil.Executor) (err err return nil } -{{- if ne .DriverName "mssql" -}} // doAfterUpsertHooks executes all "after Upsert" hooks. func (o *{{$tableNameSingular}}) doAfterUpsertHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}AfterUpsertHooks { @@ -117,7 +110,6 @@ func (o *{{$tableNameSingular}}) doAfterUpsertHooks(exec boil.Executor) (err err return nil } -{{- end}} // Add{{$tableNameSingular}}Hook registers your hook function for all future operations. func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular}}Hook {{$tableNameSingular}}Hook) { @@ -128,10 +120,8 @@ func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular} {{$varNameSingular}}BeforeUpdateHooks = append({{$varNameSingular}}BeforeUpdateHooks, {{$varNameSingular}}Hook) case boil.BeforeDeleteHook: {{$varNameSingular}}BeforeDeleteHooks = append({{$varNameSingular}}BeforeDeleteHooks, {{$varNameSingular}}Hook) - {{if ne .DriverName "mssql" -}} case boil.BeforeUpsertHook: {{$varNameSingular}}BeforeUpsertHooks = append({{$varNameSingular}}BeforeUpsertHooks, {{$varNameSingular}}Hook) - {{- end}} case boil.AfterInsertHook: {{$varNameSingular}}AfterInsertHooks = append({{$varNameSingular}}AfterInsertHooks, {{$varNameSingular}}Hook) case boil.AfterSelectHook: @@ -140,10 +130,8 @@ func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular} {{$varNameSingular}}AfterUpdateHooks = append({{$varNameSingular}}AfterUpdateHooks, {{$varNameSingular}}Hook) case boil.AfterDeleteHook: {{$varNameSingular}}AfterDeleteHooks = append({{$varNameSingular}}AfterDeleteHooks, {{$varNameSingular}}Hook) - {{if ne .DriverName "mssql" -}} case boil.AfterUpsertHook: {{$varNameSingular}}AfterUpsertHooks = append({{$varNameSingular}}AfterUpsertHooks, {{$varNameSingular}}Hook) - {{- end}} } } {{- end}} diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index 0aeb479..70df4c7 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -1,29 +1,28 @@ -{{- if ne .DriverName "mssql" -}} {{- $tableNameSingular := .Table.Name | singular | titleCase -}} {{- $varNameSingular := .Table.Name | singular | camelCase -}} {{- $schemaTable := .Table.Name | .SchemaTable}} // UpsertG attempts an insert, and does an update or ignore on conflict. -func (o *{{$tableNameSingular}}) UpsertG({{if ne .DriverName "mysql"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) error { - return o.Upsert(boil.GetDB(), {{if ne .DriverName "mysql"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...) +func (o *{{$tableNameSingular}}) UpsertG({{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) error { + return o.Upsert(boil.GetDB(), {{if eq .DriverName "postgres"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...) } // UpsertGP attempts an insert, and does an update or ignore on conflict. Panics on error. -func (o *{{$tableNameSingular}}) UpsertGP({{if ne .DriverName "mysql"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) { - if err := o.Upsert(boil.GetDB(), {{if ne .DriverName "mysql"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...); err != nil { +func (o *{{$tableNameSingular}}) UpsertGP({{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) { + if err := o.Upsert(boil.GetDB(), {{if eq .DriverName "postgres"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...); err != nil { panic(boil.WrapErr(err)) } } // UpsertP attempts an insert using an executor, and does an update or ignore on conflict. // UpsertP panics on error. -func (o *{{$tableNameSingular}}) UpsertP(exec boil.Executor, {{if ne .DriverName "mysql"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) { - if err := o.Upsert(exec, {{if ne .DriverName "mysql"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...); err != nil { +func (o *{{$tableNameSingular}}) UpsertP(exec boil.Executor, {{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) { + if err := o.Upsert(exec, {{if eq .DriverName "postgres"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...); err != nil { panic(boil.WrapErr(err)) } } // Upsert attempts an insert using an executor, and does an update or ignore on conflict. -func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if ne .DriverName "mysql"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) error { +func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) error { if o == nil { return errors.New("{{.PkgName}}: no {{.Table.Name}} provided for upsert") } @@ -40,7 +39,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if ne .DriverName // Build cache key in-line uglily - mysql vs postgres problems buf := strmangle.GetBuffer() - {{if ne .DriverName "mysql" -}} + {{if eq .DriverName "postgres"}} if updateOnConflict { buf.WriteByte('t') } else { @@ -73,36 +72,64 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if ne .DriverName var err error if !cached { - var ret []string - whitelist, ret = strmangle.InsertColumnSet( + insert, ret := strmangle.InsertColumnSet( {{$varNameSingular}}Columns, {{$varNameSingular}}ColumnsWithDefault, {{$varNameSingular}}ColumnsWithoutDefault, nzDefaults, whitelist, ) + {{- if eq .DriverName "mssql"}} + insert = strmangle.SetComplement(insert, {{$varNameSingular}}ColumnsWithAuto) + for i, v := range insert { + if strmangle.ContainsAny({{$varNameSingular}}PrimaryKeyColumns, v) && strmangle.ContainsAny({{$varNameSingular}}ColumnsWithDefault, v) { + insert = append(insert[:i], insert[i+1:]...) + } + } + if len(insert) == 0 { + return errors.New("models: unable to upsert {{.Table.Name}}, could not build insert column list") + } + + ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithAuto) + ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithDefault) + + {{end -}} update := strmangle.UpdateColumnSet( {{$varNameSingular}}Columns, {{$varNameSingular}}PrimaryKeyColumns, updateColumns, ) + + {{- if eq .DriverName "mssql"}} + update = strmangle.SetComplement(update, {{$varNameSingular}}ColumnsWithAuto) + {{end -}} + if len(update) == 0 { return errors.New("{{.PkgName}}: unable to upsert {{.Table.Name}}, could not build update column list") } - {{if ne .DriverName "mysql" -}} + {{if eq .DriverName "postgres"}} conflict := conflictColumns if len(conflict) == 0 { conflict = make([]string, len({{$varNameSingular}}PrimaryKeyColumns)) copy(conflict, {{$varNameSingular}}PrimaryKeyColumns) } - cache.query = queries.BuildUpsertQueryPostgres(dialect, "{{$schemaTable}}", updateOnConflict, ret, update, conflict, whitelist) - {{- else -}} - cache.query = queries.BuildUpsertQueryMySQL(dialect, "{{.Table.Name}}", update, whitelist) + cache.query = queries.BuildUpsertQueryPostgres(dialect, "{{$schemaTable}}", updateOnConflict, ret, update, conflict, insert) + {{- end -}} + + {{if eq .DriverName "mysql"}} + cache.query = queries.BuildUpsertQueryMySQL(dialect, "{{.Table.Name}}", update, insert) cache.retQuery = fmt.Sprintf( "SELECT %s FROM {{.LQ}}{{.Table.Name}}{{.RQ}} WHERE {{whereClause .LQ .RQ 0 .Table.PKey.Columns}}", strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, ret), ","), ) + {{- end -}} + + {{if eq .DriverName "mssql"}} + cache.query = queries.BuildUpsertQueryMSSQL(dialect, "{{.Table.Name}}", {{$varNameSingular}}PrimaryKeyColumns, update, insert, ret) + + whitelist = append({{$varNameSingular}}PrimaryKeyColumns, update...) + whitelist = append(whitelist, insert...) {{- end}} cache.valueMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, whitelist) @@ -207,5 +234,4 @@ CacheNoHooks: {{- else -}} return nil {{- end}} -} -{{- end}} +} \ No newline at end of file diff --git a/templates_test/hooks.tpl b/templates_test/hooks.tpl index 412ab0c..2cdca44 100644 --- a/templates_test/hooks.tpl +++ b/templates_test/hooks.tpl @@ -38,7 +38,6 @@ func {{$varNameSingular}}AfterDeleteHook(e boil.Executor, o *{{$tableNameSingula return nil } -{{if ne .DriverName "mssql" -}} func {{$varNameSingular}}BeforeUpsertHook(e boil.Executor, o *{{$tableNameSingular}}) error { *o = {{$tableNameSingular}}{} return nil @@ -48,7 +47,6 @@ func {{$varNameSingular}}AfterUpsertHook(e boil.Executor, o *{{$tableNameSingula *o = {{$tableNameSingular}}{} return nil } -{{- end}} func test{{$tableNamePlural}}Hooks(t *testing.T) { t.Parallel() @@ -126,7 +124,6 @@ func test{{$tableNamePlural}}Hooks(t *testing.T) { } {{$varNameSingular}}AfterDeleteHooks = []{{$tableNameSingular}}Hook{} - {{if ne .DriverName "mssql" -}} Add{{$tableNameSingular}}Hook(boil.BeforeUpsertHook, {{$varNameSingular}}BeforeUpsertHook) if err = o.doBeforeUpsertHooks(nil); err != nil { t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err) @@ -144,6 +141,5 @@ func test{{$tableNamePlural}}Hooks(t *testing.T) { t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o) } {{$varNameSingular}}AfterUpsertHooks = []{{$tableNameSingular}}Hook{} - {{- end}} } {{- end}} diff --git a/templates_test/singleton/boil_suites_test.tpl b/templates_test/singleton/boil_suites_test.tpl index 022d7be..a2ca510 100644 --- a/templates_test/singleton/boil_suites_test.tpl +++ b/templates_test/singleton/boil_suites_test.tpl @@ -327,7 +327,7 @@ func TestSliceUpdateAll(t *testing.T) { {{end -}} {{- end -}} } -{{if ne .DriverName "mssql" -}} + func TestUpsert(t *testing.T) { {{- range $index, $table := .Tables}} {{- if $table.IsJoinTable -}} @@ -336,5 +336,4 @@ func TestUpsert(t *testing.T) { t.Run("{{$tableName}}", test{{$tableName}}Upsert) {{end -}} {{- end -}} -} -{{- end -}} \ No newline at end of file +} \ No newline at end of file diff --git a/templates_test/upsert.tpl b/templates_test/upsert.tpl index 25807cf..7e08819 100644 --- a/templates_test/upsert.tpl +++ b/templates_test/upsert.tpl @@ -1,4 +1,3 @@ -{{- if ne .DriverName "mssql" -}} {{- $tableNameSingular := .Table.Name | singular | titleCase -}} {{- $tableNamePlural := .Table.Name | plural | titleCase -}} {{- $varNamePlural := .Table.Name | plural | camelCase -}} @@ -48,5 +47,4 @@ func test{{$tableNamePlural}}Upsert(t *testing.T) { if count != 1 { t.Error("want one record, got:", count) } -} -{{- end}} +} \ No newline at end of file From f4b9b2a40627e8906ec9807c33b93585c1be3c6d Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Tue, 21 Mar 2017 13:15:37 +0300 Subject: [PATCH 105/179] Better startIndex calculation --- queries/query_builders.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queries/query_builders.go b/queries/query_builders.go index 7cf753f..2188044 100644 --- a/queries/query_builders.go +++ b/queries/query_builders.go @@ -305,12 +305,12 @@ func BuildUpsertQueryMSSQL(dia Dialect, tableName string, primary, update, inser } fmt.Fprint(buf, ")\n") - startIndex = len(primary) + 1 + startIndex += len(primary) fmt.Fprint(buf, "WHEN MATCHED THEN ") fmt.Fprintf(buf, "UPDATE SET %s\n", strmangle.SetParamNames(string(dia.LQ), string(dia.RQ), startIndex, update)) - startIndex = len(primary) + 1 + len(update) + startIndex += len(update) fmt.Fprint(buf, "WHEN NOT MATCHED THEN ") fmt.Fprintf(buf, "INSERT (%s) VALUES (%s)", From 8ef33a112784f7d86bcd6431c3d912c30dee84d8 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Fri, 31 Mar 2017 10:52:54 +0300 Subject: [PATCH 106/179] PkgName in error --- templates/17_upsert.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index 70df4c7..92fc4ca 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -87,7 +87,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName } } if len(insert) == 0 { - return errors.New("models: unable to upsert {{.Table.Name}}, could not build insert column list") + return errors.New("{{.PkgName}}: unable to upsert {{.Table.Name}}, could not build insert column list") } ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithAuto) From 83b935168f754c22a25eef04e3a87d6b1986c8da Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Fri, 31 Mar 2017 10:59:03 +0300 Subject: [PATCH 107/179] Branching with if-else --- templates/17_upsert.tpl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index 92fc4ca..a7a527c 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -115,17 +115,13 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName copy(conflict, {{$varNameSingular}}PrimaryKeyColumns) } cache.query = queries.BuildUpsertQueryPostgres(dialect, "{{$schemaTable}}", updateOnConflict, ret, update, conflict, insert) - {{- end -}} - - {{if eq .DriverName "mysql"}} + {{else if eq .DriverName "mysql"}} cache.query = queries.BuildUpsertQueryMySQL(dialect, "{{.Table.Name}}", update, insert) cache.retQuery = fmt.Sprintf( "SELECT %s FROM {{.LQ}}{{.Table.Name}}{{.RQ}} WHERE {{whereClause .LQ .RQ 0 .Table.PKey.Columns}}", strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, ret), ","), ) - {{- end -}} - - {{if eq .DriverName "mssql"}} + {{else if eq .DriverName "mssql"}} cache.query = queries.BuildUpsertQueryMSSQL(dialect, "{{.Table.Name}}", {{$varNameSingular}}PrimaryKeyColumns, update, insert, ret) whitelist = append({{$varNameSingular}}PrimaryKeyColumns, update...) From b4fc4447f803bd12f2961dd1877e39b62a600335 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Fri, 31 Mar 2017 11:03:16 +0300 Subject: [PATCH 108/179] Fixed trim direction --- templates/01_types.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/01_types.tpl b/templates/01_types.tpl index 1f8d015..a1a1ef0 100644 --- a/templates/01_types.tpl +++ b/templates/01_types.tpl @@ -6,7 +6,7 @@ var ( {{$varNameSingular}}Columns = []string{{"{"}}{{.Table.Columns | columnNames | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}} {{if eq .DriverName "mssql" -}} {{$varNameSingular}}ColumnsWithAuto = []string{{"{"}}{{.Table.Columns | filterColumnsByAuto true | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} - {{- end}} + {{end -}} {{$varNameSingular}}ColumnsWithoutDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}ColumnsWithDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault true | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}PrimaryKeyColumns = []string{{"{"}}{{.Table.PKey.Columns | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}} From b10df110a0240c5674a82c116234b36c02e4f6ce Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Fri, 31 Mar 2017 11:05:52 +0300 Subject: [PATCH 109/179] Newline at end of file --- templates/17_upsert.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index a7a527c..fb9c2d0 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -230,4 +230,4 @@ CacheNoHooks: {{- else -}} return nil {{- end}} -} \ No newline at end of file +} From dbf897977a0a0ac73e4c4408b56fb7fe7bf88b3d Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Fri, 31 Mar 2017 11:07:30 +0300 Subject: [PATCH 110/179] Newline at end of file --- templates_test/singleton/boil_suites_test.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates_test/singleton/boil_suites_test.tpl b/templates_test/singleton/boil_suites_test.tpl index a2ca510..08a5e21 100644 --- a/templates_test/singleton/boil_suites_test.tpl +++ b/templates_test/singleton/boil_suites_test.tpl @@ -336,4 +336,4 @@ func TestUpsert(t *testing.T) { t.Run("{{$tableName}}", test{{$tableName}}Upsert) {{end -}} {{- end -}} -} \ No newline at end of file +} From 874d2ed2e6734f6f91aa014c0ad44dfdba07d638 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Fri, 31 Mar 2017 11:24:11 +0300 Subject: [PATCH 111/179] Fixed type for non-nullable uniqueidentifier, xml --- bdb/drivers/mssql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 9d20d0f..e8a0b5f 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -343,7 +343,7 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { case "timestamp", "rowversion": c.Type = "[]byte" case "uniqueidentifier", "xml": - c.Type = "null.String" + c.Type = "string" default: c.Type = "string" } From dfbcef7fe33c358d5898378bf94584006a20061e Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Fri, 31 Mar 2017 11:27:34 +0300 Subject: [PATCH 112/179] Fixed typo and combined variables declaration --- templates/15_insert.tpl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index bb72404..429154c 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -75,21 +75,20 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string {{end -}} } - queryOutput := "" - qyeryReturning := "" + var queryOutput, queryReturning string if len(cache.retMapping) != 0 { {{if .UseLastInsertID -}} cache.retQuery = fmt.Sprintf("SELECT {{.LQ}}%s{{.RQ}} FROM {{$schemaTable}} WHERE %s", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}"), strmangle.WhereClause("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns)) {{else -}} {{if ne .DriverName "mssql" -}} - qyeryReturning = fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) + queryReturning = fmt.Sprintf(" RETURNING {{.LQ}}%s{{.RQ}}", strings.Join(returnColumns, "{{.RQ}},{{.LQ}}")) {{else -}} queryOutput = fmt.Sprintf("OUTPUT INSERTED.{{.LQ}}%s{{.RQ}} ", strings.Join(returnColumns, "{{.RQ}},INSERTED.{{.LQ}}")) {{end -}} {{- end}} } - cache.query = fmt.Sprintf(cache.query, queryOutput, qyeryReturning) + cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning) } value := reflect.Indirect(reflect.ValueOf(o)) From a4ba4b6e9d903bc8a1157218696fb7bb3ba2a009 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Fri, 31 Mar 2017 11:39:12 +0300 Subject: [PATCH 113/179] Uniqueidentifier now treated as uuid --- bdb/drivers/mssql.go | 10 ++++++++-- randomize/randomize.go | 8 ++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index e8a0b5f..3698069 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -313,8 +313,11 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "null.Bytes" case "timestamp", "rowversion": c.Type = "null.Bytes" - case "uniqueidentifier", "xml": + case "xml": c.Type = "null.String" + case "uniqueidentifier": + c.Type = "null.String" + c.DBType = "uuid" default: c.Type = "null.String" } @@ -342,8 +345,11 @@ func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.Type = "[]byte" case "timestamp", "rowversion": c.Type = "[]byte" - case "uniqueidentifier", "xml": + case "xml": c.Type = "string" + case "uniqueidentifier": + c.Type = "string" + c.DBType = "uuid" default: c.Type = "string" } diff --git a/randomize/randomize.go b/randomize/randomize.go index 2af945d..59f7eff 100644 --- a/randomize/randomize.go +++ b/randomize/randomize.go @@ -52,7 +52,7 @@ var ( rgxValidTime = regexp.MustCompile(`[2-9]+`) validatedTypes = []string{ - "inet", "line", "uuid", "uniqueidentifier", "interval", "mediumint", + "inet", "line", "uuid", "interval", "mediumint", "json", "jsonb", "box", "cidr", "circle", "lseg", "macaddr", "path", "pg_lsn", "point", "polygon", "txid_snapshot", "money", "hstore", @@ -195,7 +195,7 @@ func randomizeField(s *Seed, field reflect.Value, fieldType string, canBeNull bo field.Set(reflect.ValueOf(value)) return nil } - if fieldType == "uuid" || fieldType == "uniqueidentifier" { + if fieldType == "uuid" { value = null.NewString(uuid.NewV4().String(), true) field.Set(reflect.ValueOf(value)) return nil @@ -268,7 +268,7 @@ func randomizeField(s *Seed, field reflect.Value, fieldType string, canBeNull bo field.Set(reflect.ValueOf(value)) return nil } - if fieldType == "uuid" || fieldType == "uniqueidentifier" { + if fieldType == "uuid" { value = uuid.NewV4().String() field.Set(reflect.ValueOf(value)) return nil @@ -390,7 +390,7 @@ func getArrayRandValue(s *Seed, typ reflect.Type, fieldType string) interface{} value := strconv.Itoa((s.nextInt()%26)+2) + " days" return types.StringArray{value, value} } - if fieldType == "uuid" || fieldType == "uniqueidentifier" { + if fieldType == "uuid" { value := uuid.NewV4().String() return types.StringArray{value, value} } From f94fa547e714fe0ab7c2e6dcdc2b00cfd3a7feb0 Mon Sep 17 00:00:00 2001 From: Aric Huang <arichuang@gmail.com> Date: Fri, 31 Mar 2017 15:02:32 -0700 Subject: [PATCH 114/179] (fix) typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2d8ae1..8893bc7 100644 --- a/README.md +++ b/README.md @@ -408,7 +408,7 @@ much benefit over it. **Method 3: Embedding** -This pattern is not for the feint of heart, what it provides in benefits it +This pattern is not for the faint of heart, what it provides in benefits it more than makes up for in downsides. It's possible to embed the SQLBoiler structs inside your own to enhance them. However it's subject to easy breakages and a dependency on these additional objects. It can also introduce From e1dfd0bb1e890cf4d4dfee5e337e5fa264f08361 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 1 Apr 2017 18:59:22 +0300 Subject: [PATCH 115/179] Newline at end of file --- templates_test/upsert.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates_test/upsert.tpl b/templates_test/upsert.tpl index 7e08819..f9c351e 100644 --- a/templates_test/upsert.tpl +++ b/templates_test/upsert.tpl @@ -47,4 +47,4 @@ func test{{$tableNamePlural}}Upsert(t *testing.T) { if count != 1 { t.Error("want one record, got:", count) } -} \ No newline at end of file +} From b6d0a5142ac4db4a3f1e284da1655dcef1ddb70b Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 1 Apr 2017 20:16:05 +0300 Subject: [PATCH 116/179] Removed special case for mssql --- templates_test/insert.tpl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/templates_test/insert.tpl b/templates_test/insert.tpl index 82f87b6..e3446f0 100644 --- a/templates_test/insert.tpl +++ b/templates_test/insert.tpl @@ -41,11 +41,7 @@ func test{{$tableNamePlural}}InsertWhitelist(t *testing.T) { tx := MustTx(boil.Begin()) defer tx.Rollback() - {{if ne .DriverName "mssql"}} - if err = {{$varNameSingular}}.Insert(tx, {{$varNameSingular}}Columns...); err != nil { - {{- else -}} if err = {{$varNameSingular}}.Insert(tx, {{$varNameSingular}}ColumnsWithoutDefault...); err != nil { - {{- end}} t.Error(err) } From 783e7a82b34de24a12c071bd53a3ad72a5da6a78 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 1 Apr 2017 20:30:40 +0300 Subject: [PATCH 117/179] Choosing StartIndex at template time --- templates/16_update.tpl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/templates/16_update.tpl b/templates/16_update.tpl index 2ee87cc..7b6e585 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -171,15 +171,9 @@ func (o {{$tableNameSingular}}Slice) UpdateAll(exec boil.Executor, cols M) error strmangle.Placeholders(dialect.IndexPlaceholders, len(o) * len({{$varNameSingular}}PrimaryKeyColumns), len(colNames)+1, len({{$varNameSingular}}PrimaryKeyColumns)), ) {{- else -}} - startIndex := len(colNames) + 1 - - if !dialect.IndexPlaceholders { - startIndex = 0 - } - sql := fmt.Sprintf("UPDATE {{$schemaTable}} SET %s WHERE %s", strmangle.SetParamNames("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, colNames), - strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), startIndex, {{$varNameSingular}}PrimaryKeyColumns, len(o))) + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}len(colNames)+1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(o))) {{- end}} if boil.DebugMode { From 178d925c24c4fddefbf6c273847775e483e1fd0b Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 1 Apr 2017 21:00:13 +0300 Subject: [PATCH 118/179] Generic Update statement --- templates/16_update.tpl | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/templates/16_update.tpl b/templates/16_update.tpl index 7b6e585..708723e 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -164,17 +164,10 @@ func (o {{$tableNameSingular}}Slice) UpdateAll(exec boil.Executor, cols M) error pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), {{$varNameSingular}}PrimaryKeyMapping) args = append(args, pkeyArgs...) } - {{if ne .DriverName "mssql" -}} - sql := fmt.Sprintf( - "UPDATE {{$schemaTable}} SET %s WHERE ({{.LQ}}{{.Table.PKey.Columns | join (printf "%s,%s" .LQ .RQ)}}{{.RQ}}) IN (%s)", - strmangle.SetParamNames("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, colNames), - strmangle.Placeholders(dialect.IndexPlaceholders, len(o) * len({{$varNameSingular}}PrimaryKeyColumns), len(colNames)+1, len({{$varNameSingular}}PrimaryKeyColumns)), - ) - {{- else -}} + sql := fmt.Sprintf("UPDATE {{$schemaTable}} SET %s WHERE %s", strmangle.SetParamNames("{{.LQ}}", "{{.RQ}}", {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, colNames), strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}len(colNames)+1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(o))) - {{- end}} if boil.DebugMode { fmt.Fprintln(boil.DebugWriter, sql) From f913d5e791b08b3cb862db41ee4a03815f3de0a6 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 1 Apr 2017 21:08:29 +0300 Subject: [PATCH 119/179] Generic Delete statement --- templates/18_delete.tpl | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/templates/18_delete.tpl b/templates/18_delete.tpl index 61282aa..f2ddbb6 100644 --- a/templates/18_delete.tpl +++ b/templates/18_delete.tpl @@ -135,22 +135,8 @@ func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error { args = append(args, pkeyArgs...) } - {{if ne .DriverName "mssql" -}} - sql := fmt.Sprintf( - "DELETE FROM {{$schemaTable}} WHERE (%s) IN (%s)", - strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, {{$varNameSingular}}PrimaryKeyColumns), ","), - strmangle.Placeholders(dialect.IndexPlaceholders, len(o) * len({{$varNameSingular}}PrimaryKeyColumns), 1, len({{$varNameSingular}}PrimaryKeyColumns)), - ) - {{- else -}} - startIndex := 1 - - if !dialect.IndexPlaceholders { - startIndex = 0 - } - - sql := "DELETE FROM {{$schemaTable}} WHERE " + - strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), startIndex, {{$varNameSingular}}PrimaryKeyColumns, len(o)) - {{- end}} + sql := "DELETE FROM {{$schemaTable}} WHERE " + + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(o)) if boil.DebugMode { fmt.Fprintln(boil.DebugWriter, sql) From b2acda8ade28b1ee1c51aaa083bd8a594b586f40 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sat, 1 Apr 2017 21:11:01 +0300 Subject: [PATCH 120/179] Generic Select statement in Reload --- templates/19_reload.tpl | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/templates/19_reload.tpl b/templates/19_reload.tpl index ff656f1..b1201b8 100644 --- a/templates/19_reload.tpl +++ b/templates/19_reload.tpl @@ -79,22 +79,8 @@ func (o *{{$tableNameSingular}}Slice) ReloadAll(exec boil.Executor) error { args = append(args, pkeyArgs...) } - {{if ne .DriverName "mssql" -}} - sql := fmt.Sprintf( - "SELECT {{$schemaTable}}.* FROM {{$schemaTable}} WHERE (%s) IN (%s)", - strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, {{$varNameSingular}}PrimaryKeyColumns), ","), - strmangle.Placeholders(dialect.IndexPlaceholders, len(*o) * len({{$varNameSingular}}PrimaryKeyColumns), 1, len({{$varNameSingular}}PrimaryKeyColumns)), - ) - {{- else -}} - startIndex := 1 - - if !dialect.IndexPlaceholders { - startIndex = 0 - } - sql := "SELECT {{$schemaTable}}.* FROM {{$schemaTable}} WHERE " + - strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), startIndex, {{$varNameSingular}}PrimaryKeyColumns, len(*o)) - {{- end}} + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(*o)) q := queries.Raw(exec, sql, args...) From fd2c7ad764db44de2307fdcc842fe4f83ba5b58e Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sun, 2 Apr 2017 12:27:33 +0300 Subject: [PATCH 121/179] Fixed Upsert statement for postgres, mysql --- templates/17_upsert.tpl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index fb9c2d0..e45a87e 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -79,7 +79,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName nzDefaults, whitelist, ) - {{- if eq .DriverName "mssql"}} + {{if eq .DriverName "mssql" -}} insert = strmangle.SetComplement(insert, {{$varNameSingular}}ColumnsWithAuto) for i, v := range insert { if strmangle.ContainsAny({{$varNameSingular}}PrimaryKeyColumns, v) && strmangle.ContainsAny({{$varNameSingular}}ColumnsWithDefault, v) { @@ -93,17 +93,16 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithAuto) ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithDefault) - {{end -}} + {{end}} update := strmangle.UpdateColumnSet( {{$varNameSingular}}Columns, {{$varNameSingular}}PrimaryKeyColumns, updateColumns, ) - - {{- if eq .DriverName "mssql"}} + {{if eq .DriverName "mssql" -}} update = strmangle.SetComplement(update, {{$varNameSingular}}ColumnsWithAuto) {{end -}} - + if len(update) == 0 { return errors.New("{{.PkgName}}: unable to upsert {{.Table.Name}}, could not build update column list") } @@ -128,7 +127,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName whitelist = append(whitelist, insert...) {{- end}} - cache.valueMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, whitelist) + cache.valueMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, {{if eq .DriverName "mssql"}}whitelist{{else}}insert{{end}}) if err != nil { return err } From 4560da9bd1c0f816d1bf624f762481a986538464 Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Sun, 2 Apr 2017 12:48:29 +0300 Subject: [PATCH 122/179] Fixed comment for TableNames --- bdb/drivers/mssql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 3698069..cb3290a 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -75,7 +75,7 @@ func (m *MSSQLDriver) UseTopClause() bool { // TableNames connects to the postgres database and // retrieves all table names from the information_schema where the -// table schema is public. +// table schema is schema. It uses a whitelist and blacklist. func (m *MSSQLDriver) TableNames(schema string, whitelist, blacklist []string) ([]string, error) { var names []string From 3d1f3fc60936a7d50b0c6e559b645331466f1d28 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 2 Apr 2017 09:40:08 -0700 Subject: [PATCH 123/179] Use different schema names for different drivers --- main.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index a2f9396..acc7b1e 100644 --- a/main.go +++ b/main.go @@ -73,7 +73,7 @@ func main() { // Set up the cobra root command flags rootCmd.PersistentFlags().StringP("output", "o", "models", "The name of the folder to output to") - rootCmd.PersistentFlags().StringP("schema", "s", "public", "The name of your database schema, for databases that support real schemas") + rootCmd.PersistentFlags().StringP("schema", "s", "", "schema name for drivers that support it (default psql: public, mssql: dbo)") rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package") rootCmd.PersistentFlags().StringP("basedir", "", "", "The base directory has the templates and templates_test folders") rootCmd.PersistentFlags().StringSliceP("blacklist", "b", nil, "Do not include these tables in your generated package") @@ -202,6 +202,10 @@ func preRun(cmd *cobra.Command, args []string) error { viper.Set("postgres.port", cmdConfig.Postgres.Port) } + if len(cmdConfig.Schema) == 0 { + cmdConfig.Schema = "public" + } + err = vala.BeginValidation().Validate( vala.StringNotEmpty(cmdConfig.Postgres.User, "postgres.user"), vala.StringNotEmpty(cmdConfig.Postgres.Host, "postgres.host"), @@ -280,6 +284,10 @@ func preRun(cmd *cobra.Command, args []string) error { viper.Set("mssql.port", cmdConfig.MSSQL.Port) } + if len(cmdConfig.Schema) == 0 { + cmdConfig.Schema = "dbo" + } + err = vala.BeginValidation().Validate( vala.StringNotEmpty(cmdConfig.MSSQL.User, "mssql.user"), vala.StringNotEmpty(cmdConfig.MSSQL.Host, "mssql.host"), From 91950e711e186e6911c7129a4de91ca2d0e9355d Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 4 Apr 2017 17:46:51 -0700 Subject: [PATCH 124/179] Add slack badge to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8893bc7..3960d81 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [](https://github.com/vattle/sqlboiler/blob/master/LICENSE) [](https://godoc.org/github.com/vattle/sqlboiler) +[](https://sqlboiler.from-the.cloud) [](https://circleci.com/gh/vattle/sqlboiler) [](http://goreportcard.com/report/vattle/sqlboiler) From d13410617fc6eb2d385808a4ce74f9611b13fd3e Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 4 Apr 2017 19:40:12 -0700 Subject: [PATCH 125/179] Correct whitespace errors --- templates/17_upsert.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index e45a87e..f3d51e7 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -92,7 +92,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithAuto) ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithDefault) - + {{end}} update := strmangle.UpdateColumnSet( {{$varNameSingular}}Columns, @@ -102,7 +102,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName {{if eq .DriverName "mssql" -}} update = strmangle.SetComplement(update, {{$varNameSingular}}ColumnsWithAuto) {{end -}} - + if len(update) == 0 { return errors.New("{{.PkgName}}: unable to upsert {{.Table.Name}}, could not build update column list") } From 10cfe749891641f5002c568684d1a9b913e631cd Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 4 Apr 2017 19:42:49 -0700 Subject: [PATCH 126/179] Fix a bug that could occur on no-field inserts --- templates/15_insert.tpl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 429154c..870e20d 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -86,9 +86,12 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string {{else -}} queryOutput = fmt.Sprintf("OUTPUT INSERTED.{{.LQ}}%s{{.RQ}} ", strings.Join(returnColumns, "{{.RQ}},INSERTED.{{.LQ}}")) {{end -}} - {{- end}} + {{end -}} + } + + if len(wl) != 0 { + cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning) } - cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning) } value := reflect.Indirect(reflect.ValueOf(o)) From 0818af0e2607dc3a9d9f0686d67bac3984855b93 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Tue, 4 Apr 2017 19:44:36 -0700 Subject: [PATCH 127/179] Don't use XSlice where unneeded. - In the bowels of the eager loading we weave in and out of reflection, but we should not care about using XSlice unless it's going back to the user. This change makes it so the XSlice is only used where it matters, everywhere else is *[]*X to avoid type assertion errors from being able to have either or come into the Load() functions. - Fix #124 --- templates/03_finishers.tpl | 2 +- templates/07_relationship_to_one_eager.tpl | 3 +-- templates/08_relationship_one_to_one_eager.tpl | 3 +-- templates/09_relationship_to_many_eager.tpl | 3 +-- templates_test/relationship_one_to_one.tpl | 2 +- templates_test/relationship_to_many.tpl | 2 +- templates_test/relationship_to_one.tpl | 2 +- 7 files changed, 7 insertions(+), 10 deletions(-) diff --git a/templates/03_finishers.tpl b/templates/03_finishers.tpl index 429a276..0cc7e78 100644 --- a/templates/03_finishers.tpl +++ b/templates/03_finishers.tpl @@ -45,7 +45,7 @@ func (q {{$varNameSingular}}Query) AllP() {{$tableNameSingular}}Slice { // All returns all {{$tableNameSingular}} records from the query. func (q {{$varNameSingular}}Query) All() ({{$tableNameSingular}}Slice, error) { - var o {{$tableNameSingular}}Slice + var o []*{{$tableNameSingular}} err := q.Bind(&o) if err != nil { diff --git a/templates/07_relationship_to_one_eager.tpl b/templates/07_relationship_to_one_eager.tpl index 4af84ca..dd1481a 100644 --- a/templates/07_relationship_to_one_eager.tpl +++ b/templates/07_relationship_to_one_eager.tpl @@ -5,7 +5,6 @@ {{- $txt := txtsFromFKey $dot.Tables $dot.Table . -}} {{- $varNameSingular := $dot.Table.Name | singular | camelCase -}} {{- $arg := printf "maybe%s" $txt.LocalTable.NameGo -}} - {{- $slice := printf "%sSlice" $txt.LocalTable.NameGo}} // Load{{$txt.Function.Name}} allows an eager lookup of values, cached into the // loaded structs of the objects. func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singular bool, {{$arg}} interface{}) error { @@ -16,7 +15,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula if singular { object = {{$arg}}.(*{{$txt.LocalTable.NameGo}}) } else { - slice = *{{$arg}}.(*{{$slice}}) + slice = *{{$arg}}.(*[]*{{$txt.LocalTable.NameGo}}) count = len(slice) } diff --git a/templates/08_relationship_one_to_one_eager.tpl b/templates/08_relationship_one_to_one_eager.tpl index 7363b69..37d1af0 100644 --- a/templates/08_relationship_one_to_one_eager.tpl +++ b/templates/08_relationship_one_to_one_eager.tpl @@ -5,7 +5,6 @@ {{- $txt := txtsFromOneToOne $dot.Tables $dot.Table . -}} {{- $varNameSingular := $dot.Table.Name | singular | camelCase -}} {{- $arg := printf "maybe%s" $txt.LocalTable.NameGo -}} - {{- $slice := printf "%sSlice" $txt.LocalTable.NameGo}} // Load{{$txt.Function.Name}} allows an eager lookup of values, cached into the // loaded structs of the objects. func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singular bool, {{$arg}} interface{}) error { @@ -16,7 +15,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula if singular { object = {{$arg}}.(*{{$txt.LocalTable.NameGo}}) } else { - slice = *{{$arg}}.(*{{$slice}}) + slice = *{{$arg}}.(*[]*{{$txt.LocalTable.NameGo}}) count = len(slice) } diff --git a/templates/09_relationship_to_many_eager.tpl b/templates/09_relationship_to_many_eager.tpl index f1a7f5d..1603188 100644 --- a/templates/09_relationship_to_many_eager.tpl +++ b/templates/09_relationship_to_many_eager.tpl @@ -5,7 +5,6 @@ {{- $varNameSingular := $dot.Table.Name | singular | camelCase -}} {{- $txt := txtsFromToMany $dot.Tables $dot.Table . -}} {{- $arg := printf "maybe%s" $txt.LocalTable.NameGo -}} - {{- $slice := printf "%sSlice" $txt.LocalTable.NameGo -}} {{- $schemaForeignTable := .ForeignTable | $dot.SchemaTable}} // Load{{$txt.Function.Name}} allows an eager lookup of values, cached into the // loaded structs of the objects. @@ -17,7 +16,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula if singular { object = {{$arg}}.(*{{$txt.LocalTable.NameGo}}) } else { - slice = *{{$arg}}.(*{{$slice}}) + slice = *{{$arg}}.(*[]*{{$txt.LocalTable.NameGo}}) count = len(slice) } diff --git a/templates_test/relationship_one_to_one.tpl b/templates_test/relationship_one_to_one.tpl index 4e253e5..27c13ef 100644 --- a/templates_test/relationship_one_to_one.tpl +++ b/templates_test/relationship_one_to_one.tpl @@ -50,7 +50,7 @@ func test{{$txt.LocalTable.NameGo}}OneToOne{{$txt.ForeignTable.NameGo}}Using{{$t } slice := {{$txt.LocalTable.NameGo}}Slice{&local} - if err = local.L.Load{{$txt.Function.Name}}(tx, false, &slice); err != nil { + if err = local.L.Load{{$txt.Function.Name}}(tx, false, (*[]*{{$txt.LocalTable.NameGo}})(&slice)); err != nil { t.Fatal(err) } if local.R.{{$txt.Function.Name}} == nil { diff --git a/templates_test/relationship_to_many.tpl b/templates_test/relationship_to_many.tpl index 0080688..0402609 100644 --- a/templates_test/relationship_to_many.tpl +++ b/templates_test/relationship_to_many.tpl @@ -87,7 +87,7 @@ func test{{$txt.LocalTable.NameGo}}ToMany{{$txt.Function.Name}}(t *testing.T) { } slice := {{$txt.LocalTable.NameGo}}Slice{&a} - if err = a.L.Load{{$txt.Function.Name}}(tx, false, &slice); err != nil { + if err = a.L.Load{{$txt.Function.Name}}(tx, false, (*[]*{{$txt.LocalTable.NameGo}})(&slice)); err != nil { t.Fatal(err) } if got := len(a.R.{{$txt.Function.Name}}); got != 2 { diff --git a/templates_test/relationship_to_one.tpl b/templates_test/relationship_to_one.tpl index 3c66bf7..e95c61a 100644 --- a/templates_test/relationship_to_one.tpl +++ b/templates_test/relationship_to_one.tpl @@ -50,7 +50,7 @@ func test{{$txt.LocalTable.NameGo}}ToOne{{$txt.ForeignTable.NameGo}}Using{{$txt. } slice := {{$txt.LocalTable.NameGo}}Slice{&local} - if err = local.L.Load{{$txt.Function.Name}}(tx, false, &slice); err != nil { + if err = local.L.Load{{$txt.Function.Name}}(tx, false, (*[]*{{$txt.LocalTable.NameGo}})(&slice)); err != nil { t.Fatal(err) } if local.R.{{$txt.Function.Name}} == nil { From 91a798d9afe9c1479620cae5d1132bfae5b42619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Mrozek?= <Michsior14@users.noreply.github.com> Date: Sun, 16 Apr 2017 20:00:43 +0200 Subject: [PATCH 128/179] Update global.go --- boil/global.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boil/global.go b/boil/global.go index f357d61..03c574b 100644 --- a/boil/global.go +++ b/boil/global.go @@ -1,6 +1,7 @@ package boil import ( + "io" "os" "time" ) @@ -20,7 +21,7 @@ var ( var DebugMode = false // DebugWriter is where the debug output will be sent if DebugMode is true -var DebugWriter = os.Stdout +var DebugWriter io.Writer = os.Stdout // SetDB initializes the database handle for all template db interactions func SetDB(db Executor) { From 5a3389441299cbd2d42b17f9b5ff45a53bee9a9a Mon Sep 17 00:00:00 2001 From: Aaron <aaron@bettercoder.net> Date: Sun, 16 Apr 2017 11:57:06 -0700 Subject: [PATCH 129/179] Add contribution guidelines --- CONTRIBUTING.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8ce4412 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,60 @@ +# Contributing + +Thanks for your interest in contributing to SQLBoiler! + +We have a very lightweight process and aim to keep it that way. +Read the sections for the piece you're interested in and go from +there. + +If you need quick communication we're usually on [Slack](https://sqlboiler.from-the.cloud). + +# New Code / Features + +## Small Change + +#### TLDR + +1. Open PR against **dev** branch with explanation +1. Participate in Github Code Review + +#### Long version + +For code that requires little to no discussion, please just open a pull request with some +explanation against the **dev** branch. All code goes through dev before going out in a release. + +## Bigger Change + +#### TLDR + +1. Start proposal of idea in Github issue +1. After design concensus, open PR with the work against the **dev** branch +1. Participate in Github Code Review + +#### Long version + +If however you're working on something bigger, it's usually better to check with us on the idea +before starting on a pull request, just so there's no time wasted in redoing/refactoring or being +outright rejected because the PR is at odds with the design. The best way to accomplish this is to +open an issue to discuss it. It can always start as a Slack conversation but should eventually end +up as an issue to avoid penalizing the rest of the users for not being on Slack. Once we agree on +the way to do something, then open the PR against the **dev** branch and we'll commence code review +with the Github code review tools. Then it will be merged into dev, and later go out in a release. + +# Bugs + +Issues should be filed on Github, simply use the template provided and fill in detail. If there's +more information you feel you should give use your best judgement and add it in, the more the better. +See the section below for information on providing database schemas. + +Bugs that have responses from contributors but no action from those who opened them after a time +will be closed with the comment: "Stale" + +## Schemas + +A database schema can help us fix generation issues very quickly. However not everyone is willing to part +with their database schema for various reasons and that's fine. Instead of providing the schema please +then provide a subset of your database (you can munge the names so as to be unrecognizable) that can +help us reproduce the problem. + +*Note:* Your schema information is included in the output from `--debug`, so be careful giving this +information out publicly on a Github issue if you're sensitive about this. From 6a0817d37db9533d16a0a35deb3333037859ba70 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 23 Apr 2017 14:28:21 -0700 Subject: [PATCH 130/179] Update benchmark section --- README.md | 119 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 3960d81..214926d 100644 --- a/README.md +++ b/README.md @@ -1253,51 +1253,78 @@ generator is located at: https://github.com/vattle/sqlboiler If you'd like to run the benchmarks yourself check out our [boilbench](https://github.com/vattle/boilbench) repo. -Here are the results (lower is better): - -`go test -bench . -benchmem` -``` -BenchmarkGORMDelete/gorm-8 100000 15364 ns/op 5395 B/op 113 allocs/op -BenchmarkGORPDelete/gorp-8 1000000 1703 ns/op 304 B/op 12 allocs/op -BenchmarkXORMDelete/xorm-8 100000 14733 ns/op 3634 B/op 107 allocs/op -BenchmarkBoilDelete/boil-8 2000000 986 ns/op 120 B/op 7 allocs/op - -BenchmarkGORMInsert/gorm-8 100000 19197 ns/op 8054 B/op 161 allocs/op -BenchmarkGORPInsert/gorp-8 500000 3413 ns/op 1008 B/op 32 allocs/op -BenchmarkXORMInsert/xorm-8 100000 15428 ns/op 5836 B/op 131 allocs/op -BenchmarkBoilInsert/boil-8 500000 3041 ns/op 568 B/op 21 allocs/op - -BenchmarkGORMSelectAll/gorm-8 20000 85422 ns/op 29912 B/op 511 allocs/op -BenchmarkGORPSelectAll/gorp-8 50000 35824 ns/op 8837 B/op 312 allocs/op -BenchmarkXORMSelectAll/xorm-8 30000 58843 ns/op 13805 B/op 298 allocs/op -BenchmarkBoilSelectAll/boil-8 100000 13844 ns/op 2840 B/op 61 allocs/op - -BenchmarkGORMSelectSubset/gorm-8 10000 100714 ns/op 30875 B/op 517 allocs/op -BenchmarkGORPSelectSubset/gorp-8 30000 43547 ns/op 8837 B/op 312 allocs/op -BenchmarkXORMSelectSubset/xorm-8 30000 48128 ns/op 12989 B/op 282 allocs/op -BenchmarkBoilSelectSubset/boil-8 100000 12316 ns/op 2977 B/op 65 allocs/op - -BenchmarkGORMSelectComplex/gorm-8 10000 133598 ns/op 49398 B/op 772 allocs/op -BenchmarkGORPSelectComplex/gorp-8 50000 40588 ns/op 9037 B/op 321 allocs/op -BenchmarkXORMSelectComplex/xorm-8 30000 56367 ns/op 14174 B/op 313 allocs/op -BenchmarkBoilSelectComplex/boil-8 100000 16941 ns/op 3821 B/op 95 allocs/op - -BenchmarkGORMUpdate/gorm-8 50000 25406 ns/op 9710 B/op 195 allocs/op -BenchmarkGORPUpdate/gorp-8 300000 3614 ns/op 1152 B/op 34 allocs/op -BenchmarkXORMUpdate/xorm-8 100000 17510 ns/op 4458 B/op 132 allocs/op -BenchmarkBoilUpdate/boil-8 500000 2958 ns/op 520 B/op 16 allocs/op - -BenchmarkGORMRawBind/gorm-8 10000 112577 ns/op 38270 B/op 595 allocs/op -BenchmarkGORPRawBind/gorp-8 30000 40967 ns/op 8837 B/op 312 allocs/op -BenchmarkXORMRawBind/xorm-8 30000 54739 ns/op 12692 B/op 273 allocs/op -BenchmarkSQLXRawBind/sqlx-8 200000 13537 ns/op 4268 B/op 49 allocs/op -BenchmarkBoilRawBind/boil-8 200000 11144 ns/op 4334 B/op 49 allocs/op +```bash +go test -bench . -benchmem ``` -<img style="margin-right:6px;" src="http://i.imgur.com/TglZGoI.png"/> -<img style="margin-right:6px;" src="http://i.imgur.com/Ktm2ta4.png"/> -<img style="margin-right:6px;" src="http://i.imgur.com/yv8kFPA.png"/> -<img style="margin-right:6px;" src="http://i.imgur.com/890Zswe.png"/> -<img style="margin-right:6px;" src="http://i.imgur.com/qMgoAFJ.png"/> -<img style="margin-right:6px;" src="http://i.imgur.com/sDoNiCN.png"/> -<img style="margin-right:6px;" src="http://i.imgur.com/EvUa4UT.png"/> +### Results (lower is better) + +Test machine: +```text +OS: Ubuntu 16.04 +CPU: Intel(R) Core(TM) i7-4771 CPU @ 3.50GHz +Mem: 16GB +Go: go version go1.8.1 linux/amd64 +``` + +The graphs below have many runs like this as input to calculate errors. Here +is a sample run: + +```text +BenchmarkGORMSelectAll/gorm-8 20000 66500 ns/op 28998 B/op 455 allocs/op +BenchmarkGORPSelectAll/gorp-8 50000 31305 ns/op 9141 B/op 318 allocs/op +BenchmarkXORMSelectAll/xorm-8 20000 66074 ns/op 16317 B/op 417 allocs/op +BenchmarkKallaxSelectAll/kallax-8 100000 18278 ns/op 7428 B/op 145 allocs/op +BenchmarkBoilSelectAll/boil-8 100000 12759 ns/op 3145 B/op 67 allocs/op + +BenchmarkGORMSelectSubset/gorm-8 20000 69469 ns/op 30008 B/op 462 allocs/op +BenchmarkGORPSelectSubset/gorp-8 50000 31102 ns/op 9141 B/op 318 allocs/op +BenchmarkXORMSelectSubset/xorm-8 20000 64151 ns/op 15933 B/op 414 allocs/op +BenchmarkKallaxSelectSubset/kallax-8 100000 16996 ns/op 6499 B/op 132 allocs/op +BenchmarkBoilSelectSubset/boil-8 100000 13579 ns/op 3281 B/op 71 allocs/op + +BenchmarkGORMSelectComplex/gorm-8 20000 76284 ns/op 34566 B/op 521 allocs/op +BenchmarkGORPSelectComplex/gorp-8 50000 31886 ns/op 9501 B/op 328 allocs/op +BenchmarkXORMSelectComplex/xorm-8 20000 68430 ns/op 17694 B/op 464 allocs/op +BenchmarkKallaxSelectComplex/kallax-8 50000 26095 ns/op 10293 B/op 212 allocs/op +BenchmarkBoilSelectComplex/boil-8 100000 16403 ns/op 4205 B/op 102 allocs/op + +BenchmarkGORMDelete/gorm-8 200000 10356 ns/op 5059 B/op 98 allocs/op +BenchmarkGORPDelete/gorp-8 1000000 1335 ns/op 352 B/op 13 allocs/op +BenchmarkXORMDelete/xorm-8 200000 10796 ns/op 4146 B/op 122 allocs/op +BenchmarkKallaxDelete/kallax-8 300000 5141 ns/op 2241 B/op 48 allocs/op +BenchmarkBoilDelete/boil-8 2000000 796 ns/op 168 B/op 8 allocs/op + +BenchmarkGORMInsert/gorm-8 100000 15238 ns/op 8278 B/op 150 allocs/op +BenchmarkGORPInsert/gorp-8 300000 4648 ns/op 1616 B/op 38 allocs/op +BenchmarkXORMInsert/xorm-8 100000 12600 ns/op 6092 B/op 138 allocs/op +BenchmarkKallaxInsert/kallax-8 100000 15115 ns/op 6003 B/op 126 allocs/op +BenchmarkBoilInsert/boil-8 1000000 2249 ns/op 984 B/op 23 allocs/op + +BenchmarkGORMUpdate/gorm-8 100000 18609 ns/op 9389 B/op 174 allocs/op +BenchmarkGORPUpdate/gorp-8 500000 3180 ns/op 1536 B/op 35 allocs/op +BenchmarkXORMUpdate/xorm-8 100000 13149 ns/op 5098 B/op 149 allocs/op +BenchmarkKallaxUpdate/kallax-8 100000 22880 ns/op 11366 B/op 219 allocs/op +BenchmarkBoilUpdate/boil-8 1000000 1810 ns/op 936 B/op 18 allocs/op + +BenchmarkGORMRawBind/gorm-8 20000 65821 ns/op 30502 B/op 444 allocs/op +BenchmarkGORPRawBind/gorp-8 50000 31300 ns/op 9141 B/op 318 allocs/op +BenchmarkXORMRawBind/xorm-8 20000 62024 ns/op 15588 B/op 403 allocs/op +BenchmarkKallaxRawBind/kallax-8 200000 7843 ns/op 4380 B/op 46 allocs/op +BenchmarkSQLXRawBind/sqlx-8 100000 13056 ns/op 4572 B/op 55 allocs/op +BenchmarkBoilRawBind/boil-8 200000 11519 ns/op 4638 B/op 55 allocs/op +``` + +<img src="http://i.imgur.com/SltE8UQ.png"/><img src="http://i.imgur.com/lzvM5jJ.png"/><img src="http://i.imgur.com/SS0zNd2.png"/> + +<img src="http://i.imgur.com/Kk0IM0J.png"/><img src="http://i.imgur.com/1IFtpdP.png"/><img src="http://i.imgur.com/t6Usecx.png"/> + +<img src="http://i.imgur.com/98DOzcr.png"/><img src="http://i.imgur.com/NSp5r4Q.png"/><img src="http://i.imgur.com/dEGlOgI.png"/> + +<img src="http://i.imgur.com/W0zhuGb.png"/><img src="http://i.imgur.com/YIvDuFv.png"/><img src="http://i.imgur.com/sKwuMaU.png"/> + +<img src="http://i.imgur.com/ZUMYVmw.png"/><img src="http://i.imgur.com/T61rH3K.png"/><img src="http://i.imgur.com/lDr0xhY.png"/> + +<img src="http://i.imgur.com/LWo10M9.png"/><img src="http://i.imgur.com/Td15owT.png"/><img src="http://i.imgur.com/45XXw4K.png"/> + +<img src="http://i.imgur.com/lpP8qds.png"/><img src="http://i.imgur.com/hLyH3jQ.png"/><img src="http://i.imgur.com/C2v10t3.png"/> From e4d1e606fdfd88dd07f1f9dffbf7a9defc230e5c Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 2 Apr 2017 10:00:42 -0700 Subject: [PATCH 131/179] CI for mssql - Fix a bug where mysql was never tested in CI (hah!) --- circle.yml | 222 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 196 insertions(+), 26 deletions(-) diff --git a/circle.yml b/circle.yml index ed75a12..68bc895 100644 --- a/circle.yml +++ b/circle.yml @@ -1,28 +1,198 @@ -test: - pre: - - mkdir -p /home/ubuntu/.go_workspace/src/github.com/jstemmer - - go get -u github.com/jstemmer/go-junit-report - - echo -e "[postgres]\nhost=\"localhost\"\nport=5432\nuser=\"ubuntu\"\ndbname=\"sqlboiler\"\n[mysql]\nhost=\"localhost\"\nport=3306\nuser=\"ubuntu\"\ndbname=\"sqlboiler\"\nsslmode=\"false\"" > sqlboiler.toml - - createdb -U ubuntu sqlboiler - - psql -U ubuntu sqlboiler < ./testdata/postgres_test_schema.sql - - echo "create database sqlboiler;" | mysql -u ubuntu - - mysql -u ubuntu sqlboiler < ./testdata/mysql_test_schema.sql - - ./sqlboiler postgres -o "postgres" - - ./sqlboiler postgres -o "mysql" - override: - - go test -v -race ./... > $CIRCLE_ARTIFACTS/gotest.txt - post: - - cat $CIRCLE_ARTIFACTS/gotest.txt | go-junit-report > $CIRCLE_TEST_REPORTS/junit.xml +version: 2 +jobs: + build: + working_directory: /root + docker: + - image: aarondl0/sqlboiler-test:latest -machine: - environment: - GODIST: "go1.7.linux-amd64.tar.gz" - post: - - mkdir -p download - - test -e download/$GODIST || curl -o download/$GODIST https://storage.googleapis.com/golang/$GODIST - - sudo rm -rf /usr/local/go - - sudo tar -C /usr/local -xzf download/$GODIST + - image: postgres:9.6 + environment: + POSTGRES_PASSWORD: psqlpassword -dependencies: - cache_directories: - - ~/download + - image: mysql:5.7 + environment: + MYSQL_ROOT_PASSWORD: mysqlpassword + + - image: microsoft/mssql-server-linux:ctp1-4 + environment: + ACCEPT_EULA: 'Y' + SA_PASSWORD: 'R@@tr@@t1234' + + environment: + GOPATH: /go + ROOTPATH: /go/src/github.com/vattle/sqlboiler + + steps: + - run: + name: Add PSQL Creds + command: | + echo "*:*:*:*:psqlpassword" > /root/.pgpass + chmod 600 /root/.pgpass + - run: + name: Add MySQL Creds + command: | + echo -e "[client]\nuser = root\npassword = mysqlpassword\nhost = localhost\nprotocol = tcp" > /root/.my.cnf + chmod 600 /root/.my.cnf + + - run: + name: Wait for PSQL + command: > + for i in `seq 30`; do + echo "Waiting for psql" + set +o errexit + psql --host localhost --username postgres --dbname template1 -c 'select * from information_schema.tables;' > /dev/null + status=$? + set -o errexit + if [ $status -eq 0 ]; then + break + fi + if [ $i -eq 30 ]; then + echo "Failed to wait for psql" + exit 1 + fi + sleep 1 + done + + - run: + name: Wait for MySQL + command: > + for i in `seq 30`; do + echo "Waiting for mysql" + set +o errexit + mysql --execute 'select * from information_schema.tables;' > /dev/null + status=$? + set -o errexit + if [ $status -eq 0 ]; then + break + fi + if [ $i -eq 30 ]; then + echo "Failed to wait for mysql" + exit 1 + fi + sleep 1 + done + + - run: + name: Wait for MSSQL + command: > + for i in `seq 30`; do + echo "Waiting for mssql" + set +o errexit + sqlcmd -H localhost -U sa -P R@@tr@@t1234 -Q "select * from information_schema.tables;" > /dev/null + status=$? + set -o errexit + if [ $status -eq 0 ]; then + break + fi + if [ $i -eq 30 ]; then + echo "Failed to wait for mssql" + exit 1 + fi + sleep 1 + done + + - run: + name: Make GOPATH + command: mkdir -p /go/src/github.com/vattle/sqlboiler + + - checkout: + path: /go/src/github.com/vattle/sqlboiler + + - run: + name: Create PSQL DB + command: | + createdb --host localhost --username postgres --owner postgres sqlboiler + psql --host localhost --username postgres --dbname sqlboiler < $ROOTPATH/testdata/postgres_test_schema.sql + - run: + name: Create MySQL DB + command: | + mysql --host localhost --execute 'create database sqlboiler;' + mysql --host localhost --database sqlboiler < $ROOTPATH/testdata/mysql_test_schema.sql + - run: + name: Create MSSQL DB + command: | + sqlcmd -S localhost -U sa -P R@@tr@@t1234 -Q "create database sqlboiler;" + sqlcmd -S localhost -U sa -P R@@tr@@t1234 -d sqlboiler -i $ROOTPATH/testdata/mssql_test_schema.sql + + - run: + name: Build SQLBoiler + command: | + cd $ROOTPATH; go get -v + cd $ROOTPATH; go build -v github.com/vattle/sqlboiler + + - run: + name: Configure SQLBoiler: PSQL + command: echo -e '[postgres]\nhost="localhost"\nport=5432\nuser="postgres"\ndbname="sqlboiler"\n' > $ROOTPATH/sqlboiler.toml + - run: + name: Configure SQLBoiler: MySQL + command: echo -e '[mysql]\nhost="localhost"\nport=3306\nuser="root"\ndbname="sqlboiler"\nsslmode="false"\n' >> $ROOTPATH/sqlboiler.toml + - run: + name: Configure SQLBoiler: MSSQL + command: echo -e '[mssql]\nhost="localhost"\nport=1433\nuser="sa"\npassword="R@@tr@@t1234"\ndbname="sqlboiler"\nsslmode="disable"\n' >> $ROOTPATH/sqlboiler.toml + + - run: + name: Generate: PSQL + command: cd $ROOTPATH; ./sqlboiler -o postgres postgres + - run: + name: Generate: MySQL + command: cd $ROOTPATH; ./sqlboiler -o mysql mysql + - run: + name: Generate: MSSQL + command: cd $ROOTPATH; ./sqlboiler -o mssql mssql + + - run: + name: Test + command: | + cd $ROOTPATH + cp ./testdata/mssql_test_schema.sql mssql/tables_schema.sql + mkdir -p test_results/go + go test -v -race ./... > test_out.txt + cat test_out | go-junit-report > $ROOTPATH/test_results/go/out.xml + + - store_test_results: + path: $ROOTPATH/test_results +#test: +# pre: +# - echo -e "[postgres]\nhost=\"localhost\"\nport=5432\nuser=\"ubuntu\"\ndbname=\"sqlboiler\"\n" > sqlboiler.toml +# - createdb -U ubuntu sqlboiler +# - psql -U ubuntu sqlboiler < ./testdata/postgres_test_schema.sql +# +# - echo -e "[mysql]\nhost=\"localhost\"\nport=3306\nuser=\"ubuntu\"\ndbname=\"sqlboiler\"\nsslmode=\"false\"\n" >> sqlboiler.toml +# - echo "create database sqlboiler;" | mysql -u ubuntu +# - mysql -u ubuntu sqlboiler < ./testdata/mysql_test_schema.sql +# +# - echo -e "[mssql]\nhost=\"localhost\"\nport=1433\nuser=\"sa\"\ndbname=\"sqlboiler\"\nsslmode=\"disable\"\n" >> sqlboiler.toml +# - docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=R@@tr@@t1234' -p 1433:1433 -d --name mssql microsoft/mssql-server-linux +# - sqlcmd -S localhost -U sa -P R@@tr@@t1234 -Q "create database sqlboiler;" +# - sqlcmd -S localhost -U sa -P R@@tr@@t1234 -d sqlboiler -i ./testdata/mssql_test_schema.sql +# +# - ./sqlboiler -o postgres postgres +# - ./sqlboiler -o mysql mysql +# - ./sqlboiler -o mssql mssql +# - cp ./testdata/mssql_test_schema.sql mssql/tables_schema.sql +# override: +# - go test -v -race ./... > $CIRCLE_ARTIFACTS/gotest.txt +# post: +# - cat $CIRCLE_ARTIFACTS/gotest.txt | go-junit-report > $CIRCLE_TEST_REPORTS/junit.xml +# +#machine: +# environment: +# GODIST: go1.7.linux-amd64.tar.gz +# PATH: /home/ubuntu/.go_workspace/bin:/usr/local/go/bin:/home/ubuntu/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/mssql-tools/bin +# post: +# - mkdir -p download +# - test -e download/$GODIST || curl -o download/$GODIST https://storage.googleapis.com/golang/$GODIST +# - sudo rm -rf /usr/local/go +# - sudo tar -C /usr/local -xzf download/$GODIST +# +#dependencies: +# pre: +# - mkdir -p /home/ubuntu/.go_workspace/src/github.com/jstemmer +# - go get -u github.com/jstemmer/go-junit-report +# +# - curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - +# - curl https://packages.microsoft.com/config/ubuntu/14.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list +# - sudo apt-get update; sudo apt-get install mssql-tools unixodbc-dev +# - docker pull microsoft/mssql-server-linux +# cache_directories: +# - ~/download From 3a38ed1f7af36d026cd8532e10104c9568e584f0 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 23 Apr 2017 16:14:37 -0700 Subject: [PATCH 132/179] Add Dockerfile --- testdata/Dockerfile | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 testdata/Dockerfile diff --git a/testdata/Dockerfile b/testdata/Dockerfile new file mode 100644 index 0000000..034cedc --- /dev/null +++ b/testdata/Dockerfile @@ -0,0 +1,35 @@ +# This Dockerfile builds the image used for CI/testing. +FROM ubuntu:16.04 + +ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/opt/mssql-tools/bin +ENV GODIST go1.8.linux-amd64.tar.gz + +# Set up locales for sqlcmd (otherwise it breaks) +RUN locale-gen en_US.UTF-8 \ + && echo "LC_ALL=en_US.UTF-8" >> /etc/default/locale \ + && echo "LANG=en_US.UTF-8" >> /etc/default/locale + +# Install bootstrap-y tools +RUN apt-get update \ + && apt-get install -y apt-transport-https software-properties-common python3-software-properties \ + && apt-add-repository ppa:git-core/ppa \ + && apt-get update \ + && apt-get install -y curl git + +# Install database clients +# MySQL 8.0 is still in development, so we're using 5.7 which is already +# available in Ubuntu 16.04 +RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ + && echo 'deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main' > /etc/apt/sources.list.d/psql.list \ + && curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \ + && curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/msprod.list \ + && apt-get update \ + && env ACCEPT_EULA=Y apt-get install -y git postgresql-client-9.6 mysql-client-5.7 mssql-tools unixodbc-dev + +# Install Go +RUN curl -o $GODIST https://storage.googleapis.com/golang/$GODIST \ + && rm -rf /usr/local/go \ + && tar -C /usr/local -xzf $GODIST + +RUN go get -u -v github.com/jstemmer/go-junit-report \ + && mv /root/go/bin/go-junit-report /usr/bin/go-junit-report From 5ee4b06c9b60caaad9633a0909c81f92a2444d03 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 23 Apr 2017 16:16:07 -0700 Subject: [PATCH 133/179] Move CircleCI file to correct spot --- circle.yml => .circleci/config.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename circle.yml => .circleci/config.yml (100%) diff --git a/circle.yml b/.circleci/config.yml similarity index 100% rename from circle.yml rename to .circleci/config.yml From 85c9104d3494731dcd913889f5cb5c9551f8141a Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 23 Apr 2017 16:17:46 -0700 Subject: [PATCH 134/179] Fix syntax error --- .circleci/config.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 68bc895..e146724 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -121,27 +121,27 @@ jobs: cd $ROOTPATH; go build -v github.com/vattle/sqlboiler - run: - name: Configure SQLBoiler: PSQL + name: 'Configure SQLBoiler: PSQL' command: echo -e '[postgres]\nhost="localhost"\nport=5432\nuser="postgres"\ndbname="sqlboiler"\n' > $ROOTPATH/sqlboiler.toml - run: - name: Configure SQLBoiler: MySQL + name: 'Configure SQLBoiler: MySQL' command: echo -e '[mysql]\nhost="localhost"\nport=3306\nuser="root"\ndbname="sqlboiler"\nsslmode="false"\n' >> $ROOTPATH/sqlboiler.toml - run: - name: Configure SQLBoiler: MSSQL + name: 'Configure SQLBoiler: MSSQL' command: echo -e '[mssql]\nhost="localhost"\nport=1433\nuser="sa"\npassword="R@@tr@@t1234"\ndbname="sqlboiler"\nsslmode="disable"\n' >> $ROOTPATH/sqlboiler.toml - run: - name: Generate: PSQL + name: 'Generate: PSQL' command: cd $ROOTPATH; ./sqlboiler -o postgres postgres - run: - name: Generate: MySQL + name: 'Generate: MySQL' command: cd $ROOTPATH; ./sqlboiler -o mysql mysql - run: - name: Generate: MSSQL + name: 'Generate: MSSQL' command: cd $ROOTPATH; ./sqlboiler -o mssql mssql - run: - name: Test + name: Run Tests command: | cd $ROOTPATH cp ./testdata/mssql_test_schema.sql mssql/tables_schema.sql From 36ae141304a916b7b2bdea2bd36f592c2c68f782 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 23 Apr 2017 16:22:10 -0700 Subject: [PATCH 135/179] Turn off PSQL SSL in tests --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e146724..dcd26c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -122,7 +122,7 @@ jobs: - run: name: 'Configure SQLBoiler: PSQL' - command: echo -e '[postgres]\nhost="localhost"\nport=5432\nuser="postgres"\ndbname="sqlboiler"\n' > $ROOTPATH/sqlboiler.toml + command: echo -e '[postgres]\nhost="localhost"\nport=5432\nuser="postgres"\ndbname="sqlboiler"\nsslmode="disable"\n' > $ROOTPATH/sqlboiler.toml - run: name: 'Configure SQLBoiler: MySQL' command: echo -e '[mysql]\nhost="localhost"\nport=3306\nuser="root"\ndbname="sqlboiler"\nsslmode="false"\n' >> $ROOTPATH/sqlboiler.toml From 0b44c26a264f4b38cdab07666d3fa3f897d70378 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 23 Apr 2017 16:27:32 -0700 Subject: [PATCH 136/179] Add passwords to psql/mysql --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dcd26c2..94dc610 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -122,13 +122,13 @@ jobs: - run: name: 'Configure SQLBoiler: PSQL' - command: echo -e '[postgres]\nhost="localhost"\nport=5432\nuser="postgres"\ndbname="sqlboiler"\nsslmode="disable"\n' > $ROOTPATH/sqlboiler.toml + command: echo -e '[postgres]\nhost="localhost"\nport=5432\nuser="postgres"\npass="psqlpassword"\ndbname="sqlboiler"\nsslmode="disable"\n' > $ROOTPATH/sqlboiler.toml - run: name: 'Configure SQLBoiler: MySQL' - command: echo -e '[mysql]\nhost="localhost"\nport=3306\nuser="root"\ndbname="sqlboiler"\nsslmode="false"\n' >> $ROOTPATH/sqlboiler.toml + command: echo -e '[mysql]\nhost="localhost"\nport=3306\nuser="root"\npass="mysqlpassword"\ndbname="sqlboiler"\nsslmode="false"\n' >> $ROOTPATH/sqlboiler.toml - run: name: 'Configure SQLBoiler: MSSQL' - command: echo -e '[mssql]\nhost="localhost"\nport=1433\nuser="sa"\npassword="R@@tr@@t1234"\ndbname="sqlboiler"\nsslmode="disable"\n' >> $ROOTPATH/sqlboiler.toml + command: echo -e '[mssql]\nhost="localhost"\nport=1433\nuser="sa"\npass="R@@tr@@t1234"\ndbname="sqlboiler"\nsslmode="disable"\n' >> $ROOTPATH/sqlboiler.toml - run: name: 'Generate: PSQL' From 7de817b111412e574cd1ac8b1bf13a49cc4a56da Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 23 Apr 2017 16:43:41 -0700 Subject: [PATCH 137/179] Add dep fetch to tests part --- .circleci/config.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 94dc610..d233e6a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -117,7 +117,7 @@ jobs: - run: name: Build SQLBoiler command: | - cd $ROOTPATH; go get -v + cd $ROOTPATH; go get -v -t cd $ROOTPATH; go build -v github.com/vattle/sqlboiler - run: @@ -140,17 +140,27 @@ jobs: name: 'Generate: MSSQL' command: cd $ROOTPATH; ./sqlboiler -o mssql mssql + - run: + name: Download generated and test deps + command: | + cd $ROOTPATH + go get -v -t ./... + - run: name: Run Tests command: | cd $ROOTPATH cp ./testdata/mssql_test_schema.sql mssql/tables_schema.sql - mkdir -p test_results/go - go test -v -race ./... > test_out.txt - cat test_out | go-junit-report > $ROOTPATH/test_results/go/out.xml + go test -v -race ./... | tee test_out.txt + + - run: + name: Convert test output to JUNIT + command: | + mkdir -p $HOME/test_results/go + cat $ROOTPATH/test_out.txt | go-junit-report > $HOME/test_results/go/out.xml - store_test_results: - path: $ROOTPATH/test_results + path: test_results #test: # pre: # - echo -e "[postgres]\nhost=\"localhost\"\nport=5432\nuser=\"ubuntu\"\ndbname=\"sqlboiler\"\n" > sqlboiler.toml From bdd28d9d5b9b48bbb3c538dfcf4d1043a76ac240 Mon Sep 17 00:00:00 2001 From: Maksim <maksim.levental@gmail.com> Date: Mon, 24 Apr 2017 12:46:56 -0400 Subject: [PATCH 138/179] fix removing whitespace --- templates/16_update.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/16_update.tpl b/templates/16_update.tpl index 708723e..50c7f49 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -56,7 +56,7 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string {{if eq .DriverName "mssql"}} wl = strmangle.SetComplement(wl, {{$varNameSingular}}ColumnsWithAuto) {{end}} - {{- if not .NoAutoTimestamps}} + {{if not .NoAutoTimestamps}} if len(whitelist) == 0 { wl = strmangle.SetComplement(wl, []string{"created_at"}) } From a3442a5ab499510227f9b4cea5d5280c6227569a Mon Sep 17 00:00:00 2001 From: Sergey Kurt <sergeykurt@outlook.com> Date: Fri, 28 Apr 2017 23:58:17 +0300 Subject: [PATCH 139/179] Fixed INSERT-SELECT deadlocks in tests --- templates_test/main_test/mssql_main.tpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates_test/main_test/mssql_main.tpl b/templates_test/main_test/mssql_main.tpl index c1c76f2..ba46be7 100644 --- a/templates_test/main_test/mssql_main.tpl +++ b/templates_test/main_test/mssql_main.tpl @@ -68,6 +68,9 @@ func (m *mssqlTester) sslMode(mode string) string { func (m *mssqlTester) createTestDB() error { sql := fmt.Sprintf(` CREATE DATABASE %s; + GO + ALTER DATABASE %[1]s + SET READ_COMMITTED_SNAPSHOT ON; GO`, m.testDBName) return m.runCmd(sql, "sqlcmd", "-S", m.host, "-U", m.user, "-P", m.pass) } From ecad5334fbcd1f8e3ebcc686efa91250380af0de Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Fri, 28 Apr 2017 20:01:27 -0700 Subject: [PATCH 140/179] Fix MySQL protocol selection - If MySQL host exists on the filesystem as a file, then expect that this is actually a unix socket. If not, add protocol=tcp to force the mysql client to use tcp otherwise it tries unix sockets for no reason. Thanks MySQL. --- templates_test/main_test/mysql_main.tpl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/templates_test/main_test/mysql_main.tpl b/templates_test/main_test/mysql_main.tpl index fc43d3d..b95b32c 100644 --- a/templates_test/main_test/mysql_main.tpl +++ b/templates_test/main_test/mysql_main.tpl @@ -90,12 +90,23 @@ func (m *mysqlTester) makeOptionFile() error { return errors.Wrap(err, "failed to create option file") } + isTCP := false + _, err = os.Stat(m.host) + if os.IsNotExist(err) { + isTCP = true + } else if err != nil { + return errors.Wrap(err, "could not stat m.host") + } + fmt.Fprintln(tmp, "[client]") fmt.Fprintf(tmp, "host=%s\n", m.host) fmt.Fprintf(tmp, "port=%d\n", m.port) fmt.Fprintf(tmp, "user=%s\n", m.user) fmt.Fprintf(tmp, "password=%s\n", m.pass) fmt.Fprintf(tmp, "ssl-mode=%s\n", m.sslMode(m.sslmode)) + if isTCP { + fmt.Fprintln(tmp, "protocol=tcp") + } fmt.Fprintln(tmp, "[mysqldump]") fmt.Fprintf(tmp, "host=%s\n", m.host) @@ -103,6 +114,9 @@ func (m *mysqlTester) makeOptionFile() error { fmt.Fprintf(tmp, "user=%s\n", m.user) fmt.Fprintf(tmp, "password=%s\n", m.pass) fmt.Fprintf(tmp, "ssl-mode=%s\n", m.sslMode(m.sslmode)) + if isTCP { + fmt.Fprintln(tmp, "protocol=tcp") + } m.optionFile = tmp.Name() From 112a836af2f9ab171e47a312292b0b06f52599f0 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Fri, 28 Apr 2017 21:07:39 -0700 Subject: [PATCH 141/179] Make UDT's that aren't enums fall through - This allows typse that are not enumerations to properly escape the enumeration code in the query. - Fix #131 --- bdb/drivers/postgres.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bdb/drivers/postgres.go b/bdb/drivers/postgres.go index ec1aede..34cdd78 100644 --- a/bdb/drivers/postgres.go +++ b/bdb/drivers/postgres.go @@ -3,6 +3,7 @@ package drivers import ( "database/sql" "fmt" + "os" "strings" // Side-effect import sql driver @@ -132,7 +133,7 @@ func (p *PostgresDriver) Columns(schema, tableName string) ([]bdb.Column, error) select c.column_name, ( - case when c.data_type = 'USER-DEFINED' and c.udt_name <> 'hstore' + case when pgt.typtype = 'e' then ( select 'enum.' || c.udt_name || '(''' || string_agg(labels.label, ''',''') || ''')' @@ -176,6 +177,8 @@ func (p *PostgresDriver) Columns(schema, tableName string) ([]bdb.Column, error) )) as is_unique from information_schema.columns as c + inner join pg_namespace as pgn on pgn.nspname = c.udt_schema + left join pg_type pgt on c.data_type = 'USER-DEFINED' and pgn.oid = pgt.typnamespace and c.udt_name = pgt.typname left join information_schema.element_types e on ((c.table_catalog, c.table_schema, c.table_name, 'TABLE', c.dtd_identifier) = (e.object_catalog, e.object_schema, e.object_name, e.object_type, e.collection_type_identifier)) @@ -349,7 +352,7 @@ func (p *PostgresDriver) TranslateColumnType(c bdb.Column) bdb.Column { c.DBType = "hstore" } else { c.Type = "string" - fmt.Printf("Warning: Incompatible data type detected: %s\n", c.UDTName) + fmt.Fprintln(os.Stderr, "Warning: Incompatible data type detected: %s\n", c.UDTName) } default: c.Type = "null.String" From 3b5ab423b3028967f3bbf289d60bd164347ecaf0 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Mon, 1 May 2017 18:41:49 -0700 Subject: [PATCH 142/179] Bump version --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index acc7b1e..793055d 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( "github.com/vattle/sqlboiler/boilingcore" ) -const sqlBoilerVersion = "2.2.0" +const sqlBoilerVersion = "2.3.0" var ( cmdState *boilingcore.State From 070df18197de03cdc57b0b60e106e8d817f8ecc7 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Mon, 1 May 2017 19:44:52 -0700 Subject: [PATCH 143/179] Add mailing lists --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a0a78fd..194fdd8 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [](https://github.com/vattle/sqlboiler/blob/master/LICENSE) [](https://godoc.org/github.com/vattle/sqlboiler) +[](https://groups.google.com/a/volatile.tech/forum/#!forum/sqlboiler) +[](https://groups.google.com/a/volatile.tech/forum/#!forum/sqlboiler-announce) [](https://sqlboiler.from-the.cloud) [](https://circleci.com/gh/vattle/sqlboiler) [](http://goreportcard.com/report/vattle/sqlboiler) From 1467b88f04e176c5fadf5fc9878f57f9a0cfccba Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Mon, 8 May 2017 13:10:21 -0400 Subject: [PATCH 144/179] detect unsigned int columns in mysql --- bdb/drivers/mysql.go | 53 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index ea24f1b..346e473 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -272,23 +272,42 @@ func (m *MySQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey // "varchar" to "string" and "bigint" to "int64". It returns this parsed data // as a Column object. func (m *MySQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { + unsigned := strings.Contains(c.FullDBType, "unsigned") if c.Nullable { switch c.DBType { case "tinyint": // map tinyint(1) to bool if TinyintAsBool is true if TinyintAsBool && c.FullDBType == "tinyint(1)" { c.Type = "null.Bool" + } else if unsigned { + c.Type = "null.Uint8" } else { c.Type = "null.Int8" } case "smallint": - c.Type = "null.Int16" + if unsigned { + c.Type = "null.Uint16" + } else { + c.Type = "null.Int16" + } case "mediumint": - c.Type = "null.Int32" + if unsigned { + c.Type = "null.Uint32" + } else { + c.Type = "null.Int32" + } case "int", "integer": - c.Type = "null.Int" + if unsigned { + c.Type = "null.Uint" + } else { + c.Type = "null.Int" + } case "bigint": - c.Type = "null.Int64" + if unsigned { + c.Type = "null.Uint64" + } else { + c.Type = "null.Uint64" + } case "float": c.Type = "null.Float32" case "double", "double precision", "real": @@ -310,17 +329,35 @@ func (m *MySQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { // map tinyint(1) to bool if TinyintAsBool is true if TinyintAsBool && c.FullDBType == "tinyint(1)" { c.Type = "bool" + } else if unsigned { + c.Type = "uint8" } else { c.Type = "int8" } case "smallint": - c.Type = "int16" + if unsigned { + c.Type = "uint16" + } else { + c.Type = "int16" + } case "mediumint": - c.Type = "int32" + if unsigned { + c.Type = "uint32" + } else { + c.Type = "int32" + } case "int", "integer": - c.Type = "int" + if unsigned { + c.Type = "uint" + } else { + c.Type = "int" + } case "bigint": - c.Type = "int64" + if unsigned { + c.Type = "uint64" + } else { + c.Type = "int64" + } case "float": c.Type = "float32" case "double", "double precision", "real": From 451723ccb9b1535c98a69b3cdb10f2cf64c9f460 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Mon, 8 May 2017 13:25:15 -0400 Subject: [PATCH 145/179] fix imports to point at lbryio repo --- bdb/column.go | 2 +- bdb/drivers/mock.go | 4 +-- bdb/drivers/mssql.go | 2 +- bdb/drivers/mysql.go | 2 +- bdb/drivers/postgres.go | 4 +-- bdb/interface_test.go | 2 +- boilingcore/boilingcore.go | 10 +++--- boilingcore/imports.go | 54 ++++++++++++++++---------------- boilingcore/imports_test.go | 16 +++++----- boilingcore/output.go | 2 +- boilingcore/templates.go | 6 ++-- boilingcore/text_helpers.go | 4 +-- boilingcore/text_helpers_test.go | 4 +-- main.go | 6 ++-- queries/eager_load.go | 4 +-- queries/eager_load_test.go | 2 +- queries/helpers.go | 2 +- queries/qm/query_mods.go | 2 +- queries/query.go | 2 +- queries/query_builders.go | 2 +- queries/reflect.go | 4 +-- randomize/randomize.go | 4 +-- 22 files changed, 70 insertions(+), 70 deletions(-) diff --git a/bdb/column.go b/bdb/column.go index a3632fc..da56e48 100644 --- a/bdb/column.go +++ b/bdb/column.go @@ -3,7 +3,7 @@ package bdb import ( "strings" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/strmangle" ) // Column holds information about a database column. diff --git a/bdb/drivers/mock.go b/bdb/drivers/mock.go index 1c91d0e..7d14f2a 100644 --- a/bdb/drivers/mock.go +++ b/bdb/drivers/mock.go @@ -1,8 +1,8 @@ package drivers import ( - "github.com/vattle/sqlboiler/bdb" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/strmangle" ) // MockDriver is a mock implementation of the bdb driver Interface diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index cb3290a..e59330b 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -8,7 +8,7 @@ import ( _ "github.com/denisenkom/go-mssqldb" "github.com/pkg/errors" - "github.com/vattle/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/bdb" ) // MSSQLDriver holds the database connection string and a handle diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index 346e473..bb73574 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -8,7 +8,7 @@ import ( "github.com/go-sql-driver/mysql" "github.com/pkg/errors" - "github.com/vattle/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/bdb" ) // TinyintAsBool is a global that is set from main.go if a user specifies diff --git a/bdb/drivers/postgres.go b/bdb/drivers/postgres.go index 34cdd78..ad73a2e 100644 --- a/bdb/drivers/postgres.go +++ b/bdb/drivers/postgres.go @@ -10,8 +10,8 @@ import ( _ "github.com/lib/pq" "github.com/pkg/errors" - "github.com/vattle/sqlboiler/bdb" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/strmangle" ) // PostgresDriver holds the database connection string and a handle diff --git a/bdb/interface_test.go b/bdb/interface_test.go index 4d429c6..e5d28fb 100644 --- a/bdb/interface_test.go +++ b/bdb/interface_test.go @@ -3,7 +3,7 @@ package bdb import ( "testing" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/strmangle" ) type testMockDriver struct{} diff --git a/boilingcore/boilingcore.go b/boilingcore/boilingcore.go index 5e00687..e667cae 100644 --- a/boilingcore/boilingcore.go +++ b/boilingcore/boilingcore.go @@ -13,10 +13,10 @@ import ( "text/template" "github.com/pkg/errors" - "github.com/vattle/sqlboiler/bdb" - "github.com/vattle/sqlboiler/bdb/drivers" - "github.com/vattle/sqlboiler/queries" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/bdb/drivers" + "github.com/lbryio/sqlboiler/queries" + "github.com/lbryio/sqlboiler/strmangle" ) const ( @@ -267,7 +267,7 @@ func (s *State) processReplacements() error { return nil } -var basePackage = "github.com/vattle/sqlboiler" +var basePackage = "github.com/lbryio/sqlboiler" func getBasePath(baseDirConfig string) (string, error) { if len(baseDirConfig) > 0 { diff --git a/boilingcore/imports.go b/boilingcore/imports.go index eafcf28..1fbca14 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -6,7 +6,7 @@ import ( "sort" "strings" - "github.com/vattle/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/bdb" ) // imports defines the optional standard imports and @@ -171,25 +171,25 @@ func newImporter() importer { }, thirdParty: importList{ `"github.com/pkg/errors"`, - `"github.com/vattle/sqlboiler/boil"`, - `"github.com/vattle/sqlboiler/queries"`, - `"github.com/vattle/sqlboiler/queries/qm"`, - `"github.com/vattle/sqlboiler/strmangle"`, + `"github.com/lbryio/sqlboiler/boil"`, + `"github.com/lbryio/sqlboiler/queries"`, + `"github.com/lbryio/sqlboiler/queries/qm"`, + `"github.com/lbryio/sqlboiler/strmangle"`, }, } imp.Singleton = mapImports{ "boil_queries": { thirdParty: importList{ - `"github.com/vattle/sqlboiler/boil"`, - `"github.com/vattle/sqlboiler/queries"`, - `"github.com/vattle/sqlboiler/queries/qm"`, + `"github.com/lbryio/sqlboiler/boil"`, + `"github.com/lbryio/sqlboiler/queries"`, + `"github.com/lbryio/sqlboiler/queries/qm"`, }, }, "boil_types": { thirdParty: importList{ `"github.com/pkg/errors"`, - `"github.com/vattle/sqlboiler/strmangle"`, + `"github.com/lbryio/sqlboiler/strmangle"`, }, }, } @@ -201,9 +201,9 @@ func newImporter() importer { `"testing"`, }, thirdParty: importList{ - `"github.com/vattle/sqlboiler/boil"`, - `"github.com/vattle/sqlboiler/randomize"`, - `"github.com/vattle/sqlboiler/strmangle"`, + `"github.com/lbryio/sqlboiler/boil"`, + `"github.com/lbryio/sqlboiler/randomize"`, + `"github.com/lbryio/sqlboiler/strmangle"`, }, } @@ -223,7 +223,7 @@ func newImporter() importer { `"github.com/kat-co/vala"`, `"github.com/pkg/errors"`, `"github.com/spf13/viper"`, - `"github.com/vattle/sqlboiler/boil"`, + `"github.com/lbryio/sqlboiler/boil"`, }, }, "boil_queries_test": { @@ -236,7 +236,7 @@ func newImporter() importer { `"regexp"`, }, thirdParty: importList{ - `"github.com/vattle/sqlboiler/boil"`, + `"github.com/lbryio/sqlboiler/boil"`, }, }, "boil_suites_test": { @@ -261,8 +261,8 @@ func newImporter() importer { thirdParty: importList{ `"github.com/pkg/errors"`, `"github.com/spf13/viper"`, - `"github.com/vattle/sqlboiler/bdb/drivers"`, - `"github.com/vattle/sqlboiler/randomize"`, + `"github.com/lbryio/sqlboiler/bdb/drivers"`, + `"github.com/lbryio/sqlboiler/randomize"`, `_ "github.com/lib/pq"`, }, }, @@ -280,8 +280,8 @@ func newImporter() importer { thirdParty: importList{ `"github.com/pkg/errors"`, `"github.com/spf13/viper"`, - `"github.com/vattle/sqlboiler/bdb/drivers"`, - `"github.com/vattle/sqlboiler/randomize"`, + `"github.com/lbryio/sqlboiler/bdb/drivers"`, + `"github.com/lbryio/sqlboiler/randomize"`, `_ "github.com/go-sql-driver/mysql"`, }, }, @@ -297,8 +297,8 @@ func newImporter() importer { thirdParty: importList{ `"github.com/pkg/errors"`, `"github.com/spf13/viper"`, - `"github.com/vattle/sqlboiler/bdb/drivers"`, - `"github.com/vattle/sqlboiler/randomize"`, + `"github.com/lbryio/sqlboiler/bdb/drivers"`, + `"github.com/lbryio/sqlboiler/randomize"`, `_ "github.com/denisenkom/go-mssqldb"`, }, }, @@ -363,25 +363,25 @@ func newImporter() importer { standard: importList{`"time"`}, }, "types.JSON": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + thirdParty: importList{`"github.com/lbryio/sqlboiler/types"`}, }, "types.BytesArray": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + thirdParty: importList{`"github.com/lbryio/sqlboiler/types"`}, }, "types.Int64Array": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + thirdParty: importList{`"github.com/lbryio/sqlboiler/types"`}, }, "types.Float64Array": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + thirdParty: importList{`"github.com/lbryio/sqlboiler/types"`}, }, "types.BoolArray": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + thirdParty: importList{`"github.com/lbryio/sqlboiler/types"`}, }, "types.StringArray": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + thirdParty: importList{`"github.com/lbryio/sqlboiler/types"`}, }, "types.Hstore": { - thirdParty: importList{`"github.com/vattle/sqlboiler/types"`}, + thirdParty: importList{`"github.com/lbryio/sqlboiler/types"`}, }, } diff --git a/boilingcore/imports_test.go b/boilingcore/imports_test.go index 6f2cde4..ff2362d 100644 --- a/boilingcore/imports_test.go +++ b/boilingcore/imports_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/pkg/errors" - "github.com/vattle/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/bdb" ) func TestImportsSort(t *testing.T) { @@ -234,7 +234,7 @@ func TestCombineTypeImports(t *testing.T) { `"fmt"`, }, thirdParty: importList{ - `"github.com/vattle/sqlboiler/boil"`, + `"github.com/lbryio/sqlboiler/boil"`, }, } @@ -245,7 +245,7 @@ func TestCombineTypeImports(t *testing.T) { `"time"`, }, thirdParty: importList{ - `"github.com/vattle/sqlboiler/boil"`, + `"github.com/lbryio/sqlboiler/boil"`, `"gopkg.in/nullbio/null.v6"`, }, } @@ -280,7 +280,7 @@ func TestCombineTypeImports(t *testing.T) { `"time"`, }, thirdParty: importList{ - `"github.com/vattle/sqlboiler/boil"`, + `"github.com/lbryio/sqlboiler/boil"`, `"gopkg.in/nullbio/null.v6"`, }, } @@ -297,11 +297,11 @@ func TestCombineImports(t *testing.T) { a := imports{ standard: importList{"fmt"}, - thirdParty: importList{"github.com/vattle/sqlboiler", "gopkg.in/nullbio/null.v6"}, + thirdParty: importList{"github.com/lbryio/sqlboiler", "gopkg.in/nullbio/null.v6"}, } b := imports{ standard: importList{"os"}, - thirdParty: importList{"github.com/vattle/sqlboiler"}, + thirdParty: importList{"github.com/lbryio/sqlboiler"}, } c := combineImports(a, b) @@ -309,8 +309,8 @@ func TestCombineImports(t *testing.T) { if c.standard[0] != "fmt" && c.standard[1] != "os" { t.Errorf("Wanted: fmt, os got: %#v", c.standard) } - if c.thirdParty[0] != "github.com/vattle/sqlboiler" && c.thirdParty[1] != "gopkg.in/nullbio/null.v6" { - t.Errorf("Wanted: github.com/vattle/sqlboiler, gopkg.in/nullbio/null.v6 got: %#v", c.thirdParty) + if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "gopkg.in/nullbio/null.v6" { + t.Errorf("Wanted: github.com/lbryio/sqlboiler, gopkg.in/nullbio/null.v6 got: %#v", c.thirdParty) } } diff --git a/boilingcore/output.go b/boilingcore/output.go index 64f28b5..5c65634 100644 --- a/boilingcore/output.go +++ b/boilingcore/output.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" ) -var noEditDisclaimer = []byte(`// This file is generated by SQLBoiler (https://github.com/vattle/sqlboiler) +var noEditDisclaimer = []byte(`// This file is generated by SQLBoiler (https://github.com/lbryio/sqlboiler) // and is meant to be re-generated in place and/or deleted at any time. // DO NOT EDIT diff --git a/boilingcore/templates.go b/boilingcore/templates.go index 9c60739..62c0ae5 100644 --- a/boilingcore/templates.go +++ b/boilingcore/templates.go @@ -9,9 +9,9 @@ import ( "text/template" "github.com/pkg/errors" - "github.com/vattle/sqlboiler/bdb" - "github.com/vattle/sqlboiler/queries" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/queries" + "github.com/lbryio/sqlboiler/strmangle" ) // templateData for sqlboiler templates diff --git a/boilingcore/text_helpers.go b/boilingcore/text_helpers.go index e9577a7..0bce73f 100644 --- a/boilingcore/text_helpers.go +++ b/boilingcore/text_helpers.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/vattle/sqlboiler/bdb" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/strmangle" ) // TxtToOne contains text that will be used by templates for a one-to-many or diff --git a/boilingcore/text_helpers_test.go b/boilingcore/text_helpers_test.go index 8beed50..835ed0d 100644 --- a/boilingcore/text_helpers_test.go +++ b/boilingcore/text_helpers_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/davecgh/go-spew/spew" - "github.com/vattle/sqlboiler/bdb" - "github.com/vattle/sqlboiler/bdb/drivers" + "github.com/lbryio/sqlboiler/bdb" + "github.com/lbryio/sqlboiler/bdb/drivers" ) func TestTxtsFromOne(t *testing.T) { diff --git a/main.go b/main.go index 793055d..5103116 100644 --- a/main.go +++ b/main.go @@ -10,8 +10,8 @@ import ( "github.com/kat-co/vala" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/vattle/sqlboiler/bdb/drivers" - "github.com/vattle/sqlboiler/boilingcore" + "github.com/lbryio/sqlboiler/bdb/drivers" + "github.com/lbryio/sqlboiler/boilingcore" ) const sqlBoilerVersion = "2.3.0" @@ -62,7 +62,7 @@ func main() { Use: "sqlboiler [flags] <driver>", Short: "SQL Boiler generates an ORM tailored to your database schema.", Long: "SQL Boiler generates a Go ORM from template files, tailored to your database schema.\n" + - `Complete documentation is available at http://github.com/vattle/sqlboiler`, + `Complete documentation is available at http://github.com/lbryio/sqlboiler`, Example: `sqlboiler postgres`, PreRunE: preRun, RunE: run, diff --git a/queries/eager_load.go b/queries/eager_load.go index 18abcc8..081aac4 100644 --- a/queries/eager_load.go +++ b/queries/eager_load.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/pkg/errors" - "github.com/vattle/sqlboiler/boil" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/boil" + "github.com/lbryio/sqlboiler/strmangle" ) type loadRelationshipState struct { diff --git a/queries/eager_load_test.go b/queries/eager_load_test.go index dc3f5f1..c7b3cef 100644 --- a/queries/eager_load_test.go +++ b/queries/eager_load_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/vattle/sqlboiler/boil" + "github.com/lbryio/sqlboiler/boil" ) var testEagerCounters struct { diff --git a/queries/helpers.go b/queries/helpers.go index 59ad8a3..b953a67 100644 --- a/queries/helpers.go +++ b/queries/helpers.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/strmangle" ) // NonZeroDefaultSet returns the fields included in the diff --git a/queries/qm/query_mods.go b/queries/qm/query_mods.go index 81ae3a0..89d31d4 100644 --- a/queries/qm/query_mods.go +++ b/queries/qm/query_mods.go @@ -1,6 +1,6 @@ package qm -import "github.com/vattle/sqlboiler/queries" +import "github.com/lbryio/sqlboiler/queries" // QueryMod to modify the query object type QueryMod func(q *queries.Query) diff --git a/queries/query.go b/queries/query.go index 8c2abdb..be539e5 100644 --- a/queries/query.go +++ b/queries/query.go @@ -4,7 +4,7 @@ import ( "database/sql" "fmt" - "github.com/vattle/sqlboiler/boil" + "github.com/lbryio/sqlboiler/boil" ) // joinKind is the type of join diff --git a/queries/query_builders.go b/queries/query_builders.go index 2188044..d221aca 100644 --- a/queries/query_builders.go +++ b/queries/query_builders.go @@ -7,7 +7,7 @@ import ( "sort" "strings" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/strmangle" ) var ( diff --git a/queries/reflect.go b/queries/reflect.go index 9c55b33..8cf8f3d 100644 --- a/queries/reflect.go +++ b/queries/reflect.go @@ -8,8 +8,8 @@ import ( "sync" "github.com/pkg/errors" - "github.com/vattle/sqlboiler/boil" - "github.com/vattle/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/boil" + "github.com/lbryio/sqlboiler/strmangle" ) var ( diff --git a/randomize/randomize.go b/randomize/randomize.go index 59f7eff..ea99c43 100644 --- a/randomize/randomize.go +++ b/randomize/randomize.go @@ -18,8 +18,8 @@ import ( "github.com/pkg/errors" "github.com/satori/go.uuid" - "github.com/vattle/sqlboiler/strmangle" - "github.com/vattle/sqlboiler/types" + "github.com/lbryio/sqlboiler/strmangle" + "github.com/lbryio/sqlboiler/types" ) var ( From f863ecb48e3215e98d75dfebeefbd38061cad936 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Mon, 8 May 2017 16:30:40 -0400 Subject: [PATCH 146/179] add model Filters, FindOrCreate methods, IsNew() --- templates/00_struct.tpl | 7 ++++ templates/14_find.tpl | 79 +++++++++++++++++++++++++++++++++++++++++ templates/20_exists.tpl | 36 +++++++++++++++++++ 3 files changed, 122 insertions(+) diff --git a/templates/00_struct.tpl b/templates/00_struct.tpl index 585feee..f9c3a04 100644 --- a/templates/00_struct.tpl +++ b/templates/00_struct.tpl @@ -17,6 +17,13 @@ type {{$modelName}} struct { {{end -}} } +// {{$modelName}}Filter allows you to filter on any columns by making them all pointers. +type {{$modelName}}Filter struct { + {{range $column := .Table.Columns -}} + {{titleCase $column.Name}} *{{$column.Type}} `{{generateTags $dot.Tags $column.Name}}boil:"{{$column.Name}}" json:"{{$column.Name}},omitempty" toml:"{{$column.Name}}" yaml:"{{$column.Name}},omitempty"` + {{end -}} +} + {{- if .Table.IsJoinTable -}} {{- else}} // {{$modelNameCamel}}R is where relationships are stored. diff --git a/templates/14_find.tpl b/templates/14_find.tpl index 097f184..12ffa85 100644 --- a/templates/14_find.tpl +++ b/templates/14_find.tpl @@ -53,3 +53,82 @@ func Find{{$tableNameSingular}}P(exec boil.Executor, {{$pkArgs}}, selectCols ... return retobj } + +// FindOne{{$tableNameSingular}} retrieves a single record using filters. +func FindOne{{$tableNameSingular}}(exec boil.Executor, filters {{$tableNameSingular}}Filter) (*{{$tableNameSingular}}, error) { + {{$varNameSingular}}Obj := &{{$tableNameSingular}}{} + + query := NewQuery(exec, qm.Select("*"), qm.From("{{.Table.Name | .SchemaTable}}")) + + r := reflect.ValueOf(filters) + for i := 0; i < r.NumField(); i++ { + f := r.Field(i) + if f.Elem().IsValid() { + queries.AppendWhere(query, r.Type().Field(i).Tag.Get("boil")+" = ?", f.Elem().Interface()) + } + } + + queries.SetLimit(query, 1) + + err := query.Bind({{$varNameSingular}}Obj) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, sql.ErrNoRows + } + return nil, errors.Wrap(err, "{{.PkgName}}: unable to select from {{.Table.Name}}") + } + + return {{$varNameSingular}}Obj, nil +} + +// FindOne{{$tableNameSingular}}G retrieves a single record using filters. +func FindOne{{$tableNameSingular}}G(filters {{$tableNameSingular}}Filter) (*{{$tableNameSingular}}, error) { + return FindOne{{$tableNameSingular}}(boil.GetDB(), filters) +} + +// FindOne{{$tableNameSingular}}OrInit retrieves a single record using filters, or initializes a new record if one is not found. +func FindOne{{$tableNameSingular}}OrInit(exec boil.Executor, filters {{$tableNameSingular}}Filter) (*{{$tableNameSingular}}, error) { + {{$varNameSingular}}Obj, err := FindOne{{$tableNameSingular}}(exec, filters) + if err != nil && errors.Cause(err) != sql.ErrNoRows { + return nil, err + } + + if {{$varNameSingular}}Obj == nil { + {{$varNameSingular}}Obj = &{{$tableNameSingular}}{} + objR := reflect.ValueOf({{$varNameSingular}}Obj).Elem() + r := reflect.ValueOf(filters) + for i := 0; i < r.NumField(); i++ { + f := r.Field(i) + if f.Elem().IsValid() { + objR.FieldByName(r.Type().Field(i).Name).Set(f.Elem()) + } + } + } + + return {{$varNameSingular}}Obj, nil +} + +// FindOne{{$tableNameSingular}}OrInit retrieves a single record using filters, or initializes a new record if one is not found. +func FindOne{{$tableNameSingular}}OrInitG(filters {{$tableNameSingular}}Filter) (*{{$tableNameSingular}}, error) { + return FindOne{{$tableNameSingular}}OrInit(boil.GetDB(), filters) +} + +// FindOne{{$tableNameSingular}}OrInit retrieves a single record using filters, or initializes and inserts a new record if one is not found. +func FindOne{{$tableNameSingular}}OrCreate(exec boil.Executor, filters {{$tableNameSingular}}Filter) (*{{$tableNameSingular}}, error) { + {{$varNameSingular}}Obj, err := FindOne{{$tableNameSingular}}OrInit(exec, filters) + if err != nil { + return nil, err + } + if {{$varNameSingular}}Obj.IsNew() { + err := {{$varNameSingular}}Obj.Insert(exec) + if err != nil { + return nil, err + } + } + return {{$varNameSingular}}Obj, nil +} + +// FindOne{{$tableNameSingular}}OrInit retrieves a single record using filters, or initializes and inserts a new record if one is not found. +func FindOne{{$tableNameSingular}}OrCreateG(filters {{$tableNameSingular}}Filter) (*{{$tableNameSingular}}, error) { + return FindOne{{$tableNameSingular}}OrCreate(boil.GetDB(), filters) +} diff --git a/templates/20_exists.tpl b/templates/20_exists.tpl index 22a67a0..2e20fa6 100644 --- a/templates/20_exists.tpl +++ b/templates/20_exists.tpl @@ -1,4 +1,5 @@ {{- $tableNameSingular := .Table.Name | singular | titleCase -}} +{{- $varNameSingular := .Table.Name | singular | camelCase -}} {{- $colDefs := sqlColDefinitions .Table.Columns .Table.PKey.Columns -}} {{- $pkNames := $colDefs.Names | stringMap .StringFuncs.camelCase | stringMap .StringFuncs.replaceReserved -}} {{- $pkArgs := joinSlices " " $pkNames $colDefs.Types | join ", " -}} @@ -51,3 +52,38 @@ func {{$tableNameSingular}}ExistsP(exec boil.Executor, {{$pkArgs}}) bool { return e } + +// IsNew() checks if record exists in db (aka if its primary key is set). +func (o *{{$tableNameSingular}}) IsNew() bool { + r := reflect.ValueOf(o).Elem() + for i := 0; i < r.NumField(); i++ { + column := r.Type().Field(i).Tag.Get("boil") + for _, pkColumn := range {{$varNameSingular}}PrimaryKeyColumns { + if column == pkColumn { + field := r.Field(i) + if field.Interface() != reflect.Zero(field.Type()).Interface() { + return false + } + } + } + } + return true +} + +// Save() inserts the record if it does not exist, or updates it if it does. +func (o *{{$tableNameSingular}}) Save(exec boil.Executor, whitelist ...string) error { + if o.IsNew() { + return o.Insert(exec, whitelist...) + } else { + return o.Update(exec, whitelist...) + } +} + +// SaveG() inserts the record if it does not exist, or updates it if it does. +func (o *{{$tableNameSingular}}) SaveG(whitelist ...string) error { + if o.IsNew() { + return o.InsertG(whitelist...) + } else { + return o.UpdateG(whitelist...) + } +} From ed43c9078f34416398d074fe772af8e1513289a6 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Tue, 9 May 2017 15:30:04 -0400 Subject: [PATCH 147/179] made query structs public, added IS NULL to filter --- .gitignore | 1 + templates/01_types.tpl | 2 +- templates/03_finishers.tpl | 20 ++++++------- templates/04_relationship_to_one.tpl | 6 ++-- templates/05_relationship_one_to_one.tpl | 6 ++-- templates/06_relationship_to_many.tpl | 6 ++-- templates/07_relationship_to_one_eager.tpl | 2 +- templates/13_all.tpl | 8 +++--- templates/14_find.tpl | 20 ++++--------- templates/16_update.tpl | 4 +-- templates/18_delete.tpl | 6 ++-- templates/22_query.tpl | 33 ++++++++++++++++++++++ templates/singleton/boil_types.tpl | 5 ++++ 13 files changed, 75 insertions(+), 44 deletions(-) create mode 100644 templates/22_query.tpl diff --git a/.gitignore b/.gitignore index 8f7858d..a7dc0b1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ sqlboiler.toml models/ testschema.sql .cover +/.idea diff --git a/templates/01_types.tpl b/templates/01_types.tpl index a1a1ef0..9340e42 100644 --- a/templates/01_types.tpl +++ b/templates/01_types.tpl @@ -21,7 +21,7 @@ type ( {{$tableNameSingular}}Hook func(boil.Executor, *{{$tableNameSingular}}) error {{- end}} - {{$varNameSingular}}Query struct { + {{$tableNameSingular}}Query struct { *queries.Query } ) diff --git a/templates/03_finishers.tpl b/templates/03_finishers.tpl index 0cc7e78..841769b 100644 --- a/templates/03_finishers.tpl +++ b/templates/03_finishers.tpl @@ -1,7 +1,7 @@ {{- $tableNameSingular := .Table.Name | singular | titleCase -}} {{- $varNameSingular := .Table.Name | singular | camelCase -}} -// OneP returns a single {{$varNameSingular}} record from the query, and panics on error. -func (q {{$varNameSingular}}Query) OneP() (*{{$tableNameSingular}}) { +// OneP returns a single {{$tableNameSingular}} record from the query, and panics on error. +func (q {{$tableNameSingular}}Query) OneP() (*{{$tableNameSingular}}) { o, err := q.One() if err != nil { panic(boil.WrapErr(err)) @@ -10,8 +10,8 @@ func (q {{$varNameSingular}}Query) OneP() (*{{$tableNameSingular}}) { return o } -// One returns a single {{$varNameSingular}} record from the query. -func (q {{$varNameSingular}}Query) One() (*{{$tableNameSingular}}, error) { +// One returns a single {{$tableNameSingular}} record from the query. +func (q {{$tableNameSingular}}Query) One() (*{{$tableNameSingular}}, error) { o := &{{$tableNameSingular}}{} queries.SetLimit(q.Query, 1) @@ -34,7 +34,7 @@ func (q {{$varNameSingular}}Query) One() (*{{$tableNameSingular}}, error) { } // AllP returns all {{$tableNameSingular}} records from the query, and panics on error. -func (q {{$varNameSingular}}Query) AllP() {{$tableNameSingular}}Slice { +func (q {{$tableNameSingular}}Query) AllP() {{$tableNameSingular}}Slice { o, err := q.All() if err != nil { panic(boil.WrapErr(err)) @@ -44,7 +44,7 @@ func (q {{$varNameSingular}}Query) AllP() {{$tableNameSingular}}Slice { } // All returns all {{$tableNameSingular}} records from the query. -func (q {{$varNameSingular}}Query) All() ({{$tableNameSingular}}Slice, error) { +func (q {{$tableNameSingular}}Query) All() ({{$tableNameSingular}}Slice, error) { var o []*{{$tableNameSingular}} err := q.Bind(&o) @@ -66,7 +66,7 @@ func (q {{$varNameSingular}}Query) All() ({{$tableNameSingular}}Slice, error) { } // CountP returns the count of all {{$tableNameSingular}} records in the query, and panics on error. -func (q {{$varNameSingular}}Query) CountP() int64 { +func (q {{$tableNameSingular}}Query) CountP() int64 { c, err := q.Count() if err != nil { panic(boil.WrapErr(err)) @@ -76,7 +76,7 @@ func (q {{$varNameSingular}}Query) CountP() int64 { } // Count returns the count of all {{$tableNameSingular}} records in the query. -func (q {{$varNameSingular}}Query) Count() (int64, error) { +func (q {{$tableNameSingular}}Query) Count() (int64, error) { var count int64 queries.SetSelect(q.Query, nil) @@ -91,7 +91,7 @@ func (q {{$varNameSingular}}Query) Count() (int64, error) { } // Exists checks if the row exists in the table, and panics on error. -func (q {{$varNameSingular}}Query) ExistsP() bool { +func (q {{$tableNameSingular}}Query) ExistsP() bool { e, err := q.Exists() if err != nil { panic(boil.WrapErr(err)) @@ -101,7 +101,7 @@ func (q {{$varNameSingular}}Query) ExistsP() bool { } // Exists checks if the row exists in the table. -func (q {{$varNameSingular}}Query) Exists() (bool, error) { +func (q {{$tableNameSingular}}Query) Exists() (bool, error) { var count int64 queries.SetCount(q.Query) diff --git a/templates/04_relationship_to_one.tpl b/templates/04_relationship_to_one.tpl index 05c75c9..9bc8c5f 100644 --- a/templates/04_relationship_to_one.tpl +++ b/templates/04_relationship_to_one.tpl @@ -3,14 +3,14 @@ {{- $dot := . -}} {{- range .Table.FKeys -}} {{- $txt := txtsFromFKey $dot.Tables $dot.Table . -}} - {{- $varNameSingular := .ForeignTable | singular | camelCase}} + {{- $tableNameSingular := .ForeignTable | singular | titleCase}} // {{$txt.Function.Name}}G pointed to by the foreign key. -func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}G(mods ...qm.QueryMod) {{$varNameSingular}}Query { +func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}G(mods ...qm.QueryMod) {{$tableNameSingular}}Query { return o.{{$txt.Function.Name}}(boil.GetDB(), mods...) } // {{$txt.Function.Name}} pointed to by the foreign key. -func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}(exec boil.Executor, mods ...qm.QueryMod) ({{$varNameSingular}}Query) { +func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}(exec boil.Executor, mods ...qm.QueryMod) ({{$tableNameSingular}}Query) { queryMods := []qm.QueryMod{ qm.Where("{{$txt.ForeignTable.ColumnName}}=?", o.{{$txt.LocalTable.ColumnNameGo}}), } diff --git a/templates/05_relationship_one_to_one.tpl b/templates/05_relationship_one_to_one.tpl index e74279c..1dcd2ee 100644 --- a/templates/05_relationship_one_to_one.tpl +++ b/templates/05_relationship_one_to_one.tpl @@ -3,14 +3,14 @@ {{- $dot := . -}} {{- range .Table.ToOneRelationships -}} {{- $txt := txtsFromOneToOne $dot.Tables $dot.Table . -}} - {{- $varNameSingular := .ForeignTable | singular | camelCase}} + {{- $tableNameSingular := .ForeignTable | singular | titleCase}} // {{$txt.Function.Name}}G pointed to by the foreign key. -func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}G(mods ...qm.QueryMod) {{$varNameSingular}}Query { +func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}G(mods ...qm.QueryMod) {{$tableNameSingular}}Query { return o.{{$txt.Function.Name}}(boil.GetDB(), mods...) } // {{$txt.Function.Name}} pointed to by the foreign key. -func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}(exec boil.Executor, mods ...qm.QueryMod) ({{$varNameSingular}}Query) { +func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}(exec boil.Executor, mods ...qm.QueryMod) ({{$tableNameSingular}}Query) { queryMods := []qm.QueryMod{ qm.Where("{{$txt.ForeignTable.ColumnName}}=?", o.{{$txt.LocalTable.ColumnNameGo}}), } diff --git a/templates/06_relationship_to_many.tpl b/templates/06_relationship_to_many.tpl index c108eeb..10aa842 100644 --- a/templates/06_relationship_to_many.tpl +++ b/templates/06_relationship_to_many.tpl @@ -3,18 +3,18 @@ {{- $dot := . -}} {{- $table := .Table -}} {{- range .Table.ToManyRelationships -}} - {{- $varNameSingular := .ForeignTable | singular | camelCase -}} + {{- $tableNameSingular := .ForeignTable | singular | titleCase -}} {{- $txt := txtsFromToMany $dot.Tables $table . -}} {{- $schemaForeignTable := .ForeignTable | $dot.SchemaTable}} // {{$txt.Function.Name}}G retrieves all the {{.ForeignTable | singular}}'s {{$txt.ForeignTable.NameHumanReadable}} {{- if not (eq $txt.Function.Name $txt.ForeignTable.NamePluralGo)}} via {{.ForeignColumn}} column{{- end}}. -func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}G(mods ...qm.QueryMod) {{$varNameSingular}}Query { +func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}G(mods ...qm.QueryMod) {{$tableNameSingular}}Query { return o.{{$txt.Function.Name}}(boil.GetDB(), mods...) } // {{$txt.Function.Name}} retrieves all the {{.ForeignTable | singular}}'s {{$txt.ForeignTable.NameHumanReadable}} with an executor {{- if not (eq $txt.Function.Name $txt.ForeignTable.NamePluralGo)}} via {{.ForeignColumn}} column{{- end}}. -func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}(exec boil.Executor, mods ...qm.QueryMod) {{$varNameSingular}}Query { +func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}(exec boil.Executor, mods ...qm.QueryMod) {{$tableNameSingular}}Query { queryMods := []qm.QueryMod{ qm.Select("{{id 0 | $dot.Quotes}}.*"), } diff --git a/templates/07_relationship_to_one_eager.tpl b/templates/07_relationship_to_one_eager.tpl index dd1481a..3b24f1a 100644 --- a/templates/07_relationship_to_one_eager.tpl +++ b/templates/07_relationship_to_one_eager.tpl @@ -4,7 +4,7 @@ {{- range .Table.FKeys -}} {{- $txt := txtsFromFKey $dot.Tables $dot.Table . -}} {{- $varNameSingular := $dot.Table.Name | singular | camelCase -}} - {{- $arg := printf "maybe%s" $txt.LocalTable.NameGo -}} + {{- $arg := printf "maybe%s" $txt.LocalTable.NameGo}} // Load{{$txt.Function.Name}} allows an eager lookup of values, cached into the // loaded structs of the objects. func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singular bool, {{$arg}} interface{}) error { diff --git a/templates/13_all.tpl b/templates/13_all.tpl index 42cf66a..c41f19c 100644 --- a/templates/13_all.tpl +++ b/templates/13_all.tpl @@ -1,12 +1,12 @@ {{- $tableNamePlural := .Table.Name | plural | titleCase -}} -{{- $varNameSingular := .Table.Name | singular | camelCase}} +{{- $tableNameSingular := .Table.Name | singular | titleCase}} // {{$tableNamePlural}}G retrieves all records. -func {{$tableNamePlural}}G(mods ...qm.QueryMod) {{$varNameSingular}}Query { +func {{$tableNamePlural}}G(mods ...qm.QueryMod) {{$tableNameSingular}}Query { return {{$tableNamePlural}}(boil.GetDB(), mods...) } // {{$tableNamePlural}} retrieves all the records using an executor. -func {{$tableNamePlural}}(exec boil.Executor, mods ...qm.QueryMod) {{$varNameSingular}}Query { +func {{$tableNamePlural}}(exec boil.Executor, mods ...qm.QueryMod) {{$tableNameSingular}}Query { mods = append(mods, qm.From("{{.Table.Name | .SchemaTable}}")) - return {{$varNameSingular}}Query{NewQuery(exec, mods...)} + return {{$tableNameSingular}}Query{NewQuery(exec, mods...)} } diff --git a/templates/14_find.tpl b/templates/14_find.tpl index 12ffa85..e177959 100644 --- a/templates/14_find.tpl +++ b/templates/14_find.tpl @@ -56,21 +56,13 @@ func Find{{$tableNameSingular}}P(exec boil.Executor, {{$pkArgs}}, selectCols ... // FindOne{{$tableNameSingular}} retrieves a single record using filters. func FindOne{{$tableNameSingular}}(exec boil.Executor, filters {{$tableNameSingular}}Filter) (*{{$tableNameSingular}}, error) { - {{$varNameSingular}}Obj := &{{$tableNameSingular}}{} + obj := &{{$tableNameSingular}}{} - query := NewQuery(exec, qm.Select("*"), qm.From("{{.Table.Name | .SchemaTable}}")) + err := {{$tableNameSingular}}NewQuery(exec). + Where(filters). + Limit(1). + Bind(obj) - r := reflect.ValueOf(filters) - for i := 0; i < r.NumField(); i++ { - f := r.Field(i) - if f.Elem().IsValid() { - queries.AppendWhere(query, r.Type().Field(i).Tag.Get("boil")+" = ?", f.Elem().Interface()) - } - } - - queries.SetLimit(query, 1) - - err := query.Bind({{$varNameSingular}}Obj) if err != nil { if errors.Cause(err) == sql.ErrNoRows { return nil, sql.ErrNoRows @@ -78,7 +70,7 @@ func FindOne{{$tableNameSingular}}(exec boil.Executor, filters {{$tableNameSingu return nil, errors.Wrap(err, "{{.PkgName}}: unable to select from {{.Table.Name}}") } - return {{$varNameSingular}}Obj, nil + return obj, nil } // FindOne{{$tableNameSingular}}G retrieves a single record using filters. diff --git a/templates/16_update.tpl b/templates/16_update.tpl index 50c7f49..bc4a6f5 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -101,14 +101,14 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string } // UpdateAllP updates all rows with matching column names, and panics on error. -func (q {{$varNameSingular}}Query) UpdateAllP(cols M) { +func (q {{$tableNameSingular}}Query) UpdateAllP(cols M) { if err := q.UpdateAll(cols); err != nil { panic(boil.WrapErr(err)) } } // UpdateAll updates all rows with the specified column values. -func (q {{$varNameSingular}}Query) UpdateAll(cols M) error { +func (q {{$tableNameSingular}}Query) UpdateAll(cols M) error { queries.SetUpdate(q.Query, cols) _, err := q.Query.Exec() diff --git a/templates/18_delete.tpl b/templates/18_delete.tpl index f2ddbb6..e9a5177 100644 --- a/templates/18_delete.tpl +++ b/templates/18_delete.tpl @@ -65,16 +65,16 @@ func (o *{{$tableNameSingular}}) Delete(exec boil.Executor) error { } // DeleteAllP deletes all rows, and panics on error. -func (q {{$varNameSingular}}Query) DeleteAllP() { +func (q {{$tableNameSingular}}Query) DeleteAllP() { if err := q.DeleteAll(); err != nil { panic(boil.WrapErr(err)) } } // DeleteAll deletes all matching rows. -func (q {{$varNameSingular}}Query) DeleteAll() error { +func (q {{$tableNameSingular}}Query) DeleteAll() error { if q.Query == nil { - return errors.New("{{.PkgName}}: no {{$varNameSingular}}Query provided for delete all") + return errors.New("{{.PkgName}}: no {{$tableNameSingular}}Query provided for delete all") } queries.SetDelete(q.Query) diff --git a/templates/22_query.tpl b/templates/22_query.tpl new file mode 100644 index 0000000..512c717 --- /dev/null +++ b/templates/22_query.tpl @@ -0,0 +1,33 @@ +{{- $tableNameSingular := .Table.Name | singular | titleCase -}} + +// {{$tableNameSingular}}NewQuery filters query results +func {{$tableNameSingular}}NewQuery(exec boil.Executor) *{{$tableNameSingular}}Query { + return &{{$tableNameSingular}}Query{NewQuery(exec, qm.Select("*"), qm.From("{{.Table.Name | .SchemaTable}}"))} +} + +// {{$tableNameSingular}}NewQuery filters query results +func {{$tableNameSingular}}NewQueryG() *{{$tableNameSingular}}Query { + return {{$tableNameSingular}}NewQuery(boil.GetDB()) +} + +// Where filters query results +func (q *{{$tableNameSingular}}Query) Where(filters {{$tableNameSingular}}Filter) *{{$tableNameSingular}}Query { + r := reflect.ValueOf(filters) + for i := 0; i < r.NumField(); i++ { + f := r.Field(i) + if f.Elem().IsValid() { + if nullable, ok := f.Elem().Interface().(Nullable); ok && nullable.IsZero() { + queries.AppendWhere(q.Query, r.Type().Field(i).Tag.Get("boil")+" IS NULL") + } else { + queries.AppendWhere(q.Query, r.Type().Field(i).Tag.Get("boil")+" = ?", f.Elem().Interface()) + } + } + } + return q +} + +// Limit limits query results +func (q *{{$tableNameSingular}}Query) Limit(limit int) *{{$tableNameSingular}}Query { + queries.SetLimit(q.Query, limit) + return q +} \ No newline at end of file diff --git a/templates/singleton/boil_types.tpl b/templates/singleton/boil_types.tpl index 9bf13e8..85e05d9 100644 --- a/templates/singleton/boil_types.tpl +++ b/templates/singleton/boil_types.tpl @@ -1,6 +1,11 @@ // M type is for providing columns and column values to UpdateAll. type M map[string]interface{} +// Nullable means the value may represent an sql NULL. It is implemented by null.* types. +type Nullable interface { + IsZero() bool +} + // ErrSyncFail occurs during insert when the record could not be retrieved in // order to populate default value information. This usually happens when LastInsertId // fails or there was a primary key configuration that was not resolvable. From b160e5c1f4eddc9ac9e54e87d13da683f3cd1ede Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Wed, 10 May 2017 11:21:29 -0400 Subject: [PATCH 148/179] dont error if no rows found --- templates/03_finishers.tpl | 2 +- templates/14_find.tpl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/03_finishers.tpl b/templates/03_finishers.tpl index 841769b..13ea14d 100644 --- a/templates/03_finishers.tpl +++ b/templates/03_finishers.tpl @@ -19,7 +19,7 @@ func (q {{$tableNameSingular}}Query) One() (*{{$tableNameSingular}}, error) { err := q.Bind(o) if err != nil { if errors.Cause(err) == sql.ErrNoRows { - return nil, sql.ErrNoRows + return nil, nil } return nil, errors.Wrap(err, "{{.PkgName}}: failed to execute a one query for {{.Table.Name}}") } diff --git a/templates/14_find.tpl b/templates/14_find.tpl index e177959..b7f69db 100644 --- a/templates/14_find.tpl +++ b/templates/14_find.tpl @@ -36,7 +36,7 @@ func Find{{$tableNameSingular}}(exec boil.Executor, {{$pkArgs}}, selectCols ...s err := q.Bind({{$varNameSingular}}Obj) if err != nil { if errors.Cause(err) == sql.ErrNoRows { - return nil, sql.ErrNoRows + return nil, nil } return nil, errors.Wrap(err, "{{.PkgName}}: unable to select from {{.Table.Name}}") } @@ -65,7 +65,7 @@ func FindOne{{$tableNameSingular}}(exec boil.Executor, filters {{$tableNameSingu if err != nil { if errors.Cause(err) == sql.ErrNoRows { - return nil, sql.ErrNoRows + return nil, nil } return nil, errors.Wrap(err, "{{.PkgName}}: unable to select from {{.Table.Name}}") } @@ -81,7 +81,7 @@ func FindOne{{$tableNameSingular}}G(filters {{$tableNameSingular}}Filter) (*{{$t // FindOne{{$tableNameSingular}}OrInit retrieves a single record using filters, or initializes a new record if one is not found. func FindOne{{$tableNameSingular}}OrInit(exec boil.Executor, filters {{$tableNameSingular}}Filter) (*{{$tableNameSingular}}, error) { {{$varNameSingular}}Obj, err := FindOne{{$tableNameSingular}}(exec, filters) - if err != nil && errors.Cause(err) != sql.ErrNoRows { + if err != nil { return nil, err } From 31fe8b6e1d03d29edb3c639575efc75fb2c5a970 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Wed, 10 May 2017 15:29:02 -0400 Subject: [PATCH 149/179] model merging --- boilingcore/imports.go | 7 ++- templates/23_merge.tpl | 84 ++++++++++++++++++++++++++++ templates/singleton/boil_queries.tpl | 54 ++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 templates/23_merge.tpl diff --git a/boilingcore/imports.go b/boilingcore/imports.go index 1fbca14..fc148c7 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -179,11 +179,16 @@ func newImporter() importer { } imp.Singleton = mapImports{ - "boil_queries": { + "boil_queries": imports{ + standard: importList{ + `"database/sql"`, + `"strings"`, + }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, + `"github.com/pkg/errors"`, }, }, "boil_types": { diff --git a/templates/23_merge.tpl b/templates/23_merge.tpl new file mode 100644 index 0000000..c7e27f5 --- /dev/null +++ b/templates/23_merge.tpl @@ -0,0 +1,84 @@ +{{- $tableNamePlural := .Table.Name | plural | titleCase -}} +{{- $tableNameSingular := .Table.Name | singular | titleCase -}} +{{- if .Table.IsJoinTable -}} +{{- else -}} + {{- $dot := . }} +// Merge combines two {{$tableNamePlural}} into one. The primary record will be kept, and the secondary will be deleted. +func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID uint64) error { + txdb, ok := exec.(boil.Beginner) + if !ok { + return errors.New("database does not support transactions") + } + + tx, txErr := txdb.Begin() + if txErr != nil { + return txErr + } + + primary, err := Find{{$tableNameSingular}}(tx, primaryID) + if err != nil { + tx.Rollback() + return err + } + if primary == nil { + return errors.New("Primary {{$tableNameSingular}} not found") + } + + secondary, err := Find{{$tableNameSingular}}(tx, secondaryID) + if err != nil { + tx.Rollback() + return err + } + if secondary == nil { + return errors.New("Secondary {{$tableNameSingular}} not found") + } + + relatedFields := map[string]string{ + {{- range .Tables -}} + {{- range .FKeys -}} + {{- if eq $dot.Table.Name .ForeignTable }} + "{{.Table }}": "{{ .Column}}", + {{- end -}} + {{- end -}} + {{- end }} + } + err = mergeModels(tx, primaryID, secondaryID, relatedFields) + if err != nil { + tx.Rollback() + return err + } + + pr := reflect.ValueOf(primary) + sr := reflect.ValueOf(secondary) + // for any column thats null on the primary and not null on the secondary, copy from secondary to primary + for i := 0; i < sr.Elem().NumField(); i++ { + pf := pr.Elem().Field(i) + sf := sr.Elem().Field(i) + if sf.IsValid() { + if nullable, ok := sf.Interface().(Nullable); ok && !nullable.IsZero() && pf.Interface().(Nullable).IsZero() { + pf.Set(sf) + } + } + } + + err = primary.Update(tx) + if err != nil { + tx.Rollback() + return errors.WithStack(err) + } + + err = secondary.Delete(tx) + if err != nil { + tx.Rollback() + return errors.WithStack(err) + } + + tx.Commit() + return nil +} + +// Merge combines two {{$tableNamePlural}} into one. The primary record will be kept, and the secondary will be deleted. +func Merge{{$tableNamePlural}}G(primaryID uint64, secondaryID uint64) error { + return Merge{{$tableNamePlural}}(boil.GetDB(), primaryID, secondaryID) +} +{{- end -}}{{/* join table */}} \ No newline at end of file diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index 3883e8e..d129e43 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -19,3 +19,57 @@ func NewQuery(exec boil.Executor, mods ...qm.QueryMod) *queries.Query { return q } + +func mergeModels(tx *sql.Tx, primaryID uint64, secondaryID uint64, relatedFields map[string]string) error { + if len(relatedFields) < 1 { + return nil + } + + for table, column := range relatedFields { + // TODO: use NewQuery here, not plain sql + query := "UPDATE " + table + " SET " + column + " = ? WHERE " + column + " = ?" + _, err := tx.Exec(query, primaryID, secondaryID) + if err != nil { + return errors.WithStack(err) + } + } + return checkMerge(tx, relatedFields) +} + +func checkMerge(tx *sql.Tx, fields map[string]string) error { + columns := []interface{}{} + seenColumns := map[string]bool{} + placeholders := []string{} + for _, column := range fields { + if _, ok := seenColumns[column]; !ok { + columns = append(columns, column) + seenColumns[column] = true + placeholders = append(placeholders, "?") + + } + } + + placeholder := strings.Join(placeholders, ", ") + + q := `SELECT table_name, column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND column_name IN (` + placeholder + `)` + rows, err := tx.Query(q, columns...) + defer rows.Close() + if err != nil { + return errors.WithStack(err) + } + + for rows.Next() { + var tableName string + var columnName string + err = rows.Scan(&tableName, &columnName) + if err != nil { + return errors.WithStack(err) + } + + if _, exists := fields[tableName]; !exists { + return errors.New("Missing merge for " + tableName + "." + columnName) + } + } + + return nil +} From 912a689701a945ccd9034c4227b508dfca353095 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Thu, 11 May 2017 17:42:20 -0400 Subject: [PATCH 150/179] added unique key detection (mysql only for now), improved merging --- bdb/drivers/mock.go | 4 + bdb/drivers/mssql.go | 6 +- bdb/drivers/mysql.go | 42 +++++++++- bdb/drivers/postgres.go | 8 +- bdb/interface.go | 5 ++ bdb/keys.go | 7 ++ bdb/table.go | 1 + boilingcore/imports.go | 3 +- boilingcore/templates.go | 2 +- templates/23_merge.tpl | 23 +++++- templates/singleton/boil_queries.tpl | 116 ++++++++++++++++++++------- templates/singleton/boil_types.tpl | 16 ++++ 12 files changed, 195 insertions(+), 38 deletions(-) diff --git a/bdb/drivers/mock.go b/bdb/drivers/mock.go index 7d14f2a..810eb48 100644 --- a/bdb/drivers/mock.go +++ b/bdb/drivers/mock.go @@ -58,6 +58,10 @@ func (m *MockDriver) Columns(schema, tableName string) ([]bdb.Column, error) { }[tableName], nil } +func (m *MockDriver) UniqueKeyInfo(schema, tableName string) ([]bdb.UniqueKey, error) { + return []bdb.UniqueKey{}, nil +} + // ForeignKeyInfo returns a list of mock foreignkeys func (m *MockDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) { return map[string][]bdb.ForeignKey{ diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index e59330b..70b9ed9 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -7,8 +7,8 @@ import ( "strings" _ "github.com/denisenkom/go-mssqldb" - "github.com/pkg/errors" "github.com/lbryio/sqlboiler/bdb" + "github.com/pkg/errors" ) // MSSQLDriver holds the database connection string and a handle @@ -241,6 +241,10 @@ func (m *MSSQLDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryKey, return pkey, nil } +func (m *MSSQLDriver) UniqueKeyInfo(schema, tableName string) ([]bdb.UniqueKey, error) { + return []bdb.UniqueKey{}, errors.New("not implemented") +} + // ForeignKeyInfo retrieves the foreign keys for a given table name. func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) { var fkeys []bdb.ForeignKey diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index bb73574..6a20ba0 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/go-sql-driver/mysql" - "github.com/pkg/errors" "github.com/lbryio/sqlboiler/bdb" + "github.com/pkg/errors" ) // TinyintAsBool is a global that is set from main.go if a user specifies @@ -232,6 +232,46 @@ func (m *MySQLDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryKey, return pkey, nil } +// UniqueKeyInfo retrieves the unique keys for a given table name. +func (m *MySQLDriver) UniqueKeyInfo(schema, tableName string) ([]bdb.UniqueKey, error) { + var ukeys []bdb.UniqueKey + + query := ` + select tc.table_name, tc.constraint_name, GROUP_CONCAT(kcu.column_name) + from information_schema.table_constraints tc + left join information_schema.key_column_usage kcu on tc.constraint_name = kcu.constraint_name and tc.table_name = kcu.table_name and tc.table_schema = kcu.table_schema + where tc.table_schema = ? and tc.table_name = ? and tc.constraint_type = "UNIQUE" + group by tc.table_name, tc.constraint_name + ` + + var rows *sql.Rows + var err error + if rows, err = m.dbConn.Query(query, schema, tableName); err != nil { + return nil, err + } + + for rows.Next() { + var ukey bdb.UniqueKey + var columns string + + //ukey.Table = tableName + err = rows.Scan(&ukey.Table, &ukey.Name, &columns) + if err != nil { + return nil, err + } + + ukey.Columns = strings.Split(columns, ",") + + ukeys = append(ukeys, ukey) + } + + if err = rows.Err(); err != nil { + return nil, err + } + + return ukeys, nil +} + // ForeignKeyInfo retrieves the foreign keys for a given table name. func (m *MySQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) { var fkeys []bdb.ForeignKey diff --git a/bdb/drivers/postgres.go b/bdb/drivers/postgres.go index ad73a2e..4b6a9f4 100644 --- a/bdb/drivers/postgres.go +++ b/bdb/drivers/postgres.go @@ -8,10 +8,10 @@ import ( // Side-effect import sql driver - _ "github.com/lib/pq" - "github.com/pkg/errors" "github.com/lbryio/sqlboiler/bdb" "github.com/lbryio/sqlboiler/strmangle" + _ "github.com/lib/pq" + "github.com/pkg/errors" ) // PostgresDriver holds the database connection string and a handle @@ -266,6 +266,10 @@ func (p *PostgresDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryK return pkey, nil } +func (p *PostgresDriver) UniqueKeyInfo(schema, tableName string) ([]bdb.UniqueKey, error) { + return []bdb.UniqueKey{}, errors.New("not implemented") +} + // ForeignKeyInfo retrieves the foreign keys for a given table name. func (p *PostgresDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) { var fkeys []bdb.ForeignKey diff --git a/bdb/interface.go b/bdb/interface.go index dfb33bc..1fe179a 100644 --- a/bdb/interface.go +++ b/bdb/interface.go @@ -9,6 +9,7 @@ type Interface interface { TableNames(schema string, whitelist, blacklist []string) ([]string, error) Columns(schema, tableName string) ([]Column, error) PrimaryKeyInfo(schema, tableName string) (*PrimaryKey, error) + UniqueKeyInfo(schema, tableName string) ([]UniqueKey, error) ForeignKeyInfo(schema, tableName string) ([]ForeignKey, error) // TranslateColumnType takes a Database column type and returns a go column type. @@ -63,6 +64,10 @@ func Tables(db Interface, schema string, whitelist, blacklist []string) ([]Table return nil, errors.Wrapf(err, "unable to fetch table pkey info (%s)", name) } + if t.UKeys, err = db.UniqueKeyInfo(schema, name); err != nil { + return nil, errors.Wrapf(err, "unable to fetch table ukey info (%s)", name) + } + if t.FKeys, err = db.ForeignKeyInfo(schema, name); err != nil { return nil, errors.Wrapf(err, "unable to fetch table fkey info (%s)", name) } diff --git a/bdb/keys.go b/bdb/keys.go index 909ada2..8007843 100644 --- a/bdb/keys.go +++ b/bdb/keys.go @@ -8,6 +8,13 @@ type PrimaryKey struct { Columns []string } +// UniqueKey represents a unique key constraint in a database +type UniqueKey struct { + Table string + Name string + Columns []string +} + // ForeignKey represents a foreign key constraint in a database type ForeignKey struct { Table string diff --git a/bdb/table.go b/bdb/table.go index 62dcd32..d5cbb50 100644 --- a/bdb/table.go +++ b/bdb/table.go @@ -11,6 +11,7 @@ type Table struct { Columns []Column PKey *PrimaryKey + UKeys []UniqueKey FKeys []ForeignKey IsJoinTable bool diff --git a/boilingcore/imports.go b/boilingcore/imports.go index fc148c7..62b15bb 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -182,12 +182,13 @@ func newImporter() importer { "boil_queries": imports{ standard: importList{ `"database/sql"`, - `"strings"`, + `"fmt"`, }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, + `"github.com/lbryio/sqlboiler/strmangle"`, `"github.com/pkg/errors"`, }, }, diff --git a/boilingcore/templates.go b/boilingcore/templates.go index 62c0ae5..86c78ce 100644 --- a/boilingcore/templates.go +++ b/boilingcore/templates.go @@ -8,10 +8,10 @@ import ( "strings" "text/template" - "github.com/pkg/errors" "github.com/lbryio/sqlboiler/bdb" "github.com/lbryio/sqlboiler/queries" "github.com/lbryio/sqlboiler/strmangle" + "github.com/pkg/errors" ) // templateData for sqlboiler templates diff --git a/templates/23_merge.tpl b/templates/23_merge.tpl index c7e27f5..35ca404 100644 --- a/templates/23_merge.tpl +++ b/templates/23_merge.tpl @@ -33,16 +33,33 @@ func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID return errors.New("Secondary {{$tableNameSingular}} not found") } - relatedFields := map[string]string{ + foreignKeys := []foreignKey{ {{- range .Tables -}} {{- range .FKeys -}} {{- if eq $dot.Table.Name .ForeignTable }} - "{{.Table }}": "{{ .Column}}", + {foreignTable: "{{.Table}}", foreignColumn: "{{.Column}}"}, {{- end -}} {{- end -}} {{- end }} } - err = mergeModels(tx, primaryID, secondaryID, relatedFields) + + conflictingKeys := []conflictingUniqueKey{ + {{- range .Tables -}} + {{- $table := . -}} + {{- range .FKeys -}} + {{- $fk := . -}} + {{- if eq $dot.Table.Name .ForeignTable -}} + {{- range $table.UKeys -}} + {{- if setInclude $fk.Column .Columns }} + {table: "{{$fk.Table}}", objectIdColumn: "{{$fk.Column}}", columns: []string{`{{ .Columns | join "`,`" }}`}}, + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end }} + } + + err = mergeModels(tx, primaryID, secondaryID, foreignKeys, conflictingKeys) if err != nil { tx.Rollback() return err diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index d129e43..8ff4edd 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -20,39 +20,97 @@ func NewQuery(exec boil.Executor, mods ...qm.QueryMod) *queries.Query { return q } -func mergeModels(tx *sql.Tx, primaryID uint64, secondaryID uint64, relatedFields map[string]string) error { - if len(relatedFields) < 1 { - return nil - } - - for table, column := range relatedFields { - // TODO: use NewQuery here, not plain sql - query := "UPDATE " + table + " SET " + column + " = ? WHERE " + column + " = ?" - _, err := tx.Exec(query, primaryID, secondaryID) - if err != nil { - return errors.WithStack(err) - } - } - return checkMerge(tx, relatedFields) -} - -func checkMerge(tx *sql.Tx, fields map[string]string) error { - columns := []interface{}{} - seenColumns := map[string]bool{} - placeholders := []string{} - for _, column := range fields { - if _, ok := seenColumns[column]; !ok { - columns = append(columns, column) - seenColumns[column] = true - placeholders = append(placeholders, "?") +func mergeModels(tx *sql.Tx, primaryID uint64, secondaryID uint64, foreignKeys []foreignKey, conflictingKeys []conflictingUniqueKey) error { + if len(foreignKeys) < 1 { + return nil + } + var err error + for _, conflict := range conflictingKeys { + err = deleteConflictsBeforeMerge(tx, conflict, primaryID, secondaryID) + if err != nil { + return errors.WithStack(err) } } - placeholder := strings.Join(placeholders, ", ") + for _, fk := range foreignKeys { + // TODO: use NewQuery here, not plain sql + query := fmt.Sprintf( + "UPDATE %s SET %s = %s WHERE %s = %s", + fk.foreignTable, fk.foreignColumn, strmangle.Placeholders(dialect.IndexPlaceholders, 1, 1, 1), + fk.foreignColumn, strmangle.Placeholders(dialect.IndexPlaceholders, 1, 2, 1), + ) + _, err = tx.Exec(query, primaryID, secondaryID) + if err != nil { + return errors.WithStack(err) + } + } + return checkMerge(tx, foreignKeys) +} - q := `SELECT table_name, column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND column_name IN (` + placeholder + `)` - rows, err := tx.Query(q, columns...) +func deleteConflictsBeforeMerge(tx *sql.Tx, conflict conflictingUniqueKey, primaryID uint64, secondaryID uint64) error { + conflictingColumns := strmangle.SetComplement(conflict.columns, []string{conflict.objectIdColumn}) + + if len(conflictingColumns) < 1 { + return nil + } else if len(conflictingColumns) > 1 { + return errors.New("this doesnt work for unique keys with more than two columns (yet)") + } + + query := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s IN (%s) GROUP BY %s HAVING count(distinct %s) > 1", + conflictingColumns[0], conflict.table, conflict.objectIdColumn, + strmangle.Placeholders(dialect.IndexPlaceholders, 2, 1, 1), + conflictingColumns[0], conflict.objectIdColumn, + ) + + rows, err := tx.Query(query, primaryID, secondaryID) + defer rows.Close() + if err != nil { + return errors.WithStack(err) + } + + args := []interface{}{secondaryID} + for rows.Next() { + var value string + err = rows.Scan(&value) + if err != nil { + return errors.WithStack(err) + } + args = append(args, value) + } + + query = fmt.Sprintf( + "DELETE FROM %s WHERE %s = %s AND %s IN (%s)", + conflict.table, conflict.objectIdColumn, strmangle.Placeholders(dialect.IndexPlaceholders, 1, 1, 1), + conflictingColumns[0], strmangle.Placeholders(dialect.IndexPlaceholders, len(args)-1, 2, 1), + ) + + _, err = tx.Exec(query, args...) + if err != nil { + return errors.WithStack(err) + } + return nil +} + +func checkMerge(tx *sql.Tx, foreignKeys []foreignKey) error { + uniqueColumns := []interface{}{} + uniqueColumnNames := map[string]bool{} + handledTablesColumns := map[string]bool{} + + for _, fk := range foreignKeys { + handledTablesColumns[fk.foreignTable+"."+fk.foreignColumn] = true + if _, ok := uniqueColumnNames[fk.foreignColumn]; !ok { + uniqueColumns = append(uniqueColumns, fk.foreignColumn) + uniqueColumnNames[fk.foreignColumn] = true + } + } + + q := fmt.Sprintf( + `SELECT table_name, column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND column_name IN (%s)`, + strmangle.Placeholders(dialect.IndexPlaceholders, len(uniqueColumns), 1, 1), + ) + rows, err := tx.Query(q, uniqueColumns...) defer rows.Close() if err != nil { return errors.WithStack(err) @@ -66,7 +124,7 @@ func checkMerge(tx *sql.Tx, fields map[string]string) error { return errors.WithStack(err) } - if _, exists := fields[tableName]; !exists { + if _, exists := handledTablesColumns[tableName+"."+columnName]; !exists { return errors.New("Missing merge for " + tableName + "." + columnName) } } diff --git a/templates/singleton/boil_types.tpl b/templates/singleton/boil_types.tpl index 85e05d9..aa61bf9 100644 --- a/templates/singleton/boil_types.tpl +++ b/templates/singleton/boil_types.tpl @@ -6,6 +6,22 @@ type Nullable interface { IsZero() bool } +// foreignKey connects two tables. When merging records, foreign keys from secondary record must +// be reassigned to primary record. +type foreignKey struct { + foreignTable string + foreignColumn string +} + +// conflictingUniqueKey records a merge conflict. If two rows exist with the same value in the +// conflicting column for two records being merged, one row must be deleted. +type conflictingUniqueKey struct { + table string + objectIdColumn string + columns []string +} + + // ErrSyncFail occurs during insert when the record could not be retrieved in // order to populate default value information. This usually happens when LastInsertId // fails or there was a primary key configuration that was not resolvable. From 09c585cdb1056fc48c59541b0c66f20a81282a25 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Thu, 1 Jun 2017 09:58:50 -0400 Subject: [PATCH 151/179] dont run delete query when there's nothing to delete --- templates/singleton/boil_queries.tpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index 8ff4edd..248fae1 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -80,6 +80,11 @@ func deleteConflictsBeforeMerge(tx *sql.Tx, conflict conflictingUniqueKey, prima args = append(args, value) } + // if no rows found, no need to delete anything + if len(args) < 2 { + return nil + } + query = fmt.Sprintf( "DELETE FROM %s WHERE %s = %s AND %s IN (%s)", conflict.table, conflict.objectIdColumn, strmangle.Placeholders(dialect.IndexPlaceholders, 1, 1, 1), From ed423a360605cf1d4309cc78d969fce5674d7aea Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Mon, 12 Jun 2017 14:02:04 -0400 Subject: [PATCH 152/179] detect autoincrement column, fix lastID in upsert when update doesnt change anything --- bdb/drivers/mock.go | 4 ++++ bdb/drivers/mssql.go | 4 ++++ bdb/drivers/mysql.go | 32 ++++++++++++++++++++++++++++++++ bdb/drivers/postgres.go | 4 ++++ bdb/interface.go | 5 +++++ bdb/table.go | 4 +++- main.go | 6 +++--- queries/query_builders.go | 7 ++++++- templates/01_types.tpl | 1 + templates/17_upsert.tpl | 2 +- 10 files changed, 63 insertions(+), 6 deletions(-) diff --git a/bdb/drivers/mock.go b/bdb/drivers/mock.go index 810eb48..d8a5aa0 100644 --- a/bdb/drivers/mock.go +++ b/bdb/drivers/mock.go @@ -62,6 +62,10 @@ func (m *MockDriver) UniqueKeyInfo(schema, tableName string) ([]bdb.UniqueKey, e return []bdb.UniqueKey{}, nil } +func (m *MockDriver) AutoincrementInfo(schema, tableName string) (string, error) { + return "", nil +} + // ForeignKeyInfo returns a list of mock foreignkeys func (m *MockDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) { return map[string][]bdb.ForeignKey{ diff --git a/bdb/drivers/mssql.go b/bdb/drivers/mssql.go index 70b9ed9..79f3010 100644 --- a/bdb/drivers/mssql.go +++ b/bdb/drivers/mssql.go @@ -245,6 +245,10 @@ func (m *MSSQLDriver) UniqueKeyInfo(schema, tableName string) ([]bdb.UniqueKey, return []bdb.UniqueKey{}, errors.New("not implemented") } +func (m *MSSQLDriver) AutoincrementInfo(schema, tableName string) (string, error) { + return "", errors.New("not implemented") +} + // ForeignKeyInfo retrieves the foreign keys for a given table name. func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) { var fkeys []bdb.ForeignKey diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index 6a20ba0..41510ee 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -272,6 +272,38 @@ func (m *MySQLDriver) UniqueKeyInfo(schema, tableName string) ([]bdb.UniqueKey, return ukeys, nil } +// AutoincrementInfo retrieves the autoincrement column for a given table name, if one exists. +func (m *MySQLDriver) AutoincrementInfo(schema, tableName string) (string, error) { + query := ` + select column_name + from information_schema.columns + where table_schema = ? and table_name = ? and extra like "%auto_increment%" + ` + + var rows *sql.Rows + var err error + if rows, err = m.dbConn.Query(query, schema, tableName); err != nil { + return "", err + } + + for rows.Next() { + var column string + + err = rows.Scan(&column) + if err != nil { + return "", err + } + + return column, nil + } + + if err = rows.Err(); err != nil { + return "", err + } + + return "", nil +} + // ForeignKeyInfo retrieves the foreign keys for a given table name. func (m *MySQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) { var fkeys []bdb.ForeignKey diff --git a/bdb/drivers/postgres.go b/bdb/drivers/postgres.go index 4b6a9f4..9af7edd 100644 --- a/bdb/drivers/postgres.go +++ b/bdb/drivers/postgres.go @@ -270,6 +270,10 @@ func (p *PostgresDriver) UniqueKeyInfo(schema, tableName string) ([]bdb.UniqueKe return []bdb.UniqueKey{}, errors.New("not implemented") } +func (p *PostgresDriver) AutoincrementInfo(schema, tableName string) (string, error) { + return "", errors.New("not implemented") +} + // ForeignKeyInfo retrieves the foreign keys for a given table name. func (p *PostgresDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) { var fkeys []bdb.ForeignKey diff --git a/bdb/interface.go b/bdb/interface.go index 1fe179a..7f4baa5 100644 --- a/bdb/interface.go +++ b/bdb/interface.go @@ -10,6 +10,7 @@ type Interface interface { Columns(schema, tableName string) ([]Column, error) PrimaryKeyInfo(schema, tableName string) (*PrimaryKey, error) UniqueKeyInfo(schema, tableName string) ([]UniqueKey, error) + AutoincrementInfo(schema, tableName string) (string, error) ForeignKeyInfo(schema, tableName string) ([]ForeignKey, error) // TranslateColumnType takes a Database column type and returns a go column type. @@ -72,6 +73,10 @@ func Tables(db Interface, schema string, whitelist, blacklist []string) ([]Table return nil, errors.Wrapf(err, "unable to fetch table fkey info (%s)", name) } + if t.AutoIncrementColumn, err = db.AutoincrementInfo(schema, name); err != nil { + return nil, errors.Wrapf(err, "unable to fetch table autoincrement info (%s)", name) + } + setIsJoinTable(&t) tables = append(tables, t) diff --git a/bdb/table.go b/bdb/table.go index d5cbb50..13355aa 100644 --- a/bdb/table.go +++ b/bdb/table.go @@ -8,7 +8,9 @@ type Table struct { // For dbs with real schemas, like Postgres. // Example value: "schema_name"."table_name" SchemaName string - Columns []Column + + Columns []Column + AutoIncrementColumn string PKey *PrimaryKey UKeys []UniqueKey diff --git a/main.go b/main.go index 5103116..8344e57 100644 --- a/main.go +++ b/main.go @@ -8,13 +8,13 @@ import ( "strings" "github.com/kat-co/vala" - "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/lbryio/sqlboiler/bdb/drivers" "github.com/lbryio/sqlboiler/boilingcore" + "github.com/spf13/cobra" + "github.com/spf13/viper" ) -const sqlBoilerVersion = "2.3.0" +const sqlBoilerVersion = "2.4.0+lbry" var ( cmdState *boilingcore.State diff --git a/queries/query_builders.go b/queries/query_builders.go index d221aca..f884f91 100644 --- a/queries/query_builders.go +++ b/queries/query_builders.go @@ -190,7 +190,7 @@ func buildUpdateQuery(q *Query) (*bytes.Buffer, []interface{}) { } // BuildUpsertQueryMySQL builds a SQL statement string using the upsertData provided. -func BuildUpsertQueryMySQL(dia Dialect, tableName string, update, whitelist []string) string { +func BuildUpsertQueryMySQL(dia Dialect, tableName string, update, whitelist []string, autoIncrementCol string) string { whitelist = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, whitelist) buf := strmangle.GetBuffer() @@ -220,6 +220,11 @@ func BuildUpsertQueryMySQL(dia Dialect, tableName string, update, whitelist []st strmangle.Placeholders(dia.IndexPlaceholders, len(whitelist), 1, 1), ) + // https://stackoverflow.com/questions/778534/mysql-on-duplicate-key-last-insert-id + if autoIncrementCol != "" { + buf.WriteString(autoIncrementCol + " = LAST_INSERT_ID(" + autoIncrementCol + "), ") + } + for i, v := range update { if i != 0 { buf.WriteByte(',') diff --git a/templates/01_types.tpl b/templates/01_types.tpl index 9340e42..5aac35f 100644 --- a/templates/01_types.tpl +++ b/templates/01_types.tpl @@ -10,6 +10,7 @@ var ( {{$varNameSingular}}ColumnsWithoutDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}ColumnsWithDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault true | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}PrimaryKeyColumns = []string{{"{"}}{{.Table.PKey.Columns | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}} + {{$varNameSingular}}AutoIncrementColumn = "{{.Table.AutoIncrementColumn }}" ) type ( diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index f3d51e7..dbce58c 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -115,7 +115,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName } cache.query = queries.BuildUpsertQueryPostgres(dialect, "{{$schemaTable}}", updateOnConflict, ret, update, conflict, insert) {{else if eq .DriverName "mysql"}} - cache.query = queries.BuildUpsertQueryMySQL(dialect, "{{.Table.Name}}", update, insert) + cache.query = queries.BuildUpsertQueryMySQL(dialect, "{{.Table.Name}}", update, insert, {{$varNameSingular}}AutoIncrementColumn) cache.retQuery = fmt.Sprintf( "SELECT %s FROM {{.LQ}}{{.Table.Name}}{{.RQ}} WHERE {{whereClause .LQ .RQ 0 .Table.PKey.Columns}}", strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, ret), ","), From 05c7f7d06a7f9c1a1c845a91ee04c9020e8e7ba3 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Thu, 6 Jul 2017 15:15:00 -0400 Subject: [PATCH 153/179] switch to more complete null package, implement nicer sql logging --- boilingcore/imports.go | 42 +++++---- boilingcore/imports_test.go | 10 +- queries/helpers_test.go | 2 +- queries/query.go | 94 +++++++++++++++++-- randomize/randomize.go | 2 +- randomize/randomize_test.go | 2 +- templates/07_relationship_to_one_eager.tpl | 6 +- .../08_relationship_one_to_one_eager.tpl | 6 +- templates/09_relationship_to_many_eager.tpl | 6 +- templates/10_relationship_to_one_setops.tpl | 7 +- .../11_relationship_one_to_one_setops.tpl | 7 +- templates/12_relationship_to_many_setops.tpl | 28 ++++-- templates/15_insert.tpl | 14 ++- templates/16_update.tpl | 14 ++- templates/17_upsert.tpl | 14 ++- templates/18_delete.tpl | 40 ++++---- templates/20_exists.tpl | 7 +- templates/22_query.tpl | 2 +- templates/23_merge.tpl | 2 +- templates/singleton/boil_queries.tpl | 66 +++++++++++++ templates/singleton/boil_types.tpl | 5 - 21 files changed, 291 insertions(+), 85 deletions(-) diff --git a/boilingcore/imports.go b/boilingcore/imports.go index 62b15bb..2c31c63 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -170,11 +170,12 @@ func newImporter() importer { `"time"`, }, thirdParty: importList{ - `"github.com/pkg/errors"`, + `"github.com/lbryio/null.go"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, `"github.com/lbryio/sqlboiler/strmangle"`, + `"github.com/pkg/errors"`, }, } @@ -183,6 +184,10 @@ func newImporter() importer { standard: importList{ `"database/sql"`, `"fmt"`, + `"reflect"`, + `"strconv"`, + `"strings"`, + `"time"`, }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, @@ -190,6 +195,7 @@ func newImporter() importer { `"github.com/lbryio/sqlboiler/queries/qm"`, `"github.com/lbryio/sqlboiler/strmangle"`, `"github.com/pkg/errors"`, + `"github.com/lbryio/null.go"`, }, }, "boil_types": { @@ -315,55 +321,55 @@ func newImporter() importer { // TranslateColumnType to see the type assignments. imp.BasedOnType = mapImports{ "null.Float32": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Float64": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Int": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Int8": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Int16": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Int32": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Int64": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Uint": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Uint8": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Uint16": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Uint32": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Uint64": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.String": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Bool": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Time": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.JSON": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "null.Bytes": { - thirdParty: importList{`"gopkg.in/nullbio/null.v6"`}, + thirdParty: importList{`"github.com/lbryio/null.go"`}, }, "time.Time": { standard: importList{`"time"`}, diff --git a/boilingcore/imports_test.go b/boilingcore/imports_test.go index ff2362d..b25eece 100644 --- a/boilingcore/imports_test.go +++ b/boilingcore/imports_test.go @@ -246,7 +246,7 @@ func TestCombineTypeImports(t *testing.T) { }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, - `"gopkg.in/nullbio/null.v6"`, + `"github.com/lbryio/null.go"`, }, } @@ -281,7 +281,7 @@ func TestCombineTypeImports(t *testing.T) { }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, - `"gopkg.in/nullbio/null.v6"`, + `"github.com/lbryio/null.go"`, }, } @@ -297,7 +297,7 @@ func TestCombineImports(t *testing.T) { a := imports{ standard: importList{"fmt"}, - thirdParty: importList{"github.com/lbryio/sqlboiler", "gopkg.in/nullbio/null.v6"}, + thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/null.go"}, } b := imports{ standard: importList{"os"}, @@ -309,8 +309,8 @@ func TestCombineImports(t *testing.T) { if c.standard[0] != "fmt" && c.standard[1] != "os" { t.Errorf("Wanted: fmt, os got: %#v", c.standard) } - if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "gopkg.in/nullbio/null.v6" { - t.Errorf("Wanted: github.com/lbryio/sqlboiler, gopkg.in/nullbio/null.v6 got: %#v", c.thirdParty) + if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/null.go" { + t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/null.go got: %#v", c.thirdParty) } } diff --git a/queries/helpers_test.go b/queries/helpers_test.go index d37fcd9..ab4b4ac 100644 --- a/queries/helpers_test.go +++ b/queries/helpers_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - null "gopkg.in/nullbio/null.v6" + null "github.com/lbryio/null.go" ) type testObj struct { diff --git a/queries/query.go b/queries/query.go index be539e5..b3ff830 100644 --- a/queries/query.go +++ b/queries/query.go @@ -3,8 +3,15 @@ package queries import ( "database/sql" "fmt" + "reflect" + "strconv" + "strings" + "time" + "github.com/lbryio/null.go" "github.com/lbryio/sqlboiler/boil" + + "github.com/go-errors/errors" ) // joinKind is the type of join @@ -105,8 +112,11 @@ func RawG(query string, args ...interface{}) *Query { func (q *Query) Exec() (sql.Result, error) { qs, args := buildQuery(q) if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, qs) - fmt.Fprintln(boil.DebugWriter, args) + qStr, err := interpolateParams(qs, args...) + if err != nil { + return nil, err + } + fmt.Fprintln(boil.DebugWriter, qStr) } return q.executor.Exec(qs, args...) } @@ -115,8 +125,11 @@ func (q *Query) Exec() (sql.Result, error) { func (q *Query) QueryRow() *sql.Row { qs, args := buildQuery(q) if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, qs) - fmt.Fprintln(boil.DebugWriter, args) + qStr, err := interpolateParams(qs, args...) + if err != nil { + panic(err) + } + fmt.Fprintln(boil.DebugWriter, qStr) } return q.executor.QueryRow(qs, args...) } @@ -125,8 +138,11 @@ func (q *Query) QueryRow() *sql.Row { func (q *Query) Query() (*sql.Rows, error) { qs, args := buildQuery(q) if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, qs) - fmt.Fprintln(boil.DebugWriter, args) + qStr, err := interpolateParams(qs, args...) + if err != nil { + return nil, err + } + fmt.Fprintln(boil.DebugWriter, qStr) } return q.executor.Query(qs, args...) } @@ -280,3 +296,69 @@ func AppendGroupBy(q *Query, clause string) { func AppendOrderBy(q *Query, clause string) { q.orderBy = append(q.orderBy, clause) } + +// duplicated in boil_queries.tpl +func interpolateParams(query string, args ...interface{}) (string, error) { + for i := 0; i < len(args); i++ { + field := reflect.ValueOf(args[i]) + + if value, ok := field.Interface().(time.Time); ok { + query = strings.Replace(query, "?", `"`+value.Format("2006-01-02 15:04:05")+`"`, 1) + } else if nullable, ok := field.Interface().(null.Nullable); ok { + if nullable.IsNull() { + query = strings.Replace(query, "?", "NULL", 1) + } else { + switch field.Type() { + case reflect.TypeOf(null.Time{}): + query = strings.Replace(query, "?", `"`+field.Interface().(null.Time).Time.Format("2006-01-02 15:04:05")+`"`, 1) + case reflect.TypeOf(null.Int{}): + query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int).Int), 10), 1) + case reflect.TypeOf(null.Int8{}): + query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int8).Int8), 10), 1) + case reflect.TypeOf(null.Int16{}): + query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int16).Int16), 10), 1) + case reflect.TypeOf(null.Int32{}): + query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int32).Int32), 10), 1) + case reflect.TypeOf(null.Int64{}): + query = strings.Replace(query, "?", strconv.FormatInt(field.Interface().(null.Int64).Int64, 10), 1) + case reflect.TypeOf(null.Uint{}): + query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint).Uint), 10), 1) + case reflect.TypeOf(null.Uint8{}): + query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint8).Uint8), 10), 1) + case reflect.TypeOf(null.Uint16{}): + query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint16).Uint16), 10), 1) + case reflect.TypeOf(null.Uint32{}): + query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint32).Uint32), 10), 1) + case reflect.TypeOf(null.Uint64{}): + query = strings.Replace(query, "?", strconv.FormatUint(field.Interface().(null.Uint64).Uint64, 10), 1) + case reflect.TypeOf(null.String{}): + query = strings.Replace(query, "?", `"`+field.Interface().(null.String).String+`"`, 1) + case reflect.TypeOf(null.Bool{}): + if field.Interface().(null.Bool).Bool { + query = strings.Replace(query, "?", "1", 1) + } else { + query = strings.Replace(query, "?", "0", 1) + } + } + } + } else { + switch field.Kind() { + case reflect.Bool: + boolString := "0" + if field.Bool() { + boolString = "1" + } + query = strings.Replace(query, "?", boolString, 1) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + query = strings.Replace(query, "?", strconv.FormatInt(field.Int(), 10), 1) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + query = strings.Replace(query, "?", strconv.FormatUint(field.Uint(), 10), 1) + case reflect.String: + query = strings.Replace(query, "?", `"`+field.String()+`"`, 1) + default: + return "", errors.New("Dont know how to interpolate type " + field.Type().String()) + } + } + } + return query, nil +} diff --git a/randomize/randomize.go b/randomize/randomize.go index ea99c43..8deb88d 100644 --- a/randomize/randomize.go +++ b/randomize/randomize.go @@ -14,7 +14,7 @@ import ( "sync/atomic" "time" - null "gopkg.in/nullbio/null.v6" + null "github.com/lbryio/null.go" "github.com/pkg/errors" "github.com/satori/go.uuid" diff --git a/randomize/randomize_test.go b/randomize/randomize_test.go index 6f117b7..ccef115 100644 --- a/randomize/randomize_test.go +++ b/randomize/randomize_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - null "gopkg.in/nullbio/null.v6" + null "github.com/lbryio/null.go" ) func TestRandomizeStruct(t *testing.T) { diff --git a/templates/07_relationship_to_one_eager.tpl b/templates/07_relationship_to_one_eager.tpl index 3b24f1a..3bf42f4 100644 --- a/templates/07_relationship_to_one_eager.tpl +++ b/templates/07_relationship_to_one_eager.tpl @@ -40,7 +40,11 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula ) if boil.DebugMode { - fmt.Fprintf(boil.DebugWriter, "%s\n%v\n", query, args) + qStr, err := interpolateParams(query, args...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } results, err := e.Query(query, args...) diff --git a/templates/08_relationship_one_to_one_eager.tpl b/templates/08_relationship_one_to_one_eager.tpl index 37d1af0..1fbf5f7 100644 --- a/templates/08_relationship_one_to_one_eager.tpl +++ b/templates/08_relationship_one_to_one_eager.tpl @@ -40,7 +40,11 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula ) if boil.DebugMode { - fmt.Fprintf(boil.DebugWriter, "%s\n%v\n", query, args) + qStr, err := interpolateParams(query, args...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } results, err := e.Query(query, args...) diff --git a/templates/09_relationship_to_many_eager.tpl b/templates/09_relationship_to_many_eager.tpl index 1603188..3ec5bf9 100644 --- a/templates/09_relationship_to_many_eager.tpl +++ b/templates/09_relationship_to_many_eager.tpl @@ -49,7 +49,11 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula {{end -}} if boil.DebugMode { - fmt.Fprintf(boil.DebugWriter, "%s\n%v\n", query, args) + qStr, err := interpolateParams(query, args...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } results, err := e.Query(query, args...) diff --git a/templates/10_relationship_to_one_setops.tpl b/templates/10_relationship_to_one_setops.tpl index 14f726e..b293b48 100644 --- a/templates/10_relationship_to_one_setops.tpl +++ b/templates/10_relationship_to_one_setops.tpl @@ -53,8 +53,11 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{related.{{$txt.ForeignTable.ColumnNameGo}}, o.{{$dot.Table.PKey.Columns | stringMap $dot.StringFuncs.titleCase | join ", o."}}{{"}"}} if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, updateQuery) - fmt.Fprintln(boil.DebugWriter, values) + qStr, err := interpolateParams(updateQuery, values...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } if _, err = exec.Exec(updateQuery, values...); err != nil { diff --git a/templates/11_relationship_one_to_one_setops.tpl b/templates/11_relationship_one_to_one_setops.tpl index 826eec2..d4583ca 100644 --- a/templates/11_relationship_one_to_one_setops.tpl +++ b/templates/11_relationship_one_to_one_setops.tpl @@ -59,8 +59,11 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{o.{{$txt.LocalTable.ColumnNameGo}}, related.{{$foreignPKeyCols | stringMap $dot.StringFuncs.titleCase | join ", related."}}{{"}"}} if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, updateQuery) - fmt.Fprintln(boil.DebugWriter, values) + qStr, err := interpolateParams(updateQuery, values...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } if _, err = exec.Exec(updateQuery, values...); err != nil { diff --git a/templates/12_relationship_to_many_setops.tpl b/templates/12_relationship_to_many_setops.tpl index 06159ee..35a4bd4 100644 --- a/templates/12_relationship_to_many_setops.tpl +++ b/templates/12_relationship_to_many_setops.tpl @@ -66,8 +66,11 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{o.{{$txt.LocalTable.ColumnNameGo}}, rel.{{$foreignPKeyCols | stringMap $dot.StringFuncs.titleCase | join ", rel."}}{{"}"}} if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, updateQuery) - fmt.Fprintln(boil.DebugWriter, values) + qStr, err := interpolateParams(updateQuery, values...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } if _, err = exec.Exec(updateQuery, values...); err != nil { @@ -87,8 +90,11 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}, rel.{{$txt.ForeignTable.ColumnNameGo}}} if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, query) - fmt.Fprintln(boil.DebugWriter, values) + qStr, err := interpolateParams(query, values...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } _, err = exec.Exec(query, values...) @@ -184,8 +190,11 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}} {{end -}} if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, query) - fmt.Fprintln(boil.DebugWriter, values) + qStr, err := interpolateParams(query, values...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } _, err := exec.Exec(query, values...) @@ -260,8 +269,11 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Exec } if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, query) - fmt.Fprintln(boil.DebugWriter, values) + qStr, err := interpolateParams(query, values...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } _, err = exec.Exec(query, values...) diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 870e20d..8cd30b9 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -98,8 +98,11 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string vals := queries.ValuesFromMapping(value, cache.valueMapping) if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, cache.query) - fmt.Fprintln(boil.DebugWriter, vals) + qStr, err := interpolateParams(cache.query, vals...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } {{if .UseLastInsertID -}} @@ -144,8 +147,11 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string } if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, cache.retQuery) - fmt.Fprintln(boil.DebugWriter, identifierCols...) + qStr, err := interpolateParams(cache.retQuery, identifierCols...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } err = exec.QueryRow(cache.retQuery, identifierCols...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...) diff --git a/templates/16_update.tpl b/templates/16_update.tpl index bc4a6f5..1ac460b 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -78,8 +78,11 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping) if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, cache.query) - fmt.Fprintln(boil.DebugWriter, values) + qStr, err := interpolateParams(cache.query, values...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } _, err = exec.Exec(cache.query, values...) @@ -170,8 +173,11 @@ func (o {{$tableNameSingular}}Slice) UpdateAll(exec boil.Executor, cols M) error strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}len(colNames)+1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(o))) if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, sql) - fmt.Fprintln(boil.DebugWriter, args...) + qStr, err := interpolateParams(sql, args...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } _, err := exec.Exec(sql, args...) diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index dbce58c..5cf6a14 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -147,8 +147,11 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName } if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, cache.query) - fmt.Fprintln(boil.DebugWriter, vals) + qStr, err := interpolateParams(cache.query, vals...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } {{if .UseLastInsertID -}} @@ -193,8 +196,11 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName } if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, cache.retQuery) - fmt.Fprintln(boil.DebugWriter, identifierCols...) + qStr, err := interpolateParams(cache.retQuery, identifierCols...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } err = exec.QueryRow(cache.retQuery, identifierCols...).Scan(returns...) diff --git a/templates/18_delete.tpl b/templates/18_delete.tpl index e9a5177..20e819a 100644 --- a/templates/18_delete.tpl +++ b/templates/18_delete.tpl @@ -6,7 +6,7 @@ // Panics on error. func (o *{{$tableNameSingular}}) DeleteP(exec boil.Executor) { if err := o.Delete(exec); err != nil { - panic(boil.WrapErr(err)) + panic(boil.WrapErr(err)) } } @@ -14,7 +14,7 @@ func (o *{{$tableNameSingular}}) DeleteP(exec boil.Executor) { // DeleteG will match against the primary key column to find the record to delete. func (o *{{$tableNameSingular}}) DeleteG() error { if o == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}} provided for deletion") + return errors.New("{{.PkgName}}: no {{$tableNameSingular}} provided for deletion") } return o.Delete(boil.GetDB()) @@ -25,7 +25,7 @@ func (o *{{$tableNameSingular}}) DeleteG() error { // Panics on error. func (o *{{$tableNameSingular}}) DeleteGP() { if err := o.DeleteG(); err != nil { - panic(boil.WrapErr(err)) + panic(boil.WrapErr(err)) } } @@ -33,12 +33,12 @@ func (o *{{$tableNameSingular}}) DeleteGP() { // Delete will match against the primary key column to find the record to delete. func (o *{{$tableNameSingular}}) Delete(exec boil.Executor) error { if o == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}} provided for delete") + return errors.New("{{.PkgName}}: no {{$tableNameSingular}} provided for delete") } {{if not .NoHooks -}} if err := o.doBeforeDeleteHooks(exec); err != nil { - return err + return err } {{- end}} @@ -46,18 +46,21 @@ func (o *{{$tableNameSingular}}) Delete(exec boil.Executor) error { sql := "DELETE FROM {{$schemaTable}} WHERE {{if .Dialect.IndexPlaceholders}}{{whereClause .LQ .RQ 1 .Table.PKey.Columns}}{{else}}{{whereClause .LQ .RQ 0 .Table.PKey.Columns}}{{end}}" if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, sql) - fmt.Fprintln(boil.DebugWriter, args...) + qStr, err := interpolateParams(sql, args...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } _, err := exec.Exec(sql, args...) if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to delete from {{.Table.Name}}") + return errors.Wrap(err, "{{.PkgName}}: unable to delete from {{.Table.Name}}") } {{if not .NoHooks -}} if err := o.doAfterDeleteHooks(exec); err != nil { - return err + return err } {{- end}} @@ -67,21 +70,21 @@ func (o *{{$tableNameSingular}}) Delete(exec boil.Executor) error { // DeleteAllP deletes all rows, and panics on error. func (q {{$tableNameSingular}}Query) DeleteAllP() { if err := q.DeleteAll(); err != nil { - panic(boil.WrapErr(err)) + panic(boil.WrapErr(err)) } } // DeleteAll deletes all matching rows. func (q {{$tableNameSingular}}Query) DeleteAll() error { if q.Query == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}}Query provided for delete all") + return errors.New("{{.PkgName}}: no {{$tableNameSingular}}Query provided for delete all") } queries.SetDelete(q.Query) _, err := q.Query.Exec() if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to delete all from {{.Table.Name}}") + return errors.Wrap(err, "{{.PkgName}}: unable to delete all from {{.Table.Name}}") } return nil @@ -90,14 +93,14 @@ func (q {{$tableNameSingular}}Query) DeleteAll() error { // DeleteAllGP deletes all rows in the slice, and panics on error. func (o {{$tableNameSingular}}Slice) DeleteAllGP() { if err := o.DeleteAllG(); err != nil { - panic(boil.WrapErr(err)) + panic(boil.WrapErr(err)) } } // DeleteAllG deletes all rows in the slice. func (o {{$tableNameSingular}}Slice) DeleteAllG() error { if o == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}} slice provided for delete all") + return errors.New("{{.PkgName}}: no {{$tableNameSingular}} slice provided for delete all") } return o.DeleteAll(boil.GetDB()) } @@ -105,7 +108,7 @@ func (o {{$tableNameSingular}}Slice) DeleteAllG() error { // DeleteAllP deletes all rows in the slice, using an executor, and panics on error. func (o {{$tableNameSingular}}Slice) DeleteAllP(exec boil.Executor) { if err := o.DeleteAll(exec); err != nil { - panic(boil.WrapErr(err)) + panic(boil.WrapErr(err)) } } @@ -139,8 +142,11 @@ func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error { strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(o)) if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, sql) - fmt.Fprintln(boil.DebugWriter, args) + qStr, err := interpolateParams(sql, args...) + if err != nil { + return err + } + fmt.Fprintln(boil.DebugWriter, qStr) } _, err := exec.Exec(sql, args...) diff --git a/templates/20_exists.tpl b/templates/20_exists.tpl index 2e20fa6..4c4031a 100644 --- a/templates/20_exists.tpl +++ b/templates/20_exists.tpl @@ -14,8 +14,11 @@ func {{$tableNameSingular}}Exists(exec boil.Executor, {{$pkArgs}}) (bool, error) {{- end}} if boil.DebugMode { - fmt.Fprintln(boil.DebugWriter, sql) - fmt.Fprintln(boil.DebugWriter, {{$pkNames | join ", "}}) + qStr, err := interpolateParams(sql, {{$pkNames | join ", "}}) + if err != nil { + return false, err + } + fmt.Fprintln(boil.DebugWriter, qStr) } row := exec.QueryRow(sql, {{$pkNames | join ", "}}) diff --git a/templates/22_query.tpl b/templates/22_query.tpl index 512c717..2dcf973 100644 --- a/templates/22_query.tpl +++ b/templates/22_query.tpl @@ -16,7 +16,7 @@ func (q *{{$tableNameSingular}}Query) Where(filters {{$tableNameSingular}}Filter for i := 0; i < r.NumField(); i++ { f := r.Field(i) if f.Elem().IsValid() { - if nullable, ok := f.Elem().Interface().(Nullable); ok && nullable.IsZero() { + if nullable, ok := f.Elem().Interface().(null.Nullable); ok && nullable.IsNull() { queries.AppendWhere(q.Query, r.Type().Field(i).Tag.Get("boil")+" IS NULL") } else { queries.AppendWhere(q.Query, r.Type().Field(i).Tag.Get("boil")+" = ?", f.Elem().Interface()) diff --git a/templates/23_merge.tpl b/templates/23_merge.tpl index 35ca404..6b5cdd2 100644 --- a/templates/23_merge.tpl +++ b/templates/23_merge.tpl @@ -72,7 +72,7 @@ func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID pf := pr.Elem().Field(i) sf := sr.Elem().Field(i) if sf.IsValid() { - if nullable, ok := sf.Interface().(Nullable); ok && !nullable.IsZero() && pf.Interface().(Nullable).IsZero() { + if nullable, ok := sf.Interface().(null.Nullable); ok && !nullable.IsNull() && pf.Interface().(null.Nullable).IsNull() { pf.Set(sf) } } diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index 248fae1..11f103b 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -136,3 +136,69 @@ func checkMerge(tx *sql.Tx, foreignKeys []foreignKey) error { return nil } + +// duplicated in queries/query.go +func interpolateParams(query string, args ...interface{}) (string, error) { + for i := 0; i < len(args); i++ { + field := reflect.ValueOf(args[i]) + + if value, ok := field.Interface().(time.Time); ok { + query = strings.Replace(query, "?", `"`+value.Format("2006-01-02 15:04:05")+`"`, 1) + } else if nullable, ok := field.Interface().(null.Nullable); ok { + if nullable.IsNull() { + query = strings.Replace(query, "?", "NULL", 1) + } else { + switch field.Type() { + case reflect.TypeOf(null.Time{}): + query = strings.Replace(query, "?", `"`+field.Interface().(null.Time).Time.Format("2006-01-02 15:04:05")+`"`, 1) + case reflect.TypeOf(null.Int{}): + query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int).Int), 10), 1) + case reflect.TypeOf(null.Int8{}): + query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int8).Int8), 10), 1) + case reflect.TypeOf(null.Int16{}): + query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int16).Int16), 10), 1) + case reflect.TypeOf(null.Int32{}): + query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int32).Int32), 10), 1) + case reflect.TypeOf(null.Int64{}): + query = strings.Replace(query, "?", strconv.FormatInt(field.Interface().(null.Int64).Int64, 10), 1) + case reflect.TypeOf(null.Uint{}): + query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint).Uint), 10), 1) + case reflect.TypeOf(null.Uint8{}): + query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint8).Uint8), 10), 1) + case reflect.TypeOf(null.Uint16{}): + query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint16).Uint16), 10), 1) + case reflect.TypeOf(null.Uint32{}): + query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint32).Uint32), 10), 1) + case reflect.TypeOf(null.Uint64{}): + query = strings.Replace(query, "?", strconv.FormatUint(field.Interface().(null.Uint64).Uint64, 10), 1) + case reflect.TypeOf(null.String{}): + query = strings.Replace(query, "?", `"`+field.Interface().(null.String).String+`"`, 1) + case reflect.TypeOf(null.Bool{}): + if field.Interface().(null.Bool).Bool { + query = strings.Replace(query, "?", "1", 1) + } else { + query = strings.Replace(query, "?", "0", 1) + } + } + } + } else { + switch field.Kind() { + case reflect.Bool: + boolString := "0" + if field.Bool() { + boolString = "1" + } + query = strings.Replace(query, "?", boolString, 1) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + query = strings.Replace(query, "?", strconv.FormatInt(field.Int(), 10), 1) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + query = strings.Replace(query, "?", strconv.FormatUint(field.Uint(), 10), 1) + case reflect.String: + query = strings.Replace(query, "?", `"`+field.String()+`"`, 1) + default: + return "", errors.New("Dont know how to interpolate type " + field.Type().String()) + } + } + } + return query, nil +} diff --git a/templates/singleton/boil_types.tpl b/templates/singleton/boil_types.tpl index aa61bf9..547d05d 100644 --- a/templates/singleton/boil_types.tpl +++ b/templates/singleton/boil_types.tpl @@ -1,11 +1,6 @@ // M type is for providing columns and column values to UpdateAll. type M map[string]interface{} -// Nullable means the value may represent an sql NULL. It is implemented by null.* types. -type Nullable interface { - IsZero() bool -} - // foreignKey connects two tables. When merging records, foreign keys from secondary record must // be reassigned to primary record. type foreignKey struct { From 9c8262b702a2224d7e437b2e9a405fc698db506d Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Thu, 20 Jul 2017 10:50:55 -0400 Subject: [PATCH 154/179] fix exists() finisher --- templates/03_finishers.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/03_finishers.tpl b/templates/03_finishers.tpl index 13ea14d..df2081d 100644 --- a/templates/03_finishers.tpl +++ b/templates/03_finishers.tpl @@ -105,6 +105,7 @@ func (q {{$tableNameSingular}}Query) Exists() (bool, error) { var count int64 queries.SetCount(q.Query) + queries.SetSelect(q.Query, []string{}) queries.SetLimit(q.Query, 1) err := q.Query.QueryRow().Scan(&count) From 99a3a1d091d13401cd635e9b376ffec8f72ef156 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Tue, 1 Aug 2017 13:00:14 -0400 Subject: [PATCH 155/179] make merge compatible with an existing transaction --- templates/23_merge.tpl | 47 ++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/templates/23_merge.tpl b/templates/23_merge.tpl index 6b5cdd2..d84ed28 100644 --- a/templates/23_merge.tpl +++ b/templates/23_merge.tpl @@ -4,32 +4,42 @@ {{- else -}} {{- $dot := . }} // Merge combines two {{$tableNamePlural}} into one. The primary record will be kept, and the secondary will be deleted. -func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID uint64) error { - txdb, ok := exec.(boil.Beginner) +func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID uint64) (err error) { + tx, ok := exec.(boil.Transactor) if !ok { - return errors.New("database does not support transactions") - } + txdb, ok := exec.(boil.Beginner) + if !ok { + return errors.New("database does not support transactions") + } - tx, txErr := txdb.Begin() - if txErr != nil { - return txErr + tx, err = txdb.Begin() + if err != nil { + return err + } + + defer func() { + if p := recover(); p != nil { + tx.Rollback() + panic(p) // Rollback, then propagate panic + } else if err != nil { + tx.Rollback() + } else { + err = tx.Commit() + } + }() } primary, err := Find{{$tableNameSingular}}(tx, primaryID) if err != nil { - tx.Rollback() return err - } - if primary == nil { + } else if primary == nil { return errors.New("Primary {{$tableNameSingular}} not found") } secondary, err := Find{{$tableNameSingular}}(tx, secondaryID) if err != nil { - tx.Rollback() return err - } - if secondary == nil { + } else if secondary == nil { return errors.New("Secondary {{$tableNameSingular}} not found") } @@ -59,9 +69,13 @@ func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID {{- end }} } - err = mergeModels(tx, primaryID, secondaryID, foreignKeys, conflictingKeys) + sqlTx, ok := tx.(*sql.Tx) + if !ok { + return errors.New("tx must be an sql.Tx") + } + + err = mergeModels(sqlTx, primaryID, secondaryID, foreignKeys, conflictingKeys) if err != nil { - tx.Rollback() return err } @@ -80,17 +94,14 @@ func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID err = primary.Update(tx) if err != nil { - tx.Rollback() return errors.WithStack(err) } err = secondary.Delete(tx) if err != nil { - tx.Rollback() return errors.WithStack(err) } - tx.Commit() return nil } From 23f245776de75c7d884a2f208702147c417376ea Mon Sep 17 00:00:00 2001 From: Guy Tish <guy@ziprecruiter.com> Date: Mon, 26 Jun 2017 14:14:16 +0300 Subject: [PATCH 156/179] Added table columns and table names as anonymous struct --- templates/00_struct.tpl | 10 ++++++++++ templates/singleton/boil_table_names.tpl | 9 +++++++++ 2 files changed, 19 insertions(+) create mode 100644 templates/singleton/boil_table_names.tpl diff --git a/templates/00_struct.tpl b/templates/00_struct.tpl index f9c3a04..a2608fa 100644 --- a/templates/00_struct.tpl +++ b/templates/00_struct.tpl @@ -17,6 +17,16 @@ type {{$modelName}} struct { {{end -}} } +var {{$modelName}}Columns = struct { + {{range $column := .Table.Columns -}} + {{titleCase $column.Name}} string + {{end -}} +}{ + {{range $column := .Table.Columns -}} + {{titleCase $column.Name}}: "{{$column.Name}}", + {{end -}} +} + // {{$modelName}}Filter allows you to filter on any columns by making them all pointers. type {{$modelName}}Filter struct { {{range $column := .Table.Columns -}} diff --git a/templates/singleton/boil_table_names.tpl b/templates/singleton/boil_table_names.tpl new file mode 100644 index 0000000..062bcad --- /dev/null +++ b/templates/singleton/boil_table_names.tpl @@ -0,0 +1,9 @@ +var TableNames = struct { + {{range $table := .Tables -}} + {{titleCase $table.Name}} string + {{end -}} +}{ + {{range $table := .Tables -}} + {{titleCase $table.Name}}: "{{$table.Name}}", + {{end -}} +} From e3398120275addca81260d0d541abb99f1fa0dae Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 4 Jun 2017 12:29:04 -0700 Subject: [PATCH 157/179] Stop using aliases in the relationship select - This caused issues with mysql who doesn't understand the syntax: "delete from x as y where y.id = ?" --- templates/06_relationship_to_many.tpl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/templates/06_relationship_to_many.tpl b/templates/06_relationship_to_many.tpl index 10aa842..0e6e634 100644 --- a/templates/06_relationship_to_many.tpl +++ b/templates/06_relationship_to_many.tpl @@ -16,7 +16,7 @@ func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}G(mods ...qm.QueryMod {{- if not (eq $txt.Function.Name $txt.ForeignTable.NamePluralGo)}} via {{.ForeignColumn}} column{{- end}}. func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}(exec boil.Executor, mods ...qm.QueryMod) {{$tableNameSingular}}Query { queryMods := []qm.QueryMod{ - qm.Select("{{id 0 | $dot.Quotes}}.*"), + qm.Select("{{$schemaForeignTable}}.*"), } if len(mods) != 0 { @@ -25,17 +25,18 @@ func (o *{{$txt.LocalTable.NameGo}}) {{$txt.Function.Name}}(exec boil.Executor, {{if .ToJoinTable -}} queryMods = append(queryMods, - qm.InnerJoin("{{.JoinTable | $dot.SchemaTable}} as {{id 1 | $dot.Quotes}} on {{id 0 | $dot.Quotes}}.{{.ForeignColumn | $dot.Quotes}} = {{id 1 | $dot.Quotes}}.{{.JoinForeignColumn | $dot.Quotes}}"), - qm.Where("{{id 1 | $dot.Quotes}}.{{.JoinLocalColumn | $dot.Quotes}}=?", o.{{$txt.LocalTable.ColumnNameGo}}), + {{$schemaJoinTable := .JoinTable | $.SchemaTable -}} + qm.InnerJoin("{{$schemaJoinTable}} on {{$schemaForeignTable}}.{{.ForeignColumn | $dot.Quotes}} = {{$schemaJoinTable}}.{{.JoinForeignColumn | $dot.Quotes}}"), + qm.Where("{{$schemaJoinTable}}.{{.JoinLocalColumn | $dot.Quotes}}=?", o.{{$txt.LocalTable.ColumnNameGo}}), ) {{else -}} queryMods = append(queryMods, - qm.Where("{{id 0 | $dot.Quotes}}.{{.ForeignColumn | $dot.Quotes}}=?", o.{{$txt.LocalTable.ColumnNameGo}}), + qm.Where("{{$schemaForeignTable}}.{{.ForeignColumn | $dot.Quotes}}=?", o.{{$txt.LocalTable.ColumnNameGo}}), ) {{end}} query := {{$txt.ForeignTable.NamePluralGo}}(exec, queryMods...) - queries.SetFrom(query.Query, "{{$schemaForeignTable}} as {{id 0 | $dot.Quotes}}") + queries.SetFrom(query.Query, "{{$schemaForeignTable}}") return query } From ca9f47de8bbab78e3401f818f965825da1425007 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Wed, 14 Jun 2017 21:14:54 -0700 Subject: [PATCH 158/179] Correct nullability for tests in to_one - Use the nullability of the fkey column in question to determine nullability for the entire struct to make things easy, otherwise we'd have to pluck out one at a time. This makes the tests pass instead of fail sporadically. - Fix #160 --- templates_test/relationship_to_one.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates_test/relationship_to_one.tpl b/templates_test/relationship_to_one.tpl index e95c61a..9211284 100644 --- a/templates_test/relationship_to_one.tpl +++ b/templates_test/relationship_to_one.tpl @@ -13,10 +13,10 @@ func test{{$txt.LocalTable.NameGo}}ToOne{{$txt.ForeignTable.NameGo}}Using{{$txt. var foreign {{$txt.ForeignTable.NameGo}} seed := randomize.NewSeed() - if err := randomize.Struct(seed, &local, {{$varNameSingular}}DBTypes, true, {{$varNameSingular}}ColumnsWithDefault...); err != nil { + if err := randomize.Struct(seed, &local, {{$varNameSingular}}DBTypes, {{if .Nullable}}true{{else}}false{{end}}, {{$varNameSingular}}ColumnsWithDefault...); err != nil { t.Errorf("Unable to randomize {{$txt.LocalTable.NameGo}} struct: %s", err) } - if err := randomize.Struct(seed, &foreign, {{$foreignVarNameSingular}}DBTypes, true, {{$foreignVarNameSingular}}ColumnsWithDefault...); err != nil { + if err := randomize.Struct(seed, &foreign, {{$foreignVarNameSingular}}DBTypes, {{if .ForeignColumnNullable}}true{{else}}false{{end}}, {{$foreignVarNameSingular}}ColumnsWithDefault...); err != nil { t.Errorf("Unable to randomize {{$txt.ForeignTable.NameGo}} struct: %s", err) } From ce9d13abf0492b2bb473f3ee18c00ae7df9559b2 Mon Sep 17 00:00:00 2001 From: Aaron L <aaron@bettercoder.net> Date: Sun, 2 Jul 2017 11:16:21 -0700 Subject: [PATCH 159/179] Fix hook documentation to include error return Fix #171 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 194fdd8..51b5fbe 100644 --- a/README.md +++ b/README.md @@ -956,15 +956,16 @@ it with the `AddModelHook` method. Here is an example of a before insert hook: ```go // Define my hook function -func myHook(exec boil.Executor, p *Pilot) { +func myHook(exec boil.Executor, p *Pilot) error { // Do stuff + return nil } // Register my before insert hook for pilots models.AddPilotHook(boil.BeforeInsertHook, myHook) ``` -Your `ModelHook` will always be defined as `func(boil.Executor, *Model)` +Your `ModelHook` will always be defined as `func(boil.Executor, *Model) error` ### Transactions From 8837a986eed7e8ddeae1784e14cdc622cbeb03f3 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Wed, 23 Aug 2017 13:36:13 -0400 Subject: [PATCH 160/179] consistent order to unique key columns --- bdb/drivers/mysql.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index 41510ee..431bad1 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -3,6 +3,7 @@ package drivers import ( "database/sql" "fmt" + "sort" "strconv" "strings" @@ -261,6 +262,7 @@ func (m *MySQLDriver) UniqueKeyInfo(schema, tableName string) ([]bdb.UniqueKey, } ukey.Columns = strings.Split(columns, ",") + sort.Strings(ukey.Columns) ukeys = append(ukeys, ukey) } From 3abac13aeb644da942649b4338d290cfbf2b1227 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Wed, 23 Aug 2017 16:37:33 -0400 Subject: [PATCH 161/179] export InterpolateParams --- templates/07_relationship_to_one_eager.tpl | 2 +- templates/08_relationship_one_to_one_eager.tpl | 2 +- templates/09_relationship_to_many_eager.tpl | 2 +- templates/10_relationship_to_one_setops.tpl | 2 +- templates/11_relationship_one_to_one_setops.tpl | 2 +- templates/12_relationship_to_many_setops.tpl | 8 ++++---- templates/15_insert.tpl | 4 ++-- templates/16_update.tpl | 4 ++-- templates/17_upsert.tpl | 4 ++-- templates/18_delete.tpl | 4 ++-- templates/20_exists.tpl | 2 +- templates/singleton/boil_queries.tpl | 2 +- 12 files changed, 19 insertions(+), 19 deletions(-) diff --git a/templates/07_relationship_to_one_eager.tpl b/templates/07_relationship_to_one_eager.tpl index 3bf42f4..28010fd 100644 --- a/templates/07_relationship_to_one_eager.tpl +++ b/templates/07_relationship_to_one_eager.tpl @@ -40,7 +40,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula ) if boil.DebugMode { - qStr, err := interpolateParams(query, args...) + qStr, err := InterpolateParams(query, args...) if err != nil { return err } diff --git a/templates/08_relationship_one_to_one_eager.tpl b/templates/08_relationship_one_to_one_eager.tpl index 1fbf5f7..261b30c 100644 --- a/templates/08_relationship_one_to_one_eager.tpl +++ b/templates/08_relationship_one_to_one_eager.tpl @@ -40,7 +40,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula ) if boil.DebugMode { - qStr, err := interpolateParams(query, args...) + qStr, err := InterpolateParams(query, args...) if err != nil { return err } diff --git a/templates/09_relationship_to_many_eager.tpl b/templates/09_relationship_to_many_eager.tpl index 3ec5bf9..8c4159b 100644 --- a/templates/09_relationship_to_many_eager.tpl +++ b/templates/09_relationship_to_many_eager.tpl @@ -49,7 +49,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula {{end -}} if boil.DebugMode { - qStr, err := interpolateParams(query, args...) + qStr, err := InterpolateParams(query, args...) if err != nil { return err } diff --git a/templates/10_relationship_to_one_setops.tpl b/templates/10_relationship_to_one_setops.tpl index b293b48..8362420 100644 --- a/templates/10_relationship_to_one_setops.tpl +++ b/templates/10_relationship_to_one_setops.tpl @@ -53,7 +53,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{related.{{$txt.ForeignTable.ColumnNameGo}}, o.{{$dot.Table.PKey.Columns | stringMap $dot.StringFuncs.titleCase | join ", o."}}{{"}"}} if boil.DebugMode { - qStr, err := interpolateParams(updateQuery, values...) + qStr, err := InterpolateParams(updateQuery, values...) if err != nil { return err } diff --git a/templates/11_relationship_one_to_one_setops.tpl b/templates/11_relationship_one_to_one_setops.tpl index d4583ca..7648e23 100644 --- a/templates/11_relationship_one_to_one_setops.tpl +++ b/templates/11_relationship_one_to_one_setops.tpl @@ -59,7 +59,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{o.{{$txt.LocalTable.ColumnNameGo}}, related.{{$foreignPKeyCols | stringMap $dot.StringFuncs.titleCase | join ", related."}}{{"}"}} if boil.DebugMode { - qStr, err := interpolateParams(updateQuery, values...) + qStr, err := InterpolateParams(updateQuery, values...) if err != nil { return err } diff --git a/templates/12_relationship_to_many_setops.tpl b/templates/12_relationship_to_many_setops.tpl index 35a4bd4..6794889 100644 --- a/templates/12_relationship_to_many_setops.tpl +++ b/templates/12_relationship_to_many_setops.tpl @@ -66,7 +66,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{o.{{$txt.LocalTable.ColumnNameGo}}, rel.{{$foreignPKeyCols | stringMap $dot.StringFuncs.titleCase | join ", rel."}}{{"}"}} if boil.DebugMode { - qStr, err := interpolateParams(updateQuery, values...) + qStr, err := InterpolateParams(updateQuery, values...) if err != nil { return err } @@ -90,7 +90,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}, rel.{{$txt.ForeignTable.ColumnNameGo}}} if boil.DebugMode { - qStr, err := interpolateParams(query, values...) + qStr, err := InterpolateParams(query, values...) if err != nil { return err } @@ -190,7 +190,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}} {{end -}} if boil.DebugMode { - qStr, err := interpolateParams(query, values...) + qStr, err := InterpolateParams(query, values...) if err != nil { return err } @@ -269,7 +269,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Exec } if boil.DebugMode { - qStr, err := interpolateParams(query, values...) + qStr, err := InterpolateParams(query, values...) if err != nil { return err } diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 8cd30b9..00b6f5b 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -98,7 +98,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string vals := queries.ValuesFromMapping(value, cache.valueMapping) if boil.DebugMode { - qStr, err := interpolateParams(cache.query, vals...) + qStr, err := InterpolateParams(cache.query, vals...) if err != nil { return err } @@ -147,7 +147,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string } if boil.DebugMode { - qStr, err := interpolateParams(cache.retQuery, identifierCols...) + qStr, err := InterpolateParams(cache.retQuery, identifierCols...) if err != nil { return err } diff --git a/templates/16_update.tpl b/templates/16_update.tpl index 1ac460b..1f67234 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -78,7 +78,7 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping) if boil.DebugMode { - qStr, err := interpolateParams(cache.query, values...) + qStr, err := InterpolateParams(cache.query, values...) if err != nil { return err } @@ -173,7 +173,7 @@ func (o {{$tableNameSingular}}Slice) UpdateAll(exec boil.Executor, cols M) error strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}len(colNames)+1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(o))) if boil.DebugMode { - qStr, err := interpolateParams(sql, args...) + qStr, err := InterpolateParams(sql, args...) if err != nil { return err } diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index 5cf6a14..6eef9fe 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -147,7 +147,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName } if boil.DebugMode { - qStr, err := interpolateParams(cache.query, vals...) + qStr, err := InterpolateParams(cache.query, vals...) if err != nil { return err } @@ -196,7 +196,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName } if boil.DebugMode { - qStr, err := interpolateParams(cache.retQuery, identifierCols...) + qStr, err := InterpolateParams(cache.retQuery, identifierCols...) if err != nil { return err } diff --git a/templates/18_delete.tpl b/templates/18_delete.tpl index 20e819a..8c63e43 100644 --- a/templates/18_delete.tpl +++ b/templates/18_delete.tpl @@ -46,7 +46,7 @@ func (o *{{$tableNameSingular}}) Delete(exec boil.Executor) error { sql := "DELETE FROM {{$schemaTable}} WHERE {{if .Dialect.IndexPlaceholders}}{{whereClause .LQ .RQ 1 .Table.PKey.Columns}}{{else}}{{whereClause .LQ .RQ 0 .Table.PKey.Columns}}{{end}}" if boil.DebugMode { - qStr, err := interpolateParams(sql, args...) + qStr, err := InterpolateParams(sql, args...) if err != nil { return err } @@ -142,7 +142,7 @@ func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error { strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(o)) if boil.DebugMode { - qStr, err := interpolateParams(sql, args...) + qStr, err := InterpolateParams(sql, args...) if err != nil { return err } diff --git a/templates/20_exists.tpl b/templates/20_exists.tpl index 4c4031a..7d3dc3d 100644 --- a/templates/20_exists.tpl +++ b/templates/20_exists.tpl @@ -14,7 +14,7 @@ func {{$tableNameSingular}}Exists(exec boil.Executor, {{$pkArgs}}) (bool, error) {{- end}} if boil.DebugMode { - qStr, err := interpolateParams(sql, {{$pkNames | join ", "}}) + qStr, err := InterpolateParams(sql, {{$pkNames | join ", "}}) if err != nil { return false, err } diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index 11f103b..0d3c812 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -138,7 +138,7 @@ func checkMerge(tx *sql.Tx, foreignKeys []foreignKey) error { } // duplicated in queries/query.go -func interpolateParams(query string, args ...interface{}) (string, error) { +func InterpolateParams(query string, args ...interface{}) (string, error) { for i := 0; i < len(args); i++ { field := reflect.ValueOf(args[i]) From faec3464818e1429f49922dec917d7313235a40b Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Wed, 23 Aug 2017 16:59:29 -0400 Subject: [PATCH 162/179] use interface to enable custom Tx types --- boil/db.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boil/db.go b/boil/db.go index 84bc7d3..2d31d38 100644 --- a/boil/db.go +++ b/boil/db.go @@ -19,7 +19,7 @@ type Transactor interface { // Beginner begins transactions. type Beginner interface { - Begin() (*sql.Tx, error) + Begin() (Transactor, error) } // Begin a transaction From 544ff7afdd48696d7659886fc9c3b0e45588fd5f Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Wed, 23 Aug 2017 17:03:24 -0400 Subject: [PATCH 163/179] typo --- bdb/drivers/mysql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index 431bad1..dec80e0 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -380,7 +380,7 @@ func (m *MySQLDriver) TranslateColumnType(c bdb.Column) bdb.Column { if unsigned { c.Type = "null.Uint64" } else { - c.Type = "null.Uint64" + c.Type = "null.Int64" } case "float": c.Type = "null.Float32" From 55f42bc03809b788249594b190801485a236431b Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Thu, 24 Aug 2017 11:58:13 -0400 Subject: [PATCH 164/179] just use interfaces --- boilingcore/imports.go | 1 - templates/23_merge.tpl | 7 +------ templates/singleton/boil_queries.tpl | 6 +++--- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/boilingcore/imports.go b/boilingcore/imports.go index 2c31c63..528a946 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -182,7 +182,6 @@ func newImporter() importer { imp.Singleton = mapImports{ "boil_queries": imports{ standard: importList{ - `"database/sql"`, `"fmt"`, `"reflect"`, `"strconv"`, diff --git a/templates/23_merge.tpl b/templates/23_merge.tpl index d84ed28..9f1af10 100644 --- a/templates/23_merge.tpl +++ b/templates/23_merge.tpl @@ -69,12 +69,7 @@ func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID {{- end }} } - sqlTx, ok := tx.(*sql.Tx) - if !ok { - return errors.New("tx must be an sql.Tx") - } - - err = mergeModels(sqlTx, primaryID, secondaryID, foreignKeys, conflictingKeys) + err = mergeModels(tx, primaryID, secondaryID, foreignKeys, conflictingKeys) if err != nil { return err } diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index 0d3c812..89b0e74 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -20,7 +20,7 @@ func NewQuery(exec boil.Executor, mods ...qm.QueryMod) *queries.Query { return q } -func mergeModels(tx *sql.Tx, primaryID uint64, secondaryID uint64, foreignKeys []foreignKey, conflictingKeys []conflictingUniqueKey) error { +func mergeModels(tx boil.Executor, primaryID uint64, secondaryID uint64, foreignKeys []foreignKey, conflictingKeys []conflictingUniqueKey) error { if len(foreignKeys) < 1 { return nil } @@ -48,7 +48,7 @@ func mergeModels(tx *sql.Tx, primaryID uint64, secondaryID uint64, foreignKeys [ return checkMerge(tx, foreignKeys) } -func deleteConflictsBeforeMerge(tx *sql.Tx, conflict conflictingUniqueKey, primaryID uint64, secondaryID uint64) error { +func deleteConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, primaryID uint64, secondaryID uint64) error { conflictingColumns := strmangle.SetComplement(conflict.columns, []string{conflict.objectIdColumn}) if len(conflictingColumns) < 1 { @@ -98,7 +98,7 @@ func deleteConflictsBeforeMerge(tx *sql.Tx, conflict conflictingUniqueKey, prima return nil } -func checkMerge(tx *sql.Tx, foreignKeys []foreignKey) error { +func checkMerge(tx boil.Executor, foreignKeys []foreignKey) error { uniqueColumns := []interface{}{} uniqueColumnNames := map[string]bool{} handledTablesColumns := map[string]bool{} From cd445bf2f42346ac3dd5a1398e1f3ccadbe902e8 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Sat, 2 Sep 2017 11:27:27 -0400 Subject: [PATCH 165/179] revert interpolateParams, since we're doing our own logging now --- boilingcore/imports.go | 5 - queries/query.go | 94 ++----------------- templates/07_relationship_to_one_eager.tpl | 6 +- .../08_relationship_one_to_one_eager.tpl | 6 +- templates/09_relationship_to_many_eager.tpl | 6 +- templates/10_relationship_to_one_setops.tpl | 7 +- .../11_relationship_one_to_one_setops.tpl | 7 +- templates/12_relationship_to_many_setops.tpl | 28 ++---- templates/15_insert.tpl | 14 +-- templates/16_update.tpl | 14 +-- templates/17_upsert.tpl | 14 +-- templates/18_delete.tpl | 16 +--- templates/20_exists.tpl | 7 +- templates/singleton/boil_queries.tpl | 66 ------------- 14 files changed, 40 insertions(+), 250 deletions(-) diff --git a/boilingcore/imports.go b/boilingcore/imports.go index 528a946..13ed119 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -183,10 +183,6 @@ func newImporter() importer { "boil_queries": imports{ standard: importList{ `"fmt"`, - `"reflect"`, - `"strconv"`, - `"strings"`, - `"time"`, }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, @@ -194,7 +190,6 @@ func newImporter() importer { `"github.com/lbryio/sqlboiler/queries/qm"`, `"github.com/lbryio/sqlboiler/strmangle"`, `"github.com/pkg/errors"`, - `"github.com/lbryio/null.go"`, }, }, "boil_types": { diff --git a/queries/query.go b/queries/query.go index b3ff830..be539e5 100644 --- a/queries/query.go +++ b/queries/query.go @@ -3,15 +3,8 @@ package queries import ( "database/sql" "fmt" - "reflect" - "strconv" - "strings" - "time" - "github.com/lbryio/null.go" "github.com/lbryio/sqlboiler/boil" - - "github.com/go-errors/errors" ) // joinKind is the type of join @@ -112,11 +105,8 @@ func RawG(query string, args ...interface{}) *Query { func (q *Query) Exec() (sql.Result, error) { qs, args := buildQuery(q) if boil.DebugMode { - qStr, err := interpolateParams(qs, args...) - if err != nil { - return nil, err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, qs) + fmt.Fprintln(boil.DebugWriter, args) } return q.executor.Exec(qs, args...) } @@ -125,11 +115,8 @@ func (q *Query) Exec() (sql.Result, error) { func (q *Query) QueryRow() *sql.Row { qs, args := buildQuery(q) if boil.DebugMode { - qStr, err := interpolateParams(qs, args...) - if err != nil { - panic(err) - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, qs) + fmt.Fprintln(boil.DebugWriter, args) } return q.executor.QueryRow(qs, args...) } @@ -138,11 +125,8 @@ func (q *Query) QueryRow() *sql.Row { func (q *Query) Query() (*sql.Rows, error) { qs, args := buildQuery(q) if boil.DebugMode { - qStr, err := interpolateParams(qs, args...) - if err != nil { - return nil, err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, qs) + fmt.Fprintln(boil.DebugWriter, args) } return q.executor.Query(qs, args...) } @@ -296,69 +280,3 @@ func AppendGroupBy(q *Query, clause string) { func AppendOrderBy(q *Query, clause string) { q.orderBy = append(q.orderBy, clause) } - -// duplicated in boil_queries.tpl -func interpolateParams(query string, args ...interface{}) (string, error) { - for i := 0; i < len(args); i++ { - field := reflect.ValueOf(args[i]) - - if value, ok := field.Interface().(time.Time); ok { - query = strings.Replace(query, "?", `"`+value.Format("2006-01-02 15:04:05")+`"`, 1) - } else if nullable, ok := field.Interface().(null.Nullable); ok { - if nullable.IsNull() { - query = strings.Replace(query, "?", "NULL", 1) - } else { - switch field.Type() { - case reflect.TypeOf(null.Time{}): - query = strings.Replace(query, "?", `"`+field.Interface().(null.Time).Time.Format("2006-01-02 15:04:05")+`"`, 1) - case reflect.TypeOf(null.Int{}): - query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int).Int), 10), 1) - case reflect.TypeOf(null.Int8{}): - query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int8).Int8), 10), 1) - case reflect.TypeOf(null.Int16{}): - query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int16).Int16), 10), 1) - case reflect.TypeOf(null.Int32{}): - query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int32).Int32), 10), 1) - case reflect.TypeOf(null.Int64{}): - query = strings.Replace(query, "?", strconv.FormatInt(field.Interface().(null.Int64).Int64, 10), 1) - case reflect.TypeOf(null.Uint{}): - query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint).Uint), 10), 1) - case reflect.TypeOf(null.Uint8{}): - query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint8).Uint8), 10), 1) - case reflect.TypeOf(null.Uint16{}): - query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint16).Uint16), 10), 1) - case reflect.TypeOf(null.Uint32{}): - query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint32).Uint32), 10), 1) - case reflect.TypeOf(null.Uint64{}): - query = strings.Replace(query, "?", strconv.FormatUint(field.Interface().(null.Uint64).Uint64, 10), 1) - case reflect.TypeOf(null.String{}): - query = strings.Replace(query, "?", `"`+field.Interface().(null.String).String+`"`, 1) - case reflect.TypeOf(null.Bool{}): - if field.Interface().(null.Bool).Bool { - query = strings.Replace(query, "?", "1", 1) - } else { - query = strings.Replace(query, "?", "0", 1) - } - } - } - } else { - switch field.Kind() { - case reflect.Bool: - boolString := "0" - if field.Bool() { - boolString = "1" - } - query = strings.Replace(query, "?", boolString, 1) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - query = strings.Replace(query, "?", strconv.FormatInt(field.Int(), 10), 1) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - query = strings.Replace(query, "?", strconv.FormatUint(field.Uint(), 10), 1) - case reflect.String: - query = strings.Replace(query, "?", `"`+field.String()+`"`, 1) - default: - return "", errors.New("Dont know how to interpolate type " + field.Type().String()) - } - } - } - return query, nil -} diff --git a/templates/07_relationship_to_one_eager.tpl b/templates/07_relationship_to_one_eager.tpl index 28010fd..3b24f1a 100644 --- a/templates/07_relationship_to_one_eager.tpl +++ b/templates/07_relationship_to_one_eager.tpl @@ -40,11 +40,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula ) if boil.DebugMode { - qStr, err := InterpolateParams(query, args...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintf(boil.DebugWriter, "%s\n%v\n", query, args) } results, err := e.Query(query, args...) diff --git a/templates/08_relationship_one_to_one_eager.tpl b/templates/08_relationship_one_to_one_eager.tpl index 261b30c..37d1af0 100644 --- a/templates/08_relationship_one_to_one_eager.tpl +++ b/templates/08_relationship_one_to_one_eager.tpl @@ -40,11 +40,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula ) if boil.DebugMode { - qStr, err := InterpolateParams(query, args...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintf(boil.DebugWriter, "%s\n%v\n", query, args) } results, err := e.Query(query, args...) diff --git a/templates/09_relationship_to_many_eager.tpl b/templates/09_relationship_to_many_eager.tpl index 8c4159b..1603188 100644 --- a/templates/09_relationship_to_many_eager.tpl +++ b/templates/09_relationship_to_many_eager.tpl @@ -49,11 +49,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula {{end -}} if boil.DebugMode { - qStr, err := InterpolateParams(query, args...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintf(boil.DebugWriter, "%s\n%v\n", query, args) } results, err := e.Query(query, args...) diff --git a/templates/10_relationship_to_one_setops.tpl b/templates/10_relationship_to_one_setops.tpl index 8362420..14f726e 100644 --- a/templates/10_relationship_to_one_setops.tpl +++ b/templates/10_relationship_to_one_setops.tpl @@ -53,11 +53,8 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{related.{{$txt.ForeignTable.ColumnNameGo}}, o.{{$dot.Table.PKey.Columns | stringMap $dot.StringFuncs.titleCase | join ", o."}}{{"}"}} if boil.DebugMode { - qStr, err := InterpolateParams(updateQuery, values...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, updateQuery) + fmt.Fprintln(boil.DebugWriter, values) } if _, err = exec.Exec(updateQuery, values...); err != nil { diff --git a/templates/11_relationship_one_to_one_setops.tpl b/templates/11_relationship_one_to_one_setops.tpl index 7648e23..826eec2 100644 --- a/templates/11_relationship_one_to_one_setops.tpl +++ b/templates/11_relationship_one_to_one_setops.tpl @@ -59,11 +59,8 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{o.{{$txt.LocalTable.ColumnNameGo}}, related.{{$foreignPKeyCols | stringMap $dot.StringFuncs.titleCase | join ", related."}}{{"}"}} if boil.DebugMode { - qStr, err := InterpolateParams(updateQuery, values...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, updateQuery) + fmt.Fprintln(boil.DebugWriter, values) } if _, err = exec.Exec(updateQuery, values...); err != nil { diff --git a/templates/12_relationship_to_many_setops.tpl b/templates/12_relationship_to_many_setops.tpl index 6794889..06159ee 100644 --- a/templates/12_relationship_to_many_setops.tpl +++ b/templates/12_relationship_to_many_setops.tpl @@ -66,11 +66,8 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{o.{{$txt.LocalTable.ColumnNameGo}}, rel.{{$foreignPKeyCols | stringMap $dot.StringFuncs.titleCase | join ", rel."}}{{"}"}} if boil.DebugMode { - qStr, err := InterpolateParams(updateQuery, values...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, updateQuery) + fmt.Fprintln(boil.DebugWriter, values) } if _, err = exec.Exec(updateQuery, values...); err != nil { @@ -90,11 +87,8 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}, rel.{{$txt.ForeignTable.ColumnNameGo}}} if boil.DebugMode { - qStr, err := InterpolateParams(query, values...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, query) + fmt.Fprintln(boil.DebugWriter, values) } _, err = exec.Exec(query, values...) @@ -190,11 +184,8 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}} {{end -}} if boil.DebugMode { - qStr, err := InterpolateParams(query, values...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, query) + fmt.Fprintln(boil.DebugWriter, values) } _, err := exec.Exec(query, values...) @@ -269,11 +260,8 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Exec } if boil.DebugMode { - qStr, err := InterpolateParams(query, values...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, query) + fmt.Fprintln(boil.DebugWriter, values) } _, err = exec.Exec(query, values...) diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 00b6f5b..870e20d 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -98,11 +98,8 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string vals := queries.ValuesFromMapping(value, cache.valueMapping) if boil.DebugMode { - qStr, err := InterpolateParams(cache.query, vals...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, cache.query) + fmt.Fprintln(boil.DebugWriter, vals) } {{if .UseLastInsertID -}} @@ -147,11 +144,8 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string } if boil.DebugMode { - qStr, err := InterpolateParams(cache.retQuery, identifierCols...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, cache.retQuery) + fmt.Fprintln(boil.DebugWriter, identifierCols...) } err = exec.QueryRow(cache.retQuery, identifierCols...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...) diff --git a/templates/16_update.tpl b/templates/16_update.tpl index 1f67234..bc4a6f5 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -78,11 +78,8 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping) if boil.DebugMode { - qStr, err := InterpolateParams(cache.query, values...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, cache.query) + fmt.Fprintln(boil.DebugWriter, values) } _, err = exec.Exec(cache.query, values...) @@ -173,11 +170,8 @@ func (o {{$tableNameSingular}}Slice) UpdateAll(exec boil.Executor, cols M) error strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}len(colNames)+1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(o))) if boil.DebugMode { - qStr, err := InterpolateParams(sql, args...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, sql) + fmt.Fprintln(boil.DebugWriter, args...) } _, err := exec.Exec(sql, args...) diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index 6eef9fe..dbce58c 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -147,11 +147,8 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName } if boil.DebugMode { - qStr, err := InterpolateParams(cache.query, vals...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, cache.query) + fmt.Fprintln(boil.DebugWriter, vals) } {{if .UseLastInsertID -}} @@ -196,11 +193,8 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName } if boil.DebugMode { - qStr, err := InterpolateParams(cache.retQuery, identifierCols...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, cache.retQuery) + fmt.Fprintln(boil.DebugWriter, identifierCols...) } err = exec.QueryRow(cache.retQuery, identifierCols...).Scan(returns...) diff --git a/templates/18_delete.tpl b/templates/18_delete.tpl index 8c63e43..aea163e 100644 --- a/templates/18_delete.tpl +++ b/templates/18_delete.tpl @@ -6,7 +6,7 @@ // Panics on error. func (o *{{$tableNameSingular}}) DeleteP(exec boil.Executor) { if err := o.Delete(exec); err != nil { - panic(boil.WrapErr(err)) + panic(boil.WrapErr(err)) } } @@ -46,11 +46,8 @@ func (o *{{$tableNameSingular}}) Delete(exec boil.Executor) error { sql := "DELETE FROM {{$schemaTable}} WHERE {{if .Dialect.IndexPlaceholders}}{{whereClause .LQ .RQ 1 .Table.PKey.Columns}}{{else}}{{whereClause .LQ .RQ 0 .Table.PKey.Columns}}{{end}}" if boil.DebugMode { - qStr, err := InterpolateParams(sql, args...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, sql) + fmt.Fprintln(boil.DebugWriter, args...) } _, err := exec.Exec(sql, args...) @@ -142,11 +139,8 @@ func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error { strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), {{if .Dialect.IndexPlaceholders}}1{{else}}0{{end}}, {{$varNameSingular}}PrimaryKeyColumns, len(o)) if boil.DebugMode { - qStr, err := InterpolateParams(sql, args...) - if err != nil { - return err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, sql) + fmt.Fprintln(boil.DebugWriter, args) } _, err := exec.Exec(sql, args...) diff --git a/templates/20_exists.tpl b/templates/20_exists.tpl index 7d3dc3d..2e20fa6 100644 --- a/templates/20_exists.tpl +++ b/templates/20_exists.tpl @@ -14,11 +14,8 @@ func {{$tableNameSingular}}Exists(exec boil.Executor, {{$pkArgs}}) (bool, error) {{- end}} if boil.DebugMode { - qStr, err := InterpolateParams(sql, {{$pkNames | join ", "}}) - if err != nil { - return false, err - } - fmt.Fprintln(boil.DebugWriter, qStr) + fmt.Fprintln(boil.DebugWriter, sql) + fmt.Fprintln(boil.DebugWriter, {{$pkNames | join ", "}}) } row := exec.QueryRow(sql, {{$pkNames | join ", "}}) diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index 89b0e74..1260dde 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -136,69 +136,3 @@ func checkMerge(tx boil.Executor, foreignKeys []foreignKey) error { return nil } - -// duplicated in queries/query.go -func InterpolateParams(query string, args ...interface{}) (string, error) { - for i := 0; i < len(args); i++ { - field := reflect.ValueOf(args[i]) - - if value, ok := field.Interface().(time.Time); ok { - query = strings.Replace(query, "?", `"`+value.Format("2006-01-02 15:04:05")+`"`, 1) - } else if nullable, ok := field.Interface().(null.Nullable); ok { - if nullable.IsNull() { - query = strings.Replace(query, "?", "NULL", 1) - } else { - switch field.Type() { - case reflect.TypeOf(null.Time{}): - query = strings.Replace(query, "?", `"`+field.Interface().(null.Time).Time.Format("2006-01-02 15:04:05")+`"`, 1) - case reflect.TypeOf(null.Int{}): - query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int).Int), 10), 1) - case reflect.TypeOf(null.Int8{}): - query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int8).Int8), 10), 1) - case reflect.TypeOf(null.Int16{}): - query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int16).Int16), 10), 1) - case reflect.TypeOf(null.Int32{}): - query = strings.Replace(query, "?", strconv.FormatInt(int64(field.Interface().(null.Int32).Int32), 10), 1) - case reflect.TypeOf(null.Int64{}): - query = strings.Replace(query, "?", strconv.FormatInt(field.Interface().(null.Int64).Int64, 10), 1) - case reflect.TypeOf(null.Uint{}): - query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint).Uint), 10), 1) - case reflect.TypeOf(null.Uint8{}): - query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint8).Uint8), 10), 1) - case reflect.TypeOf(null.Uint16{}): - query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint16).Uint16), 10), 1) - case reflect.TypeOf(null.Uint32{}): - query = strings.Replace(query, "?", strconv.FormatUint(uint64(field.Interface().(null.Uint32).Uint32), 10), 1) - case reflect.TypeOf(null.Uint64{}): - query = strings.Replace(query, "?", strconv.FormatUint(field.Interface().(null.Uint64).Uint64, 10), 1) - case reflect.TypeOf(null.String{}): - query = strings.Replace(query, "?", `"`+field.Interface().(null.String).String+`"`, 1) - case reflect.TypeOf(null.Bool{}): - if field.Interface().(null.Bool).Bool { - query = strings.Replace(query, "?", "1", 1) - } else { - query = strings.Replace(query, "?", "0", 1) - } - } - } - } else { - switch field.Kind() { - case reflect.Bool: - boolString := "0" - if field.Bool() { - boolString = "1" - } - query = strings.Replace(query, "?", boolString, 1) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - query = strings.Replace(query, "?", strconv.FormatInt(field.Int(), 10), 1) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - query = strings.Replace(query, "?", strconv.FormatUint(field.Uint(), 10), 1) - case reflect.String: - query = strings.Replace(query, "?", `"`+field.String()+`"`, 1) - default: - return "", errors.New("Dont know how to interpolate type " + field.Type().String()) - } - } - } - return query, nil -} From 0b0a1f21c2c3512b0f08441302e5b2e1fce7fc9a Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Sat, 2 Sep 2017 11:52:31 -0400 Subject: [PATCH 166/179] allow generic interface or real sql tx --- boil/db.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/boil/db.go b/boil/db.go index 2d31d38..f3153bc 100644 --- a/boil/db.go +++ b/boil/db.go @@ -22,11 +22,19 @@ type Beginner interface { Begin() (Transactor, error) } +type SQLBeginner interface { + Begin() (*sql.Tx, error) +} + // Begin a transaction func Begin() (Transactor, error) { creator, ok := currentDB.(Beginner) if !ok { - panic("database does not support transactions") + creator2, ok2 := currentDB.(SQLBeginner) + if !ok2 { + panic("database does not support transactions") + } + return creator2.Begin() } return creator.Begin() From e4a52e21b6f0c655dc21b5ba4391bbdf1f3d1abd Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Wed, 7 Feb 2018 09:35:46 -0500 Subject: [PATCH 167/179] switch to our errors package --- boil/errors.go | 23 ------------- boil/errors_test.go | 24 ------------- boilingcore/imports.go | 26 +++++++------- queries/query.go | 5 +-- queries/reflect.go | 6 ++-- templates/02_hooks.tpl | 18 +++++----- templates/03_finishers.tpl | 18 +++++----- templates/07_relationship_to_one_eager.tpl | 6 ++-- .../08_relationship_one_to_one_eager.tpl | 6 ++-- templates/09_relationship_to_many_eager.tpl | 10 +++--- templates/10_relationship_to_one_setops.tpl | 14 ++++---- .../11_relationship_one_to_one_setops.tpl | 14 ++++---- templates/12_relationship_to_many_setops.tpl | 24 ++++++------- templates/14_find.tpl | 12 +++---- templates/15_insert.tpl | 20 +++++------ templates/16_update.tpl | 24 ++++++------- templates/17_upsert.tpl | 24 ++++++------- templates/18_delete.tpl | 34 +++++++++---------- templates/19_reload.tpl | 16 ++++----- templates/20_exists.tpl | 6 ++-- templates/23_merge.tpl | 16 ++++----- templates/singleton/boil_queries.tpl | 18 +++++----- templates/singleton/boil_types.tpl | 2 +- templates_test/main_test/mssql_main.tpl | 14 ++++---- templates_test/main_test/mysql_main.tpl | 22 ++++++------ templates_test/main_test/postgres_main.tpl | 22 ++++++------ templates_test/singleton/boil_main_test.tpl | 2 +- 27 files changed, 190 insertions(+), 236 deletions(-) delete mode 100644 boil/errors.go delete mode 100644 boil/errors_test.go diff --git a/boil/errors.go b/boil/errors.go deleted file mode 100644 index 4d02169..0000000 --- a/boil/errors.go +++ /dev/null @@ -1,23 +0,0 @@ -package boil - -type boilErr struct { - error -} - -// WrapErr wraps err in a boilErr -func WrapErr(err error) error { - return boilErr{ - error: err, - } -} - -// Error returns the underlying error string -func (e boilErr) Error() string { - return e.error.Error() -} - -// IsBoilErr checks if err is a boilErr -func IsBoilErr(err error) bool { - _, ok := err.(boilErr) - return ok -} diff --git a/boil/errors_test.go b/boil/errors_test.go deleted file mode 100644 index 2b2f1a0..0000000 --- a/boil/errors_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package boil - -import ( - "errors" - "testing" -) - -func TestErrors(t *testing.T) { - t.Parallel() - - err := errors.New("test error") - if IsBoilErr(err) == true { - t.Errorf("Expected false") - } - - err = WrapErr(errors.New("test error")) - if err.Error() != "test error" { - t.Errorf(`Expected "test error", got %v`, err.Error()) - } - - if IsBoilErr(err) != true { - t.Errorf("Expected true") - } -} diff --git a/boilingcore/imports.go b/boilingcore/imports.go index 13ed119..b73b429 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -170,12 +170,12 @@ func newImporter() importer { `"time"`, }, thirdParty: importList{ + `"github.com/lbryio/errors.go"`, `"github.com/lbryio/null.go"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, `"github.com/lbryio/sqlboiler/strmangle"`, - `"github.com/pkg/errors"`, }, } @@ -185,16 +185,16 @@ func newImporter() importer { `"fmt"`, }, thirdParty: importList{ + `"github.com/lbryio/errors.go"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, `"github.com/lbryio/sqlboiler/strmangle"`, - `"github.com/pkg/errors"`, }, }, "boil_types": { thirdParty: importList{ - `"github.com/pkg/errors"`, + `"github.com/lbryio/errors.go"`, `"github.com/lbryio/sqlboiler/strmangle"`, }, }, @@ -227,9 +227,9 @@ func newImporter() importer { }, thirdParty: importList{ `"github.com/kat-co/vala"`, - `"github.com/pkg/errors"`, - `"github.com/spf13/viper"`, + `"github.com/lbryio/errors.go"`, `"github.com/lbryio/sqlboiler/boil"`, + `"github.com/spf13/viper"`, }, }, "boil_queries_test": { @@ -265,11 +265,11 @@ func newImporter() importer { `"strings"`, }, thirdParty: importList{ - `"github.com/pkg/errors"`, - `"github.com/spf13/viper"`, + `"github.com/lbryio/errors.go"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `_ "github.com/lib/pq"`, + `"github.com/spf13/viper"`, }, }, "mysql": { @@ -284,11 +284,11 @@ func newImporter() importer { `"strings"`, }, thirdParty: importList{ - `"github.com/pkg/errors"`, - `"github.com/spf13/viper"`, + `_ "github.com/go-sql-driver/mysql"`, + `"github.com/lbryio/errors.go"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, - `_ "github.com/go-sql-driver/mysql"`, + `"github.com/spf13/viper"`, }, }, "mssql": { @@ -301,11 +301,11 @@ func newImporter() importer { `"strings"`, }, thirdParty: importList{ - `"github.com/pkg/errors"`, - `"github.com/spf13/viper"`, + `_ "github.com/denisenkom/go-mssqldb"`, + `"github.com/lbryio/errors.go"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, - `_ "github.com/denisenkom/go-mssqldb"`, + `"github.com/spf13/viper"`, }, }, } diff --git a/queries/query.go b/queries/query.go index be539e5..a76c046 100644 --- a/queries/query.go +++ b/queries/query.go @@ -4,6 +4,7 @@ import ( "database/sql" "fmt" + "github.com/lbryio/errors.go" "github.com/lbryio/sqlboiler/boil" ) @@ -136,7 +137,7 @@ func (q *Query) Query() (*sql.Rows, error) { func (q *Query) ExecP() sql.Result { res, err := q.Exec() if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return res @@ -147,7 +148,7 @@ func (q *Query) ExecP() sql.Result { func (q *Query) QueryP() *sql.Rows { rows, err := q.Query() if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return rows diff --git a/queries/reflect.go b/queries/reflect.go index 8cf8f3d..39eda23 100644 --- a/queries/reflect.go +++ b/queries/reflect.go @@ -7,9 +7,9 @@ import ( "strings" "sync" - "github.com/pkg/errors" - "github.com/lbryio/sqlboiler/boil" "github.com/lbryio/sqlboiler/strmangle" + + "github.com/pkg/errors" ) var ( @@ -41,7 +41,7 @@ const ( // It panics on error. See boil.Bind() documentation. func (q *Query) BindP(obj interface{}) { if err := q.Bind(obj); err != nil { - panic(boil.WrapErr(err)) + panic(errors.WithStack(err)) } } diff --git a/templates/02_hooks.tpl b/templates/02_hooks.tpl index 9815639..d152978 100644 --- a/templates/02_hooks.tpl +++ b/templates/02_hooks.tpl @@ -16,7 +16,7 @@ var {{$varNameSingular}}AfterUpsertHooks []{{$tableNameSingular}}Hook func (o *{{$tableNameSingular}}) doBeforeInsertHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}BeforeInsertHooks { if err := hook(exec, o); err != nil { - return err + return errors.Err(err) } } @@ -27,7 +27,7 @@ func (o *{{$tableNameSingular}}) doBeforeInsertHooks(exec boil.Executor) (err er func (o *{{$tableNameSingular}}) doBeforeUpdateHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}BeforeUpdateHooks { if err := hook(exec, o); err != nil { - return err + return errors.Err(err) } } @@ -38,7 +38,7 @@ func (o *{{$tableNameSingular}}) doBeforeUpdateHooks(exec boil.Executor) (err er func (o *{{$tableNameSingular}}) doBeforeDeleteHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}BeforeDeleteHooks { if err := hook(exec, o); err != nil { - return err + return errors.Err(err) } } @@ -49,7 +49,7 @@ func (o *{{$tableNameSingular}}) doBeforeDeleteHooks(exec boil.Executor) (err er func (o *{{$tableNameSingular}}) doBeforeUpsertHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}BeforeUpsertHooks { if err := hook(exec, o); err != nil { - return err + return errors.Err(err) } } @@ -60,7 +60,7 @@ func (o *{{$tableNameSingular}}) doBeforeUpsertHooks(exec boil.Executor) (err er func (o *{{$tableNameSingular}}) doAfterInsertHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}AfterInsertHooks { if err := hook(exec, o); err != nil { - return err + return errors.Err(err) } } @@ -71,7 +71,7 @@ func (o *{{$tableNameSingular}}) doAfterInsertHooks(exec boil.Executor) (err err func (o *{{$tableNameSingular}}) doAfterSelectHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}AfterSelectHooks { if err := hook(exec, o); err != nil { - return err + return errors.Err(err) } } @@ -82,7 +82,7 @@ func (o *{{$tableNameSingular}}) doAfterSelectHooks(exec boil.Executor) (err err func (o *{{$tableNameSingular}}) doAfterUpdateHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}AfterUpdateHooks { if err := hook(exec, o); err != nil { - return err + return errors.Err(err) } } @@ -93,7 +93,7 @@ func (o *{{$tableNameSingular}}) doAfterUpdateHooks(exec boil.Executor) (err err func (o *{{$tableNameSingular}}) doAfterDeleteHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}AfterDeleteHooks { if err := hook(exec, o); err != nil { - return err + return errors.Err(err) } } @@ -104,7 +104,7 @@ func (o *{{$tableNameSingular}}) doAfterDeleteHooks(exec boil.Executor) (err err func (o *{{$tableNameSingular}}) doAfterUpsertHooks(exec boil.Executor) (err error) { for _, hook := range {{$varNameSingular}}AfterUpsertHooks { if err := hook(exec, o); err != nil { - return err + return errors.Err(err) } } diff --git a/templates/03_finishers.tpl b/templates/03_finishers.tpl index df2081d..473bddd 100644 --- a/templates/03_finishers.tpl +++ b/templates/03_finishers.tpl @@ -4,7 +4,7 @@ func (q {{$tableNameSingular}}Query) OneP() (*{{$tableNameSingular}}) { o, err := q.One() if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return o @@ -18,10 +18,10 @@ func (q {{$tableNameSingular}}Query) One() (*{{$tableNameSingular}}, error) { err := q.Bind(o) if err != nil { - if errors.Cause(err) == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } - return nil, errors.Wrap(err, "{{.PkgName}}: failed to execute a one query for {{.Table.Name}}") + return nil, errors.Prefix("{{.PkgName}}: failed to execute a one query for {{.Table.Name}}", err) } {{if not .NoHooks -}} @@ -37,7 +37,7 @@ func (q {{$tableNameSingular}}Query) One() (*{{$tableNameSingular}}, error) { func (q {{$tableNameSingular}}Query) AllP() {{$tableNameSingular}}Slice { o, err := q.All() if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return o @@ -49,7 +49,7 @@ func (q {{$tableNameSingular}}Query) All() ({{$tableNameSingular}}Slice, error) err := q.Bind(&o) if err != nil { - return nil, errors.Wrap(err, "{{.PkgName}}: failed to assign all query results to {{$tableNameSingular}} slice") + return nil, errors.Prefix("{{.PkgName}}: failed to assign all query results to {{$tableNameSingular}} slice", err) } {{if not .NoHooks -}} @@ -69,7 +69,7 @@ func (q {{$tableNameSingular}}Query) All() ({{$tableNameSingular}}Slice, error) func (q {{$tableNameSingular}}Query) CountP() int64 { c, err := q.Count() if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return c @@ -84,7 +84,7 @@ func (q {{$tableNameSingular}}Query) Count() (int64, error) { err := q.Query.QueryRow().Scan(&count) if err != nil { - return 0, errors.Wrap(err, "{{.PkgName}}: failed to count {{.Table.Name}} rows") + return 0, errors.Prefix("{{.PkgName}}: failed to count {{.Table.Name}} rows", err) } return count, nil @@ -94,7 +94,7 @@ func (q {{$tableNameSingular}}Query) Count() (int64, error) { func (q {{$tableNameSingular}}Query) ExistsP() bool { e, err := q.Exists() if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return e @@ -110,7 +110,7 @@ func (q {{$tableNameSingular}}Query) Exists() (bool, error) { err := q.Query.QueryRow().Scan(&count) if err != nil { - return false, errors.Wrap(err, "{{.PkgName}}: failed to check if {{.Table.Name}} exists") + return false, errors.Prefix("{{.PkgName}}: failed to check if {{.Table.Name}} exists", err) } return count > 0, nil diff --git a/templates/07_relationship_to_one_eager.tpl b/templates/07_relationship_to_one_eager.tpl index 3b24f1a..f6bba5b 100644 --- a/templates/07_relationship_to_one_eager.tpl +++ b/templates/07_relationship_to_one_eager.tpl @@ -45,20 +45,20 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula results, err := e.Query(query, args...) if err != nil { - return errors.Wrap(err, "failed to eager load {{$txt.ForeignTable.NameGo}}") + return errors.Prefix("failed to eager load {{$txt.ForeignTable.NameGo}}", err) } defer results.Close() var resultSlice []*{{$txt.ForeignTable.NameGo}} if err = queries.Bind(results, &resultSlice); err != nil { - return errors.Wrap(err, "failed to bind eager loaded slice {{$txt.ForeignTable.NameGo}}") + return errors.Prefix("failed to bind eager loaded slice {{$txt.ForeignTable.NameGo}}", err) } {{if not $dot.NoHooks -}} if len({{$varNameSingular}}AfterSelectHooks) != 0 { for _, obj := range resultSlice { if err := obj.doAfterSelectHooks(e); err != nil { - return err + return errors.Err(err) } } } diff --git a/templates/08_relationship_one_to_one_eager.tpl b/templates/08_relationship_one_to_one_eager.tpl index 37d1af0..cd587fc 100644 --- a/templates/08_relationship_one_to_one_eager.tpl +++ b/templates/08_relationship_one_to_one_eager.tpl @@ -45,20 +45,20 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula results, err := e.Query(query, args...) if err != nil { - return errors.Wrap(err, "failed to eager load {{$txt.ForeignTable.NameGo}}") + return errors.Prefix("failed to eager load {{$txt.ForeignTable.NameGo}}", err) } defer results.Close() var resultSlice []*{{$txt.ForeignTable.NameGo}} if err = queries.Bind(results, &resultSlice); err != nil { - return errors.Wrap(err, "failed to bind eager loaded slice {{$txt.ForeignTable.NameGo}}") + return errors.Prefix("failed to bind eager loaded slice {{$txt.ForeignTable.NameGo}}", err) } {{if not $dot.NoHooks -}} if len({{$varNameSingular}}AfterSelectHooks) != 0 { for _, obj := range resultSlice { if err := obj.doAfterSelectHooks(e); err != nil { - return err + return errors.Err(err) } } } diff --git a/templates/09_relationship_to_many_eager.tpl b/templates/09_relationship_to_many_eager.tpl index 1603188..f3fd2c4 100644 --- a/templates/09_relationship_to_many_eager.tpl +++ b/templates/09_relationship_to_many_eager.tpl @@ -54,7 +54,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula results, err := e.Query(query, args...) if err != nil { - return errors.Wrap(err, "failed to eager load {{.ForeignTable}}") + return errors.Prefix("failed to eager load {{.ForeignTable}}", err) } defer results.Close() @@ -70,7 +70,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula err = results.Scan({{$foreignTable.Columns | columnNames | stringMap $dot.StringFuncs.titleCase | prefixStringSlice "&one." | join ", "}}, &localJoinCol) if err = results.Err(); err != nil { - return errors.Wrap(err, "failed to plebian-bind eager loaded slice {{.ForeignTable}}") + return errors.Prefix("failed to plebian-bind eager loaded slice {{.ForeignTable}}", err) } resultSlice = append(resultSlice, one) @@ -78,11 +78,11 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula } if err = results.Err(); err != nil { - return errors.Wrap(err, "failed to plebian-bind eager loaded slice {{.ForeignTable}}") + return errors.Prefix("failed to plebian-bind eager loaded slice {{.ForeignTable}}", err) } {{else -}} if err = queries.Bind(results, &resultSlice); err != nil { - return errors.Wrap(err, "failed to bind eager loaded slice {{.ForeignTable}}") + return errors.Prefix("failed to bind eager loaded slice {{.ForeignTable}}", err) } {{end}} @@ -90,7 +90,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula if len({{.ForeignTable | singular | camelCase}}AfterSelectHooks) != 0 { for _, obj := range resultSlice { if err := obj.doAfterSelectHooks(e); err != nil { - return err + return errors.Err(err) } } } diff --git a/templates/10_relationship_to_one_setops.tpl b/templates/10_relationship_to_one_setops.tpl index 14f726e..200adc3 100644 --- a/templates/10_relationship_to_one_setops.tpl +++ b/templates/10_relationship_to_one_setops.tpl @@ -20,7 +20,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}G(insert bool, rel // Panics on error. func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}P(exec boil.Executor, insert bool, related *{{$txt.ForeignTable.NameGo}}) { if err := o.Set{{$txt.Function.Name}}(exec, insert, related); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -30,7 +30,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}P(exec boil.Execut // Uses the global database handle and panics on error. func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}GP(insert bool, related *{{$txt.ForeignTable.NameGo}}) { if err := o.Set{{$txt.Function.Name}}(boil.GetDB(), insert, related); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -41,7 +41,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo var err error if insert { if err = related.Insert(exec); err != nil { - return errors.Wrap(err, "failed to insert into foreign table") + return errors.Prefix("failed to insert into foreign table", err) } } @@ -58,7 +58,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo } if _, err = exec.Exec(updateQuery, values...); err != nil { - return errors.Wrap(err, "failed to update local table") + return errors.Prefix("failed to update local table", err) } o.{{$txt.Function.LocalAssignment}} = related.{{$txt.Function.ForeignAssignment}} @@ -110,7 +110,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}G(related *{{$t // Panics on error. func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}P(exec boil.Executor, related *{{$txt.ForeignTable.NameGo}}) { if err := o.Remove{{$txt.Function.Name}}(exec, related); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -120,7 +120,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}P(exec boil.Exe // Uses the global database handle and panics on error. func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}GP(related *{{$txt.ForeignTable.NameGo}}) { if err := o.Remove{{$txt.Function.Name}}(boil.GetDB(), related); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -133,7 +133,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Exec o.{{$txt.LocalTable.ColumnNameGo}}.Valid = false if err = o.Update(exec, "{{.Column}}"); err != nil { o.{{$txt.LocalTable.ColumnNameGo}}.Valid = true - return errors.Wrap(err, "failed to update local table") + return errors.Prefix("failed to update local table", err) } o.R.{{$txt.Function.Name}} = nil diff --git a/templates/11_relationship_one_to_one_setops.tpl b/templates/11_relationship_one_to_one_setops.tpl index 826eec2..29ce0a1 100644 --- a/templates/11_relationship_one_to_one_setops.tpl +++ b/templates/11_relationship_one_to_one_setops.tpl @@ -21,7 +21,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}G(insert bool, rel // Panics on error. func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}P(exec boil.Executor, insert bool, related *{{$txt.ForeignTable.NameGo}}) { if err := o.Set{{$txt.Function.Name}}(exec, insert, related); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -31,7 +31,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}P(exec boil.Execut // Uses the global database handle and panics on error. func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}GP(insert bool, related *{{$txt.ForeignTable.NameGo}}) { if err := o.Set{{$txt.Function.Name}}(boil.GetDB(), insert, related); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -48,7 +48,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo {{- end}} if err = related.Insert(exec); err != nil { - return errors.Wrap(err, "failed to insert into foreign table") + return errors.Prefix("failed to insert into foreign table", err) } } else { updateQuery := fmt.Sprintf( @@ -64,7 +64,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo } if _, err = exec.Exec(updateQuery, values...); err != nil { - return errors.Wrap(err, "failed to update foreign table") + return errors.Prefix("failed to update foreign table", err) } related.{{$txt.Function.ForeignAssignment}} = o.{{$txt.Function.LocalAssignment}} @@ -107,7 +107,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}G(related *{{$t // Panics on error. func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}P(exec boil.Executor, related *{{$txt.ForeignTable.NameGo}}) { if err := o.Remove{{$txt.Function.Name}}(exec, related); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -117,7 +117,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}P(exec boil.Exe // Uses the global database handle and panics on error. func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}GP(related *{{$txt.ForeignTable.NameGo}}) { if err := o.Remove{{$txt.Function.Name}}(boil.GetDB(), related); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -130,7 +130,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Exec related.{{$txt.ForeignTable.ColumnNameGo}}.Valid = false if err = related.Update(exec, "{{.ForeignColumn}}"); err != nil { related.{{$txt.ForeignTable.ColumnNameGo}}.Valid = true - return errors.Wrap(err, "failed to update local table") + return errors.Prefix("failed to update local table", err) } o.R.{{$txt.Function.Name}} = nil diff --git a/templates/12_relationship_to_many_setops.tpl b/templates/12_relationship_to_many_setops.tpl index 06159ee..e54c2fd 100644 --- a/templates/12_relationship_to_many_setops.tpl +++ b/templates/12_relationship_to_many_setops.tpl @@ -24,7 +24,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}G(insert bool, rel // Panics on error. func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}P(exec boil.Executor, insert bool, related ...*{{$txt.ForeignTable.NameGo}}) { if err := o.Add{{$txt.Function.Name}}(exec, insert, related...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -35,7 +35,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}P(exec boil.Execut // Uses the global database handle and panics on error. func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}GP(insert bool, related ...*{{$txt.ForeignTable.NameGo}}) { if err := o.Add{{$txt.Function.Name}}(boil.GetDB(), insert, related...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -55,7 +55,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo {{end -}} if err = rel.Insert(exec); err != nil { - return errors.Wrap(err, "failed to insert into foreign table") + return errors.Prefix("failed to insert into foreign table", err) } }{{if not .ToJoinTable}} else { updateQuery := fmt.Sprintf( @@ -71,7 +71,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo } if _, err = exec.Exec(updateQuery, values...); err != nil { - return errors.Wrap(err, "failed to update foreign table") + return errors.Prefix("failed to update foreign table", err) } rel.{{$txt.Function.ForeignAssignment}} = o.{{$txt.Function.LocalAssignment}} @@ -93,7 +93,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executo _, err = exec.Exec(query, values...) if err != nil { - return errors.Wrap(err, "failed to insert into join table") + return errors.Prefix("failed to insert into join table", err) } } {{end -}} @@ -152,7 +152,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}G(insert bool, rel // Panics on error. func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}P(exec boil.Executor, insert bool, related ...*{{$txt.ForeignTable.NameGo}}) { if err := o.Set{{$txt.Function.Name}}(exec, insert, related...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -165,7 +165,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}P(exec boil.Execut // Uses the global database handle and panics on error. func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}GP(insert bool, related ...*{{$txt.ForeignTable.NameGo}}) { if err := o.Set{{$txt.Function.Name}}(boil.GetDB(), insert, related...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -190,7 +190,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executo _, err := exec.Exec(query, values...) if err != nil { - return errors.Wrap(err, "failed to remove relationships before set") + return errors.Prefix("failed to remove relationships before set", err) } {{if .ToJoinTable -}} @@ -230,7 +230,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}G(related ...*{ // Panics on error. func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}P(exec boil.Executor, related ...*{{$txt.ForeignTable.NameGo}}) { if err := o.Remove{{$txt.Function.Name}}(exec, related...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -240,7 +240,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}P(exec boil.Exe // Uses the global database handle and panics on error. func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}GP(related ...*{{$txt.ForeignTable.NameGo}}) { if err := o.Remove{{$txt.Function.Name}}(boil.GetDB(), related...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -266,7 +266,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Exec _, err = exec.Exec(query, values...) if err != nil { - return errors.Wrap(err, "failed to remove relationships before set") + return errors.Prefix("failed to remove relationships before set", err) } {{else -}} for _, rel := range related { @@ -277,7 +277,7 @@ func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Exec } {{end -}} if err = rel.Update(exec, "{{.ForeignColumn}}"); err != nil { - return err + return errors.Err(err) } } {{end -}} diff --git a/templates/14_find.tpl b/templates/14_find.tpl index b7f69db..f9a88c8 100644 --- a/templates/14_find.tpl +++ b/templates/14_find.tpl @@ -12,7 +12,7 @@ func Find{{$tableNameSingular}}G({{$pkArgs}}, selectCols ...string) (*{{$tableNa func Find{{$tableNameSingular}}GP({{$pkArgs}}, selectCols ...string) *{{$tableNameSingular}} { retobj, err := Find{{$tableNameSingular}}(boil.GetDB(), {{$pkNames | join ", "}}, selectCols...) if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return retobj @@ -35,10 +35,10 @@ func Find{{$tableNameSingular}}(exec boil.Executor, {{$pkArgs}}, selectCols ...s err := q.Bind({{$varNameSingular}}Obj) if err != nil { - if errors.Cause(err) == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } - return nil, errors.Wrap(err, "{{.PkgName}}: unable to select from {{.Table.Name}}") + return nil, errors.Prefix("{{.PkgName}}: unable to select from {{.Table.Name}}", err) } return {{$varNameSingular}}Obj, nil @@ -48,7 +48,7 @@ func Find{{$tableNameSingular}}(exec boil.Executor, {{$pkArgs}}, selectCols ...s func Find{{$tableNameSingular}}P(exec boil.Executor, {{$pkArgs}}, selectCols ...string) *{{$tableNameSingular}} { retobj, err := Find{{$tableNameSingular}}(exec, {{$pkNames | join ", "}}, selectCols...) if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return retobj @@ -64,10 +64,10 @@ func FindOne{{$tableNameSingular}}(exec boil.Executor, filters {{$tableNameSingu Bind(obj) if err != nil { - if errors.Cause(err) == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } - return nil, errors.Wrap(err, "{{.PkgName}}: unable to select from {{.Table.Name}}") + return nil, errors.Prefix("{{.PkgName}}: unable to select from {{.Table.Name}}", err) } return obj, nil diff --git a/templates/15_insert.tpl b/templates/15_insert.tpl index 870e20d..20905ac 100644 --- a/templates/15_insert.tpl +++ b/templates/15_insert.tpl @@ -10,7 +10,7 @@ func (o *{{$tableNameSingular}}) InsertG(whitelist ... string) error { // behavior description. func (o *{{$tableNameSingular}}) InsertGP(whitelist ... string) { if err := o.Insert(boil.GetDB(), whitelist...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -18,7 +18,7 @@ func (o *{{$tableNameSingular}}) InsertGP(whitelist ... string) { // for whitelist behavior description. func (o *{{$tableNameSingular}}) InsertP(exec boil.Executor, whitelist ... string) { if err := o.Insert(exec, whitelist...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -29,7 +29,7 @@ func (o *{{$tableNameSingular}}) InsertP(exec boil.Executor, whitelist ... strin // - All columns with a default, but non-zero are included (i.e. health = 75) func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string) error { if o == nil { - return errors.New("{{.PkgName}}: no {{.Table.Name}} provided for insertion") + return errors.Err("{{.PkgName}}: no {{.Table.Name}} provided for insertion") } var err error @@ -37,7 +37,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string {{if not .NoHooks -}} if err := o.doBeforeInsertHooks(exec); err != nil { - return err + return errors.Err(err) } {{- end}} @@ -59,11 +59,11 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string cache.valueMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, wl) if err != nil { - return err + return errors.Err(err) } cache.retMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, returnColumns) if err != nil { - return err + return errors.Err(err) } if len(wl) != 0 { cache.query = fmt.Sprintf("INSERT INTO {{$schemaTable}} ({{.LQ}}%s{{.RQ}}) %%sVALUES (%s)%%s", strings.Join(wl, "{{.RQ}},{{.LQ}}"), strmangle.Placeholders(dialect.IndexPlaceholders, len(wl), 1, 1)) @@ -110,7 +110,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string _, err = exec.Exec(cache.query, vals...) {{- end}} if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to insert into {{.Table.Name}}") + return errors.Prefix("{{.PkgName}}: unable to insert into {{.Table.Name}}", err) } {{if $canLastInsertID -}} @@ -125,7 +125,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string {{if $canLastInsertID -}} lastID, err = result.LastInsertId() if err != nil { - return ErrSyncFail + return errors.Err(ErrSyncFail) } {{$colName := index .Table.PKey.Columns 0 -}} @@ -150,7 +150,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string err = exec.QueryRow(cache.retQuery, identifierCols...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...) if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to populate default values for {{.Table.Name}}") + return errors.Prefix("{{.PkgName}}: unable to populate default values for {{.Table.Name}}", err) } {{else}} if len(cache.retMapping) != 0 { @@ -160,7 +160,7 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string } if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to insert into {{.Table.Name}}") + return errors.Prefix("{{.PkgName}}: unable to insert into {{.Table.Name}}", err) } {{end}} diff --git a/templates/16_update.tpl b/templates/16_update.tpl index bc4a6f5..eee2036 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -12,7 +12,7 @@ func (o *{{$tableNameSingular}}) UpdateG(whitelist ...string) error { // Panics on error. See Update for whitelist behavior description. func (o *{{$tableNameSingular}}) UpdateGP(whitelist ...string) { if err := o.Update(boil.GetDB(), whitelist...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -21,7 +21,7 @@ func (o *{{$tableNameSingular}}) UpdateGP(whitelist ...string) { func (o *{{$tableNameSingular}}) UpdateP(exec boil.Executor, whitelist ... string) { err := o.Update(exec, whitelist...) if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -38,7 +38,7 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string var err error {{if not .NoHooks -}} if err = o.doBeforeUpdateHooks(exec); err != nil { - return err + return errors.Err(err) } {{end -}} @@ -62,7 +62,7 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string } {{end -}} if len(wl) == 0 { - return errors.New("{{.PkgName}}: unable to update {{.Table.Name}}, could not build whitelist") + return errors.Err("{{.PkgName}}: unable to update {{.Table.Name}}, could not build whitelist") } cache.query = fmt.Sprintf("UPDATE {{$schemaTable}} SET %s WHERE %s", @@ -71,7 +71,7 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string ) cache.valueMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, append(wl, {{$varNameSingular}}PrimaryKeyColumns...)) if err != nil { - return err + return errors.Err(err) } } @@ -84,7 +84,7 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string _, err = exec.Exec(cache.query, values...) if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to update {{.Table.Name}} row") + return errors.Prefix("{{.PkgName}}: unable to update {{.Table.Name}} row", err) } if !cached { @@ -103,7 +103,7 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string // UpdateAllP updates all rows with matching column names, and panics on error. func (q {{$tableNameSingular}}Query) UpdateAllP(cols M) { if err := q.UpdateAll(cols); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -113,7 +113,7 @@ func (q {{$tableNameSingular}}Query) UpdateAll(cols M) error { _, err := q.Query.Exec() if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to update all for {{.Table.Name}}") + return errors.Prefix("{{.PkgName}}: unable to update all for {{.Table.Name}}", err) } return nil @@ -127,14 +127,14 @@ func (o {{$tableNameSingular}}Slice) UpdateAllG(cols M) error { // UpdateAllGP updates all rows with the specified column values, and panics on error. func (o {{$tableNameSingular}}Slice) UpdateAllGP(cols M) { if err := o.UpdateAll(boil.GetDB(), cols); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } // UpdateAllP updates all rows with the specified column values, and panics on error. func (o {{$tableNameSingular}}Slice) UpdateAllP(exec boil.Executor, cols M) { if err := o.UpdateAll(exec, cols); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -146,7 +146,7 @@ func (o {{$tableNameSingular}}Slice) UpdateAll(exec boil.Executor, cols M) error } if len(cols) == 0 { - return errors.New("{{.PkgName}}: update all requires at least one column argument") + return errors.Err("{{.PkgName}}: update all requires at least one column argument") } colNames := make([]string, len(cols)) @@ -176,7 +176,7 @@ func (o {{$tableNameSingular}}Slice) UpdateAll(exec boil.Executor, cols M) error _, err := exec.Exec(sql, args...) if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to update all in {{$varNameSingular}} slice") + return errors.Prefix("{{.PkgName}}: unable to update all in {{$varNameSingular}} slice", err) } return nil diff --git a/templates/17_upsert.tpl b/templates/17_upsert.tpl index dbce58c..b713243 100644 --- a/templates/17_upsert.tpl +++ b/templates/17_upsert.tpl @@ -9,7 +9,7 @@ func (o *{{$tableNameSingular}}) UpsertG({{if eq .DriverName "postgres"}}updateO // UpsertGP attempts an insert, and does an update or ignore on conflict. Panics on error. func (o *{{$tableNameSingular}}) UpsertGP({{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) { if err := o.Upsert(boil.GetDB(), {{if eq .DriverName "postgres"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -17,21 +17,21 @@ func (o *{{$tableNameSingular}}) UpsertGP({{if eq .DriverName "postgres"}}update // UpsertP panics on error. func (o *{{$tableNameSingular}}) UpsertP(exec boil.Executor, {{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) { if err := o.Upsert(exec, {{if eq .DriverName "postgres"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } // Upsert attempts an insert using an executor, and does an update or ignore on conflict. func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) error { if o == nil { - return errors.New("{{.PkgName}}: no {{.Table.Name}} provided for upsert") + return errors.Err("{{.PkgName}}: no {{.Table.Name}} provided for upsert") } {{- template "timestamp_upsert_helper" . }} {{if not .NoHooks -}} if err := o.doBeforeUpsertHooks(exec); err != nil { - return err + return errors.Err(err) } {{- end}} @@ -87,7 +87,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName } } if len(insert) == 0 { - return errors.New("{{.PkgName}}: unable to upsert {{.Table.Name}}, could not build insert column list") + return errors.Err("{{.PkgName}}: unable to upsert {{.Table.Name}}, could not build insert column list") } ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithAuto) @@ -104,7 +104,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName {{end -}} if len(update) == 0 { - return errors.New("{{.PkgName}}: unable to upsert {{.Table.Name}}, could not build update column list") + return errors.Err("{{.PkgName}}: unable to upsert {{.Table.Name}}, could not build update column list") } {{if eq .DriverName "postgres"}} @@ -129,12 +129,12 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName cache.valueMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, {{if eq .DriverName "mssql"}}whitelist{{else}}insert{{end}}) if err != nil { - return err + return errors.Err(err) } if len(ret) != 0 { cache.retMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, ret) if err != nil { - return err + return errors.Err(err) } } } @@ -159,7 +159,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName _, err = exec.Exec(cache.query, vals...) {{- end}} if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to upsert for {{.Table.Name}}") + return errors.Prefix("{{.PkgName}}: unable to upsert for {{.Table.Name}}", err) } {{if $canLastInsertID -}} @@ -174,7 +174,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName {{if $canLastInsertID -}} lastID, err = result.LastInsertId() if err != nil { - return ErrSyncFail + return errors.Err(ErrSyncFail) } {{$colName := index .Table.PKey.Columns 0 -}} @@ -199,7 +199,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName err = exec.QueryRow(cache.retQuery, identifierCols...).Scan(returns...) if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to populate default values for {{.Table.Name}}") + return errors.Prefix("{{.PkgName}}: unable to populate default values for {{.Table.Name}}", err) } {{- else}} if len(cache.retMapping) != 0 { @@ -211,7 +211,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName _, err = exec.Exec(cache.query, vals...) } if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to upsert {{.Table.Name}}") + return errors.Prefix("{{.PkgName}}: unable to upsert {{.Table.Name}}", err) } {{- end}} diff --git a/templates/18_delete.tpl b/templates/18_delete.tpl index aea163e..1ab82a3 100644 --- a/templates/18_delete.tpl +++ b/templates/18_delete.tpl @@ -6,7 +6,7 @@ // Panics on error. func (o *{{$tableNameSingular}}) DeleteP(exec boil.Executor) { if err := o.Delete(exec); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -14,7 +14,7 @@ func (o *{{$tableNameSingular}}) DeleteP(exec boil.Executor) { // DeleteG will match against the primary key column to find the record to delete. func (o *{{$tableNameSingular}}) DeleteG() error { if o == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}} provided for deletion") + return errors.Err("{{.PkgName}}: no {{$tableNameSingular}} provided for deletion") } return o.Delete(boil.GetDB()) @@ -25,7 +25,7 @@ func (o *{{$tableNameSingular}}) DeleteG() error { // Panics on error. func (o *{{$tableNameSingular}}) DeleteGP() { if err := o.DeleteG(); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -33,12 +33,12 @@ func (o *{{$tableNameSingular}}) DeleteGP() { // Delete will match against the primary key column to find the record to delete. func (o *{{$tableNameSingular}}) Delete(exec boil.Executor) error { if o == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}} provided for delete") + return errors.Err("{{.PkgName}}: no {{$tableNameSingular}} provided for delete") } {{if not .NoHooks -}} if err := o.doBeforeDeleteHooks(exec); err != nil { - return err + return errors.Err(err) } {{- end}} @@ -52,12 +52,12 @@ func (o *{{$tableNameSingular}}) Delete(exec boil.Executor) error { _, err := exec.Exec(sql, args...) if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to delete from {{.Table.Name}}") + return errors.Prefix("{{.PkgName}}: unable to delete from {{.Table.Name}}", err) } {{if not .NoHooks -}} if err := o.doAfterDeleteHooks(exec); err != nil { - return err + return errors.Err(err) } {{- end}} @@ -67,21 +67,21 @@ func (o *{{$tableNameSingular}}) Delete(exec boil.Executor) error { // DeleteAllP deletes all rows, and panics on error. func (q {{$tableNameSingular}}Query) DeleteAllP() { if err := q.DeleteAll(); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } // DeleteAll deletes all matching rows. func (q {{$tableNameSingular}}Query) DeleteAll() error { if q.Query == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}}Query provided for delete all") + return errors.Err("{{.PkgName}}: no {{$tableNameSingular}}Query provided for delete all") } queries.SetDelete(q.Query) _, err := q.Query.Exec() if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to delete all from {{.Table.Name}}") + return errors.Prefix("{{.PkgName}}: unable to delete all from {{.Table.Name}}", err) } return nil @@ -90,14 +90,14 @@ func (q {{$tableNameSingular}}Query) DeleteAll() error { // DeleteAllGP deletes all rows in the slice, and panics on error. func (o {{$tableNameSingular}}Slice) DeleteAllGP() { if err := o.DeleteAllG(); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } // DeleteAllG deletes all rows in the slice. func (o {{$tableNameSingular}}Slice) DeleteAllG() error { if o == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}} slice provided for delete all") + return errors.Err("{{.PkgName}}: no {{$tableNameSingular}} slice provided for delete all") } return o.DeleteAll(boil.GetDB()) } @@ -105,14 +105,14 @@ func (o {{$tableNameSingular}}Slice) DeleteAllG() error { // DeleteAllP deletes all rows in the slice, using an executor, and panics on error. func (o {{$tableNameSingular}}Slice) DeleteAllP(exec boil.Executor) { if err := o.DeleteAll(exec); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } // DeleteAll deletes all rows in the slice, using an executor. func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error { if o == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}} slice provided for delete all") + return errors.Err("{{.PkgName}}: no {{$tableNameSingular}} slice provided for delete all") } if len(o) == 0 { @@ -123,7 +123,7 @@ func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error { if len({{$varNameSingular}}BeforeDeleteHooks) != 0 { for _, obj := range o { if err := obj.doBeforeDeleteHooks(exec); err != nil { - return err + return errors.Err(err) } } } @@ -145,14 +145,14 @@ func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error { _, err := exec.Exec(sql, args...) if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to delete all from {{$varNameSingular}} slice") + return errors.Prefix("{{.PkgName}}: unable to delete all from {{$varNameSingular}} slice", err) } {{if not .NoHooks -}} if len({{$varNameSingular}}AfterDeleteHooks) != 0 { for _, obj := range o { if err := obj.doAfterDeleteHooks(exec); err != nil { - return err + return errors.Err(err) } } } diff --git a/templates/19_reload.tpl b/templates/19_reload.tpl index b1201b8..c7c5273 100644 --- a/templates/19_reload.tpl +++ b/templates/19_reload.tpl @@ -5,21 +5,21 @@ // ReloadGP refetches the object from the database and panics on error. func (o *{{$tableNameSingular}}) ReloadGP() { if err := o.ReloadG(); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } // ReloadP refetches the object from the database with an executor. Panics on error. func (o *{{$tableNameSingular}}) ReloadP(exec boil.Executor) { if err := o.Reload(exec); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } // ReloadG refetches the object from the database using the primary keys. func (o *{{$tableNameSingular}}) ReloadG() error { if o == nil { - return errors.New("{{.PkgName}}: no {{$tableNameSingular}} provided for reload") + return errors.Err("{{.PkgName}}: no {{$tableNameSingular}} provided for reload") } return o.Reload(boil.GetDB()) @@ -30,7 +30,7 @@ func (o *{{$tableNameSingular}}) ReloadG() error { func (o *{{$tableNameSingular}}) Reload(exec boil.Executor) error { ret, err := Find{{$tableNameSingular}}(exec, {{.Table.PKey.Columns | stringMap .StringFuncs.titleCase | prefixStringSlice "o." | join ", "}}) if err != nil { - return err + return errors.Err(err) } *o = *ret @@ -42,7 +42,7 @@ func (o *{{$tableNameSingular}}) Reload(exec boil.Executor) error { // Panics on error. func (o *{{$tableNameSingular}}Slice) ReloadAllGP() { if err := o.ReloadAllG(); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -51,7 +51,7 @@ func (o *{{$tableNameSingular}}Slice) ReloadAllGP() { // Panics on error. func (o *{{$tableNameSingular}}Slice) ReloadAllP(exec boil.Executor) { if err := o.ReloadAll(exec); err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } } @@ -59,7 +59,7 @@ func (o *{{$tableNameSingular}}Slice) ReloadAllP(exec boil.Executor) { // and overwrites the original object slice with the newly updated slice. func (o *{{$tableNameSingular}}Slice) ReloadAllG() error { if o == nil { - return errors.New("{{.PkgName}}: empty {{$tableNameSingular}}Slice provided for reload all") + return errors.Err("{{.PkgName}}: empty {{$tableNameSingular}}Slice provided for reload all") } return o.ReloadAll(boil.GetDB()) @@ -86,7 +86,7 @@ func (o *{{$tableNameSingular}}Slice) ReloadAll(exec boil.Executor) error { err := q.Bind(&{{$varNamePlural}}) if err != nil { - return errors.Wrap(err, "{{.PkgName}}: unable to reload all in {{$tableNameSingular}}Slice") + return errors.Prefix("{{.PkgName}}: unable to reload all in {{$tableNameSingular}}Slice", err) } *o = {{$varNamePlural}} diff --git a/templates/20_exists.tpl b/templates/20_exists.tpl index 2e20fa6..2a36c23 100644 --- a/templates/20_exists.tpl +++ b/templates/20_exists.tpl @@ -22,7 +22,7 @@ func {{$tableNameSingular}}Exists(exec boil.Executor, {{$pkArgs}}) (bool, error) err := row.Scan(&exists) if err != nil { - return false, errors.Wrap(err, "{{.PkgName}}: unable to check if {{.Table.Name}} exists") + return false, errors.Prefix("{{.PkgName}}: unable to check if {{.Table.Name}} exists", err) } return exists, nil @@ -37,7 +37,7 @@ func {{$tableNameSingular}}ExistsG({{$pkArgs}}) (bool, error) { func {{$tableNameSingular}}ExistsGP({{$pkArgs}}) bool { e, err := {{$tableNameSingular}}Exists(boil.GetDB(), {{$pkNames | join ", "}}) if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return e @@ -47,7 +47,7 @@ func {{$tableNameSingular}}ExistsGP({{$pkArgs}}) bool { func {{$tableNameSingular}}ExistsP(exec boil.Executor, {{$pkArgs}}) bool { e, err := {{$tableNameSingular}}Exists(exec, {{$pkNames | join ", "}}) if err != nil { - panic(boil.WrapErr(err)) + panic(errors.Err(err)) } return e diff --git a/templates/23_merge.tpl b/templates/23_merge.tpl index 9f1af10..16a7b58 100644 --- a/templates/23_merge.tpl +++ b/templates/23_merge.tpl @@ -9,12 +9,12 @@ func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID if !ok { txdb, ok := exec.(boil.Beginner) if !ok { - return errors.New("database does not support transactions") + return errors.Err("database does not support transactions") } tx, err = txdb.Begin() if err != nil { - return err + return errors.Err(err) } defer func() { @@ -31,16 +31,16 @@ func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID primary, err := Find{{$tableNameSingular}}(tx, primaryID) if err != nil { - return err + return errors.Err(err) } else if primary == nil { - return errors.New("Primary {{$tableNameSingular}} not found") + return errors.Err("primary {{$tableNameSingular}} not found") } secondary, err := Find{{$tableNameSingular}}(tx, secondaryID) if err != nil { - return err + return errors.Err(err) } else if secondary == nil { - return errors.New("Secondary {{$tableNameSingular}} not found") + return errors.Err("secondary {{$tableNameSingular}} not found") } foreignKeys := []foreignKey{ @@ -89,12 +89,12 @@ func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID err = primary.Update(tx) if err != nil { - return errors.WithStack(err) + return err } err = secondary.Delete(tx) if err != nil { - return errors.WithStack(err) + return err } return nil diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index 1260dde..51ed108 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -29,7 +29,7 @@ func mergeModels(tx boil.Executor, primaryID uint64, secondaryID uint64, foreign for _, conflict := range conflictingKeys { err = deleteConflictsBeforeMerge(tx, conflict, primaryID, secondaryID) if err != nil { - return errors.WithStack(err) + return err } } @@ -42,7 +42,7 @@ func mergeModels(tx boil.Executor, primaryID uint64, secondaryID uint64, foreign ) _, err = tx.Exec(query, primaryID, secondaryID) if err != nil { - return errors.WithStack(err) + return errors.Err(err) } } return checkMerge(tx, foreignKeys) @@ -54,7 +54,7 @@ func deleteConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, if len(conflictingColumns) < 1 { return nil } else if len(conflictingColumns) > 1 { - return errors.New("this doesnt work for unique keys with more than two columns (yet)") + return errors.Err("this doesnt work for unique keys with more than two columns (yet)") } query := fmt.Sprintf( @@ -67,7 +67,7 @@ func deleteConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, rows, err := tx.Query(query, primaryID, secondaryID) defer rows.Close() if err != nil { - return errors.WithStack(err) + return errors.Err(err) } args := []interface{}{secondaryID} @@ -75,7 +75,7 @@ func deleteConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, var value string err = rows.Scan(&value) if err != nil { - return errors.WithStack(err) + return errors.Err(err) } args = append(args, value) } @@ -93,7 +93,7 @@ func deleteConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, _, err = tx.Exec(query, args...) if err != nil { - return errors.WithStack(err) + return errors.Err(err) } return nil } @@ -118,7 +118,7 @@ func checkMerge(tx boil.Executor, foreignKeys []foreignKey) error { rows, err := tx.Query(q, uniqueColumns...) defer rows.Close() if err != nil { - return errors.WithStack(err) + return errors.Err(err) } for rows.Next() { @@ -126,11 +126,11 @@ func checkMerge(tx boil.Executor, foreignKeys []foreignKey) error { var columnName string err = rows.Scan(&tableName, &columnName) if err != nil { - return errors.WithStack(err) + return errors.Err(err) } if _, exists := handledTablesColumns[tableName+"."+columnName]; !exists { - return errors.New("Missing merge for " + tableName + "." + columnName) + return errors.Err("missing merge for " + tableName + "." + columnName) } } diff --git a/templates/singleton/boil_types.tpl b/templates/singleton/boil_types.tpl index 547d05d..48a85e2 100644 --- a/templates/singleton/boil_types.tpl +++ b/templates/singleton/boil_types.tpl @@ -20,7 +20,7 @@ type conflictingUniqueKey struct { // ErrSyncFail occurs during insert when the record could not be retrieved in // order to populate default value information. This usually happens when LastInsertId // fails or there was a primary key configuration that was not resolvable. -var ErrSyncFail = errors.New("{{.PkgName}}: failed to synchronize data after insert") +var ErrSyncFail = errors.Base("{{.PkgName}}: failed to synchronize data after insert") type insertCache struct { query string diff --git a/templates_test/main_test/mssql_main.tpl b/templates_test/main_test/mssql_main.tpl index ba46be7..5b0b5e1 100644 --- a/templates_test/main_test/mssql_main.tpl +++ b/templates_test/main_test/mssql_main.tpl @@ -25,17 +25,17 @@ func (m *mssqlTester) setup() error { m.testDBName = randomize.StableDBName(m.dbName) if err = m.dropTestDB(); err != nil { - return err + return errors.Err(err) } if err = m.createTestDB(); err != nil { - return err + return errors.Err(err) } createCmd := exec.Command("sqlcmd", "-S", m.host, "-U", m.user, "-P", m.pass, "-d", m.testDBName) f, err := os.Open("tables_schema.sql") if err != nil { - return errors.Wrap(err, "failed to open tables_schema.sql file") + return errors.Prefix("failed to open tables_schema.sql file", err) } defer f.Close() @@ -43,12 +43,12 @@ func (m *mssqlTester) setup() error { createCmd.Stdin = newFKeyDestroyer(rgxMSSQLkey, f) if err = createCmd.Start(); err != nil { - return errors.Wrap(err, "failed to start sqlcmd command") + return errors.Prefix("failed to start sqlcmd command", err) } if err = createCmd.Wait(); err != nil { fmt.Println(err) - return errors.Wrap(err, "failed to wait for sqlcmd command") + return errors.Prefix("failed to wait for sqlcmd command", err) } return nil @@ -92,7 +92,7 @@ func (m *mssqlTester) teardown() error { } if err := m.dropTestDB(); err != nil { - return err + return errors.Err(err) } return nil @@ -110,7 +110,7 @@ func (m *mssqlTester) runCmd(stdin, command string, args ...string) error { fmt.Println("failed running:", command, args) fmt.Println(stdout.String()) fmt.Println(stderr.String()) - return err + return errors.Err(err) } return nil diff --git a/templates_test/main_test/mysql_main.tpl b/templates_test/main_test/mysql_main.tpl index b95b32c..ef735e2 100644 --- a/templates_test/main_test/mysql_main.tpl +++ b/templates_test/main_test/mysql_main.tpl @@ -30,14 +30,14 @@ func (m *mysqlTester) setup() error { m.testDBName = randomize.StableDBName(m.dbName) if err = m.makeOptionFile(); err != nil { - return errors.Wrap(err, "couldn't make option file") + return errors.Prefix("couldn't make option file", err) } if err = m.dropTestDB(); err != nil { - return err + return errors.Err(err) } if err = m.createTestDB(); err != nil { - return err + return errors.Err(err) } dumpCmd := exec.Command("mysqldump", m.defaultsFile(), "--no-data", m.dbName) @@ -48,22 +48,22 @@ func (m *mysqlTester) setup() error { createCmd.Stdin = newFKeyDestroyer(rgxMySQLkey, r) if err = dumpCmd.Start(); err != nil { - return errors.Wrap(err, "failed to start mysqldump command") + return errors.Prefix("failed to start mysqldump command", err) } if err = createCmd.Start(); err != nil { - return errors.Wrap(err, "failed to start mysql command") + return errors.Prefix("failed to start mysql command", err) } if err = dumpCmd.Wait(); err != nil { fmt.Println(err) - return errors.Wrap(err, "failed to wait for mysqldump command") + return errors.Prefix("failed to wait for mysqldump command", err) } w.Close() // After dumpCmd is done, close the write end of the pipe if err = createCmd.Wait(); err != nil { fmt.Println(err) - return errors.Wrap(err, "failed to wait for mysql command") + return errors.Prefix("failed to wait for mysql command", err) } return nil @@ -87,7 +87,7 @@ func (m *mysqlTester) defaultsFile() string { func (m *mysqlTester) makeOptionFile() error { tmp, err := ioutil.TempFile("", "optionfile") if err != nil { - return errors.Wrap(err, "failed to create option file") + return errors.Prefix("failed to create option file", err) } isTCP := false @@ -95,7 +95,7 @@ func (m *mysqlTester) makeOptionFile() error { if os.IsNotExist(err) { isTCP = true } else if err != nil { - return errors.Wrap(err, "could not stat m.host") + return errors.Prefix("could not stat m.host", err) } fmt.Fprintln(tmp, "[client]") @@ -139,7 +139,7 @@ func (m *mysqlTester) teardown() error { } if err := m.dropTestDB(); err != nil { - return err + return errors.Err(err) } return os.Remove(m.optionFile) @@ -159,7 +159,7 @@ func (m *mysqlTester) runCmd(stdin, command string, args ...string) error { fmt.Println("failed running:", command, args) fmt.Println(stdout.String()) fmt.Println(stderr.String()) - return err + return errors.Err(err) } return nil diff --git a/templates_test/main_test/postgres_main.tpl b/templates_test/main_test/postgres_main.tpl index 0abcba3..3110325 100644 --- a/templates_test/main_test/postgres_main.tpl +++ b/templates_test/main_test/postgres_main.tpl @@ -33,14 +33,14 @@ func (p *pgTester) setup() error { p.testDBName = randomize.StableDBName(p.dbName) if err = p.makePGPassFile(); err != nil { - return err + return errors.Err(err) } if err = p.dropTestDB(); err != nil { - return err + return errors.Err(err) } if err = p.createTestDB(); err != nil { - return err + return errors.Err(err) } dumpCmd := exec.Command("pg_dump", "--schema-only", p.dbName) @@ -53,22 +53,22 @@ func (p *pgTester) setup() error { createCmd.Stdin = newFKeyDestroyer(rgxPGFkey, r) if err = dumpCmd.Start(); err != nil { - return errors.Wrap(err, "failed to start pg_dump command") + return errors.Prefix("failed to start pg_dump command", err) } if err = createCmd.Start(); err != nil { - return errors.Wrap(err, "failed to start psql command") + return errors.Prefix("failed to start psql command", err) } if err = dumpCmd.Wait(); err != nil { fmt.Println(err) - return errors.Wrap(err, "failed to wait for pg_dump command") + return errors.Prefix("failed to wait for pg_dump command", err) } w.Close() // After dumpCmd is done, close the write end of the pipe if err = createCmd.Wait(); err != nil { fmt.Println(err) - return errors.Wrap(err, "failed to wait for psql command") + return errors.Prefix("failed to wait for psql command", err) } return nil @@ -90,7 +90,7 @@ func (p *pgTester) runCmd(stdin, command string, args ...string) error { fmt.Println("failed running:", command, args) fmt.Println(stdout.String()) fmt.Println(stderr.String()) - return err + return errors.Err(err) } return nil @@ -108,7 +108,7 @@ func (p *pgTester) pgEnv() []string { func (p *pgTester) makePGPassFile() error { tmp, err := ioutil.TempFile("", "pgpass") if err != nil { - return errors.Wrap(err, "failed to create option file") + return errors.Prefix("failed to create option file", err) } fmt.Fprintf(tmp, "%s:%d:postgres:%s", p.host, p.port, p.user) @@ -145,12 +145,12 @@ func (p *pgTester) dropTestDB() error { func (p *pgTester) teardown() error { var err error if err = p.dbConn.Close(); err != nil { - return err + return errors.Err(err) } p.dbConn = nil if err = p.dropTestDB(); err != nil { - return err + return errors.Err(err) } return os.Remove(p.pgPassFile) diff --git a/templates_test/singleton/boil_main_test.tpl b/templates_test/singleton/boil_main_test.tpl index ebb4758..3cf466d 100644 --- a/templates_test/singleton/boil_main_test.tpl +++ b/templates_test/singleton/boil_main_test.tpl @@ -143,5 +143,5 @@ func validateConfig(driverName string) error { ).Check() } - return errors.New("not a valid driver name") + return errors.Err("not a valid driver name") } From 77fc991e7b786a1b1f373a274ff5e6457e43f2df Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Thu, 22 Feb 2018 13:18:41 -0500 Subject: [PATCH 168/179] new location for null.go --- boilingcore/imports.go | 50 ++++++++++++++++++------------------- boilingcore/imports_test.go | 10 ++++---- queries/helpers_test.go | 2 +- queries/query.go | 2 +- randomize/randomize.go | 2 +- randomize/randomize_test.go | 2 +- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/boilingcore/imports.go b/boilingcore/imports.go index b73b429..9a5c656 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -170,8 +170,8 @@ func newImporter() importer { `"time"`, }, thirdParty: importList{ - `"github.com/lbryio/errors.go"`, - `"github.com/lbryio/null.go"`, + `"github.com/lbryio/lbry.go/errors"`, + `"github.com/lbryio/lbry.go/null"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, @@ -185,7 +185,7 @@ func newImporter() importer { `"fmt"`, }, thirdParty: importList{ - `"github.com/lbryio/errors.go"`, + `"github.com/lbryio/lbry.go/errors"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, @@ -194,7 +194,7 @@ func newImporter() importer { }, "boil_types": { thirdParty: importList{ - `"github.com/lbryio/errors.go"`, + `"github.com/lbryio/lbry.go/errors"`, `"github.com/lbryio/sqlboiler/strmangle"`, }, }, @@ -227,7 +227,7 @@ func newImporter() importer { }, thirdParty: importList{ `"github.com/kat-co/vala"`, - `"github.com/lbryio/errors.go"`, + `"github.com/lbryio/lbry.go/errors"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/spf13/viper"`, }, @@ -265,7 +265,7 @@ func newImporter() importer { `"strings"`, }, thirdParty: importList{ - `"github.com/lbryio/errors.go"`, + `"github.com/lbryio/lbry.go/errors"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `_ "github.com/lib/pq"`, @@ -285,7 +285,7 @@ func newImporter() importer { }, thirdParty: importList{ `_ "github.com/go-sql-driver/mysql"`, - `"github.com/lbryio/errors.go"`, + `"github.com/lbryio/lbry.go/errors"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `"github.com/spf13/viper"`, @@ -302,7 +302,7 @@ func newImporter() importer { }, thirdParty: importList{ `_ "github.com/denisenkom/go-mssqldb"`, - `"github.com/lbryio/errors.go"`, + `"github.com/lbryio/lbry.go/errors"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `"github.com/spf13/viper"`, @@ -315,55 +315,55 @@ func newImporter() importer { // TranslateColumnType to see the type assignments. imp.BasedOnType = mapImports{ "null.Float32": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Float64": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Int": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Int8": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Int16": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Int32": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Int64": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Uint": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Uint8": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Uint16": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Uint32": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Uint64": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.String": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Bool": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Time": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.JSON": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "null.Bytes": { - thirdParty: importList{`"github.com/lbryio/null.go"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, }, "time.Time": { standard: importList{`"time"`}, diff --git a/boilingcore/imports_test.go b/boilingcore/imports_test.go index b25eece..693bc10 100644 --- a/boilingcore/imports_test.go +++ b/boilingcore/imports_test.go @@ -246,7 +246,7 @@ func TestCombineTypeImports(t *testing.T) { }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, - `"github.com/lbryio/null.go"`, + `"github.com/lbryio/lbry.go/null"`, }, } @@ -281,7 +281,7 @@ func TestCombineTypeImports(t *testing.T) { }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, - `"github.com/lbryio/null.go"`, + `"github.com/lbryio/lbry.go/null"`, }, } @@ -297,7 +297,7 @@ func TestCombineImports(t *testing.T) { a := imports{ standard: importList{"fmt"}, - thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/null.go"}, + thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/lbry.go/null"}, } b := imports{ standard: importList{"os"}, @@ -309,8 +309,8 @@ func TestCombineImports(t *testing.T) { if c.standard[0] != "fmt" && c.standard[1] != "os" { t.Errorf("Wanted: fmt, os got: %#v", c.standard) } - if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/null.go" { - t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/null.go got: %#v", c.thirdParty) + if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/lbry.go/null" { + t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/lbry.go/null got: %#v", c.thirdParty) } } diff --git a/queries/helpers_test.go b/queries/helpers_test.go index ab4b4ac..b6b757d 100644 --- a/queries/helpers_test.go +++ b/queries/helpers_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - null "github.com/lbryio/null.go" + null "github.com/lbryio/lbry.go/null" ) type testObj struct { diff --git a/queries/query.go b/queries/query.go index a76c046..fdd54b4 100644 --- a/queries/query.go +++ b/queries/query.go @@ -4,7 +4,7 @@ import ( "database/sql" "fmt" - "github.com/lbryio/errors.go" + "github.com/lbryio/lbry.go/errors" "github.com/lbryio/sqlboiler/boil" ) diff --git a/randomize/randomize.go b/randomize/randomize.go index 8deb88d..54c88bf 100644 --- a/randomize/randomize.go +++ b/randomize/randomize.go @@ -14,7 +14,7 @@ import ( "sync/atomic" "time" - null "github.com/lbryio/null.go" + null "github.com/lbryio/lbry.go/null" "github.com/pkg/errors" "github.com/satori/go.uuid" diff --git a/randomize/randomize_test.go b/randomize/randomize_test.go index ccef115..bc7a382 100644 --- a/randomize/randomize_test.go +++ b/randomize/randomize_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - null "github.com/lbryio/null.go" + null "github.com/lbryio/lbry.go/null" ) func TestRandomizeStruct(t *testing.T) { From 396f42bc91eec4d95f3dc1c5180a863175824033 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg <git@grin.io> Date: Mon, 16 Apr 2018 12:41:55 -0400 Subject: [PATCH 169/179] never update created_at, updated_at columns --- templates/16_update.tpl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/16_update.tpl b/templates/16_update.tpl index eee2036..a4a688c 100644 --- a/templates/16_update.tpl +++ b/templates/16_update.tpl @@ -56,11 +56,9 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string {{if eq .DriverName "mssql"}} wl = strmangle.SetComplement(wl, {{$varNameSingular}}ColumnsWithAuto) {{end}} - {{if not .NoAutoTimestamps}} if len(whitelist) == 0 { - wl = strmangle.SetComplement(wl, []string{"created_at"}) + wl = strmangle.SetComplement(wl, []string{"created_at","updated_at"}) } - {{end -}} if len(wl) == 0 { return errors.Err("{{.PkgName}}: unable to update {{.Table.Name}}, could not build whitelist") } From 8d4055e3eb1c18919978c53522a64d70139c43d1 Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr <markbeamerjr@gmail.com> Date: Wed, 6 Jun 2018 18:26:33 -0400 Subject: [PATCH 170/179] reverted default to native password authentication. --- bdb/drivers/mysql.go | 1 + 1 file changed, 1 insertion(+) diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index dec80e0..19855fb 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -53,6 +53,7 @@ func MySQLBuildQueryString(user, pass, dbname, host string, port int, sslmode st } config.Addr += ":" + strconv.Itoa(port) config.TLSConfig = sslmode + config.AllowNativePasswords = false // MySQL is a bad, and by default reads date/datetime into a []byte // instead of a time.Time. Tell it to stop being a bad. From 466f2d5b2c6a8f8996b1b2136e5807767625ce6f Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr <markbeamerjr@gmail.com> Date: Wed, 6 Jun 2018 19:03:50 -0400 Subject: [PATCH 171/179] make it true --- bdb/drivers/mysql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index 19855fb..5523a6b 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -53,7 +53,7 @@ func MySQLBuildQueryString(user, pass, dbname, host string, port int, sslmode st } config.Addr += ":" + strconv.Itoa(port) config.TLSConfig = sslmode - config.AllowNativePasswords = false + config.AllowNativePasswords = true // MySQL is a bad, and by default reads date/datetime into a []byte // instead of a time.Time. Tell it to stop being a bad. From 3baa9e72ca1cc55c8a8fb870190861ae56d9a8a1 Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr <markbeamerjr@gmail.com> Date: Thu, 10 Jan 2019 20:11:42 -0500 Subject: [PATCH 172/179] Updated to use the latest lbry.go changes --- boilingcore/imports.go | 50 ++++++++++++++++++------------------- boilingcore/imports_test.go | 10 ++++---- queries/helpers_test.go | 2 +- queries/query.go | 2 +- randomize/randomize.go | 2 +- randomize/randomize_test.go | 2 +- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/boilingcore/imports.go b/boilingcore/imports.go index 9a5c656..b5979e3 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -170,8 +170,8 @@ func newImporter() importer { `"time"`, }, thirdParty: importList{ - `"github.com/lbryio/lbry.go/errors"`, - `"github.com/lbryio/lbry.go/null"`, + `"github.com/lbryio/lbry.go/extras/errors"`, + `"github.com/lbryio/lbry.go/extras/null"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, @@ -185,7 +185,7 @@ func newImporter() importer { `"fmt"`, }, thirdParty: importList{ - `"github.com/lbryio/lbry.go/errors"`, + `"github.com/lbryio/lbry.go/extras/errors"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, @@ -194,7 +194,7 @@ func newImporter() importer { }, "boil_types": { thirdParty: importList{ - `"github.com/lbryio/lbry.go/errors"`, + `"github.com/lbryio/lbry.go/extras/errors"`, `"github.com/lbryio/sqlboiler/strmangle"`, }, }, @@ -227,7 +227,7 @@ func newImporter() importer { }, thirdParty: importList{ `"github.com/kat-co/vala"`, - `"github.com/lbryio/lbry.go/errors"`, + `"github.com/lbryio/lbry.go/extras/errors"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/spf13/viper"`, }, @@ -265,7 +265,7 @@ func newImporter() importer { `"strings"`, }, thirdParty: importList{ - `"github.com/lbryio/lbry.go/errors"`, + `"github.com/lbryio/lbry.go/extras/errors"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `_ "github.com/lib/pq"`, @@ -285,7 +285,7 @@ func newImporter() importer { }, thirdParty: importList{ `_ "github.com/go-sql-driver/mysql"`, - `"github.com/lbryio/lbry.go/errors"`, + `"github.com/lbryio/lbry.go/extras/errors"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `"github.com/spf13/viper"`, @@ -302,7 +302,7 @@ func newImporter() importer { }, thirdParty: importList{ `_ "github.com/denisenkom/go-mssqldb"`, - `"github.com/lbryio/lbry.go/errors"`, + `"github.com/lbryio/lbry.go/extras/errors"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `"github.com/spf13/viper"`, @@ -315,55 +315,55 @@ func newImporter() importer { // TranslateColumnType to see the type assignments. imp.BasedOnType = mapImports{ "null.Float32": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Float64": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Int": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Int8": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Int16": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Int32": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Int64": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Uint": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Uint8": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Uint16": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Uint32": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Uint64": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.String": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Bool": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Time": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.JSON": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "null.Bytes": { - thirdParty: importList{`"github.com/lbryio/lbry.go/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, }, "time.Time": { standard: importList{`"time"`}, diff --git a/boilingcore/imports_test.go b/boilingcore/imports_test.go index 693bc10..6d22956 100644 --- a/boilingcore/imports_test.go +++ b/boilingcore/imports_test.go @@ -246,7 +246,7 @@ func TestCombineTypeImports(t *testing.T) { }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, - `"github.com/lbryio/lbry.go/null"`, + `"github.com/lbryio/lbry.go/extras/null"`, }, } @@ -281,7 +281,7 @@ func TestCombineTypeImports(t *testing.T) { }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, - `"github.com/lbryio/lbry.go/null"`, + `"github.com/lbryio/lbry.go/extras/null"`, }, } @@ -297,7 +297,7 @@ func TestCombineImports(t *testing.T) { a := imports{ standard: importList{"fmt"}, - thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/lbry.go/null"}, + thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/lbry.go/extras/null"}, } b := imports{ standard: importList{"os"}, @@ -309,8 +309,8 @@ func TestCombineImports(t *testing.T) { if c.standard[0] != "fmt" && c.standard[1] != "os" { t.Errorf("Wanted: fmt, os got: %#v", c.standard) } - if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/lbry.go/null" { - t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/lbry.go/null got: %#v", c.thirdParty) + if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/lbry.go/extras/null" { + t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/lbry.go/extras/null got: %#v", c.thirdParty) } } diff --git a/queries/helpers_test.go b/queries/helpers_test.go index b6b757d..5a1802a 100644 --- a/queries/helpers_test.go +++ b/queries/helpers_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - null "github.com/lbryio/lbry.go/null" + null "github.com/lbryio/lbry.go/extras/null" ) type testObj struct { diff --git a/queries/query.go b/queries/query.go index fdd54b4..14f2d51 100644 --- a/queries/query.go +++ b/queries/query.go @@ -4,7 +4,7 @@ import ( "database/sql" "fmt" - "github.com/lbryio/lbry.go/errors" + "github.com/lbryio/lbry.go/extras/errors" "github.com/lbryio/sqlboiler/boil" ) diff --git a/randomize/randomize.go b/randomize/randomize.go index 54c88bf..65b2a2b 100644 --- a/randomize/randomize.go +++ b/randomize/randomize.go @@ -14,7 +14,7 @@ import ( "sync/atomic" "time" - null "github.com/lbryio/lbry.go/null" + null "github.com/lbryio/lbry.go/extras/null" "github.com/pkg/errors" "github.com/satori/go.uuid" diff --git a/randomize/randomize_test.go b/randomize/randomize_test.go index bc7a382..45ddf16 100644 --- a/randomize/randomize_test.go +++ b/randomize/randomize_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - null "github.com/lbryio/lbry.go/null" + null "github.com/lbryio/lbry.go/extras/null" ) func TestRandomizeStruct(t *testing.T) { From eea3d349a773e4f5bcfb9a194b78674e5b92fdd5 Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr <markbeamerjr@gmail.com> Date: Mon, 3 Sep 2018 00:51:55 -0400 Subject: [PATCH 173/179] added support for 1 to 1 relations and added support for n unique keys for conflict resolution during merging. split out into two functions. --- templates/singleton/boil_queries.tpl | 118 +++++++++++++++++++-------- 1 file changed, 83 insertions(+), 35 deletions(-) diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index 51ed108..a881cda 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -27,11 +27,15 @@ func mergeModels(tx boil.Executor, primaryID uint64, secondaryID uint64, foreign var err error for _, conflict := range conflictingKeys { - err = deleteConflictsBeforeMerge(tx, conflict, primaryID, secondaryID) - if err != nil { - return err - } - } + if len(conflict.columns) == 1 && conflict.columns[0] == conflict.objectIdColumn { + err = deleteOneToOneConflictsBeforeMerge(tx, conflict, primaryID, secondaryID) + } else { + err = deleteOneToManyConflictsBeforeMerge(tx, conflict, primaryID, secondaryID) + } + if err != nil { + return err + } + } for _, fk := range foreignKeys { // TODO: use NewQuery here, not plain sql @@ -48,53 +52,97 @@ func mergeModels(tx boil.Executor, primaryID uint64, secondaryID uint64, foreign return checkMerge(tx, foreignKeys) } -func deleteConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, primaryID uint64, secondaryID uint64) error { - conflictingColumns := strmangle.SetComplement(conflict.columns, []string{conflict.objectIdColumn}) - - if len(conflictingColumns) < 1 { - return nil - } else if len(conflictingColumns) > 1 { - return errors.Err("this doesnt work for unique keys with more than two columns (yet)") - } - +func deleteOneToOneConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, primaryID uint64, secondaryID uint64) error { query := fmt.Sprintf( - "SELECT %s FROM %s WHERE %s IN (%s) GROUP BY %s HAVING count(distinct %s) > 1", - conflictingColumns[0], conflict.table, conflict.objectIdColumn, + "SELECT COUNT(*) FROM %s WHERE %s IN (%s)", + conflict.table, conflict.objectIdColumn, strmangle.Placeholders(dialect.IndexPlaceholders, 2, 1, 1), - conflictingColumns[0], conflict.objectIdColumn, ) - rows, err := tx.Query(query, primaryID, secondaryID) - defer rows.Close() + var count int + err := tx.QueryRow(query, primaryID, secondaryID).Scan(&count) if err != nil { return errors.Err(err) } - args := []interface{}{secondaryID} - for rows.Next() { - var value string - err = rows.Scan(&value) - if err != nil { - return errors.Err(err) - } - args = append(args, value) - } - - // if no rows found, no need to delete anything - if len(args) < 2 { - return nil + if count > 2 { + return errors.Err("it should not be possible to have more than two rows here") + } else if count != 2 { + return nil // no conflicting rows } query = fmt.Sprintf( - "DELETE FROM %s WHERE %s = %s AND %s IN (%s)", + "DELETE FROM %s WHERE %s = %s", conflict.table, conflict.objectIdColumn, strmangle.Placeholders(dialect.IndexPlaceholders, 1, 1, 1), - conflictingColumns[0], strmangle.Placeholders(dialect.IndexPlaceholders, len(args)-1, 2, 1), ) - _, err = tx.Exec(query, args...) + _, err = tx.Exec(query, secondaryID) + return errors.Err(err) +} + +func deleteOneToManyConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, primaryID uint64, secondaryID uint64) error { + conflictingColumns := strmangle.SetComplement(conflict.columns, []string{conflict.objectIdColumn}) + + query := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s IN (%s) GROUP BY %s HAVING count(distinct %s) > 1", + strings.Join(conflictingColumns, ","), conflict.table, conflict.objectIdColumn, + strmangle.Placeholders(dialect.IndexPlaceholders, 2, 1, 1), + strings.Join(conflictingColumns, ","), conflict.objectIdColumn, + ) + + //The selectParams should be the ObjectIDs to search for regarding the conflict. + rows, err := tx.Query(query, primaryID, secondaryID) if err != nil { return errors.Err(err) } + + //Since we don't don't know if advance how many columns the query returns, we have dynamically assign them to be + // used in the delete query. + colNames, err := rows.Columns() + if err != nil { + log.Fatal(err) + } + //Each row result of the query needs to be removed for being a conflicting row. Store each row's keys in an array. + var rowsToRemove = [][]interface{}(nil) + for rows.Next() { + //Set pointers for dynamic scan + iColPtrs := make([]interface{}, len(colNames)) + for i := 0; i < len(colNames); i++ { + s := string("") + iColPtrs[i] = &s + } + //Dynamically scan n columns + err = rows.Scan(iColPtrs...) + if err != nil { + return errors.Err(err) + } + //Grab scanned values for query arguments + iCol := make([]interface{}, len(colNames)) + for i, col := range iColPtrs { + x := col.(*string) + iCol[i] = *x + } + rowsToRemove = append(rowsToRemove, iCol) + } + defer rows.Close() + + //This query will adjust dynamically depending on the number of conflicting keys, adding AND expressions for each + // key to ensure the right conflicting rows are deleted. + query = fmt.Sprintf( + "DELETE FROM %s %s", + conflict.table, + "WHERE "+strings.Join(conflict.columns, " = ? AND ")+" = ?", + ) + + //There could be multiple conflicting rows between ObjectIDs. In the SELECT query we grab each row and their column + // keys to be deleted here in a loop. + for _, rowToDelete := range rowsToRemove { + rowToDelete = append(rowToDelete, secondaryID) + _, err = tx.Exec(query, rowToDelete...) + if err != nil { + return errors.Err(err) + } + } return nil } From 29172e976b0923c864899a362c4aefe974a168d7 Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr <markbeamerjr@gmail.com> Date: Fri, 7 Sep 2018 20:51:50 -0400 Subject: [PATCH 174/179] changed from Fatal to Error - copy paste mistake. --- templates/singleton/boil_queries.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index a881cda..3e9ebb8 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -100,7 +100,7 @@ func deleteOneToManyConflictsBeforeMerge(tx boil.Executor, conflict conflictingU // used in the delete query. colNames, err := rows.Columns() if err != nil { - log.Fatal(err) + return errors.Err(err) } //Each row result of the query needs to be removed for being a conflicting row. Store each row's keys in an array. var rowsToRemove = [][]interface{}(nil) From f892107dad46b36f3487ffe336d53064c8a053d8 Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr <markbeamerjr@gmail.com> Date: Thu, 7 Feb 2019 20:24:07 -0500 Subject: [PATCH 175/179] missing import --- boilingcore/imports.go | 1 + 1 file changed, 1 insertion(+) diff --git a/boilingcore/imports.go b/boilingcore/imports.go index b5979e3..d37c84b 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -183,6 +183,7 @@ func newImporter() importer { "boil_queries": imports{ standard: importList{ `"fmt"`, + `"strings"`, }, thirdParty: importList{ `"github.com/lbryio/lbry.go/extras/errors"`, From 4e1b83ab3942cc6747e04065aef330b0d9dff56d Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr <markbeamerjr@gmail.com> Date: Sat, 29 Jun 2019 11:30:23 -0400 Subject: [PATCH 176/179] Add force index query mod for select query. Fix eager loading casting for nested levels. --- queries/eager_load.go | 14 ++++++++++++-- queries/qm/query_mods.go | 7 +++++++ queries/query.go | 6 ++++++ queries/query_builders.go | 8 +++++++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/queries/eager_load.go b/queries/eager_load.go index 081aac4..962d722 100644 --- a/queries/eager_load.go +++ b/queries/eager_load.go @@ -206,9 +206,16 @@ func (l loadRelationshipState) loadRelationshipsRecurse(depth int, obj reflect.V } bkind := kindStruct - if reflect.Indirect(loadedObject).Kind() != reflect.Struct { + if derefed := reflect.Indirect(loadedObject); derefed.Kind() != reflect.Struct { bkind = kindPtrSliceStruct - loadedObject = loadedObject.Addr() + + // Convert away any helper slice types + // elemType is *elem (from []*elem or helperSliceType) + // sliceType is *[]*elem + elemType := derefed.Type().Elem() + sliceType := reflect.PtrTo(reflect.SliceOf(elemType)) + + loadedObject = loadedObject.Addr().Convert(sliceType) } return l.loadRelationships(depth+1, loadedObject.Interface(), bkind) } @@ -241,6 +248,9 @@ func collectLoaded(key string, loadingFrom reflect.Value) (reflect.Value, bindKi if loadedType.Elem().Kind() == reflect.Struct { bkind = kindStruct loadedType = reflect.SliceOf(loadedType) + } else { + // Ensure that we get rid of all the helper "XSlice" types + loadedType = reflect.SliceOf(loadedType.Elem()) } collection := reflect.MakeSlice(loadedType, 0, 0) diff --git a/queries/qm/query_mods.go b/queries/qm/query_mods.go index 89d31d4..6fa6e2f 100644 --- a/queries/qm/query_mods.go +++ b/queries/qm/query_mods.go @@ -123,6 +123,13 @@ func From(from string) QueryMod { } } +func ForceIndex( index string) QueryMod { + return func(q *queries.Query) { + queries.SetForceIndex(q, index) + } +} + + // Limit the number of returned rows func Limit(limit int) QueryMod { return func(q *queries.Query) { diff --git a/queries/query.go b/queries/query.go index 14f2d51..5b0ffbc 100644 --- a/queries/query.go +++ b/queries/query.go @@ -30,6 +30,7 @@ type Query struct { selectCols []string count bool from []string + forceindex string joins []join where []where in []in @@ -263,6 +264,11 @@ func SetLastWhereAsOr(q *Query) { q.where[len(q.where)-1].orSeparator = true } +// SetForceIndex sets the index to be used by the query +func SetForceIndex(q *Query, index string){ + q.forceindex = index +} + // SetLastInAsOr sets the or separator for the tail "IN" in the slice func SetLastInAsOr(q *Query) { if len(q.in) == 0 { diff --git a/queries/query_builders.go b/queries/query_builders.go index f884f91..e682877 100644 --- a/queries/query_builders.go +++ b/queries/query_builders.go @@ -76,7 +76,13 @@ func buildSelectQuery(q *Query) (*bytes.Buffer, []interface{}) { buf.WriteByte(')') } - fmt.Fprintf(buf, " FROM %s", strings.Join(strmangle.IdentQuoteSlice(q.dialect.LQ, q.dialect.RQ, q.from), ", ")) + if len(q.forceindex) > 0 { + fmt.Fprintf(buf, " FROM %s FORCE INDEX (%s)", strings.Join(strmangle.IdentQuoteSlice(q.dialect.LQ, q.dialect.RQ, q.from), ", "),q.forceindex) + + }else{ + fmt.Fprintf(buf, " FROM %s", strings.Join(strmangle.IdentQuoteSlice(q.dialect.LQ, q.dialect.RQ, q.from), ", ")) + + } if len(q.joins) > 0 { argsLen := len(args) From c01b1828391ba95ef3366817aad739e264f46441 Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr <markbeamerjr@gmail.com> Date: Mon, 1 Jul 2019 23:46:37 -0400 Subject: [PATCH 177/179] Allow null query mods for dynamic queries --- queries/qm/query_mods.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/queries/qm/query_mods.go b/queries/qm/query_mods.go index 6fa6e2f..42e881e 100644 --- a/queries/qm/query_mods.go +++ b/queries/qm/query_mods.go @@ -8,7 +8,9 @@ type QueryMod func(q *queries.Query) // Apply the query mods to the Query object func Apply(q *queries.Query, mods ...QueryMod) { for _, mod := range mods { - mod(q) + if mod != nil { + mod(q) + } } } @@ -123,13 +125,12 @@ func From(from string) QueryMod { } } -func ForceIndex( index string) QueryMod { +func ForceIndex(index string) QueryMod { return func(q *queries.Query) { queries.SetForceIndex(q, index) } } - // Limit the number of returned rows func Limit(limit int) QueryMod { return func(q *queries.Query) { From 256a6d4225b70fd17caba555c5b8f035e33db8c9 Mon Sep 17 00:00:00 2001 From: Niko Storni <niko@lbry.io> Date: Mon, 10 Feb 2020 14:49:11 +0100 Subject: [PATCH 178/179] update lbry.go library --- boilingcore/imports.go | 50 ++++++++++++++++++------------------- boilingcore/imports_test.go | 10 ++++---- queries/helpers_test.go | 2 +- queries/query.go | 2 +- randomize/randomize.go | 2 +- randomize/randomize_test.go | 2 +- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/boilingcore/imports.go b/boilingcore/imports.go index d37c84b..e20480b 100644 --- a/boilingcore/imports.go +++ b/boilingcore/imports.go @@ -170,8 +170,8 @@ func newImporter() importer { `"time"`, }, thirdParty: importList{ - `"github.com/lbryio/lbry.go/extras/errors"`, - `"github.com/lbryio/lbry.go/extras/null"`, + `"github.com/lbryio/lbry.go/v2/extras/errors"`, + `"github.com/lbryio/lbry.go/v2/extras/null"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, @@ -186,7 +186,7 @@ func newImporter() importer { `"strings"`, }, thirdParty: importList{ - `"github.com/lbryio/lbry.go/extras/errors"`, + `"github.com/lbryio/lbry.go/v2/extras/errors"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries/qm"`, @@ -195,7 +195,7 @@ func newImporter() importer { }, "boil_types": { thirdParty: importList{ - `"github.com/lbryio/lbry.go/extras/errors"`, + `"github.com/lbryio/lbry.go/v2/extras/errors"`, `"github.com/lbryio/sqlboiler/strmangle"`, }, }, @@ -228,7 +228,7 @@ func newImporter() importer { }, thirdParty: importList{ `"github.com/kat-co/vala"`, - `"github.com/lbryio/lbry.go/extras/errors"`, + `"github.com/lbryio/lbry.go/v2/extras/errors"`, `"github.com/lbryio/sqlboiler/boil"`, `"github.com/spf13/viper"`, }, @@ -266,7 +266,7 @@ func newImporter() importer { `"strings"`, }, thirdParty: importList{ - `"github.com/lbryio/lbry.go/extras/errors"`, + `"github.com/lbryio/lbry.go/v2/extras/errors"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `_ "github.com/lib/pq"`, @@ -286,7 +286,7 @@ func newImporter() importer { }, thirdParty: importList{ `_ "github.com/go-sql-driver/mysql"`, - `"github.com/lbryio/lbry.go/extras/errors"`, + `"github.com/lbryio/lbry.go/v2/extras/errors"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `"github.com/spf13/viper"`, @@ -303,7 +303,7 @@ func newImporter() importer { }, thirdParty: importList{ `_ "github.com/denisenkom/go-mssqldb"`, - `"github.com/lbryio/lbry.go/extras/errors"`, + `"github.com/lbryio/lbry.go/v2/extras/errors"`, `"github.com/lbryio/sqlboiler/bdb/drivers"`, `"github.com/lbryio/sqlboiler/randomize"`, `"github.com/spf13/viper"`, @@ -316,55 +316,55 @@ func newImporter() importer { // TranslateColumnType to see the type assignments. imp.BasedOnType = mapImports{ "null.Float32": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Float64": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Int": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Int8": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Int16": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Int32": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Int64": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Uint": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Uint8": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Uint16": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Uint32": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Uint64": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.String": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Bool": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Time": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.JSON": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "null.Bytes": { - thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`}, + thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`}, }, "time.Time": { standard: importList{`"time"`}, diff --git a/boilingcore/imports_test.go b/boilingcore/imports_test.go index 6d22956..d0043c0 100644 --- a/boilingcore/imports_test.go +++ b/boilingcore/imports_test.go @@ -246,7 +246,7 @@ func TestCombineTypeImports(t *testing.T) { }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, - `"github.com/lbryio/lbry.go/extras/null"`, + `"github.com/lbryio/lbry.go/v2/extras/null"`, }, } @@ -281,7 +281,7 @@ func TestCombineTypeImports(t *testing.T) { }, thirdParty: importList{ `"github.com/lbryio/sqlboiler/boil"`, - `"github.com/lbryio/lbry.go/extras/null"`, + `"github.com/lbryio/lbry.go/v2/extras/null"`, }, } @@ -297,7 +297,7 @@ func TestCombineImports(t *testing.T) { a := imports{ standard: importList{"fmt"}, - thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/lbry.go/extras/null"}, + thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/lbry.go/v2/extras/null"}, } b := imports{ standard: importList{"os"}, @@ -309,8 +309,8 @@ func TestCombineImports(t *testing.T) { if c.standard[0] != "fmt" && c.standard[1] != "os" { t.Errorf("Wanted: fmt, os got: %#v", c.standard) } - if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/lbry.go/extras/null" { - t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/lbry.go/extras/null got: %#v", c.thirdParty) + if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/lbry.go/v2/extras/null" { + t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/lbry.go/v2/extras/null got: %#v", c.thirdParty) } } diff --git a/queries/helpers_test.go b/queries/helpers_test.go index 5a1802a..fd093e3 100644 --- a/queries/helpers_test.go +++ b/queries/helpers_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - null "github.com/lbryio/lbry.go/extras/null" + null "github.com/lbryio/lbry.go/v2/extras/null" ) type testObj struct { diff --git a/queries/query.go b/queries/query.go index 5b0ffbc..e3a6470 100644 --- a/queries/query.go +++ b/queries/query.go @@ -4,7 +4,7 @@ import ( "database/sql" "fmt" - "github.com/lbryio/lbry.go/extras/errors" + "github.com/lbryio/lbry.go/v2/extras/errors" "github.com/lbryio/sqlboiler/boil" ) diff --git a/randomize/randomize.go b/randomize/randomize.go index 65b2a2b..30592bf 100644 --- a/randomize/randomize.go +++ b/randomize/randomize.go @@ -14,7 +14,7 @@ import ( "sync/atomic" "time" - null "github.com/lbryio/lbry.go/extras/null" + null "github.com/lbryio/lbry.go/v2/extras/null" "github.com/pkg/errors" "github.com/satori/go.uuid" diff --git a/randomize/randomize_test.go b/randomize/randomize_test.go index 45ddf16..71afb55 100644 --- a/randomize/randomize_test.go +++ b/randomize/randomize_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - null "github.com/lbryio/lbry.go/extras/null" + null "github.com/lbryio/lbry.go/v2/extras/null" ) func TestRandomizeStruct(t *testing.T) { From 3db4f30f564e65e40f5467f76692501c21489a46 Mon Sep 17 00:00:00 2001 From: Mark Beamer Jr <markbeamerjr@gmail.com> Date: Wed, 19 Aug 2020 23:43:39 -0400 Subject: [PATCH 179/179] Fix multi-depth eager loading of relationships. If a relationship is nil, do not add it to the collection for checking the next depth level. --- queries/eager_load.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/queries/eager_load.go b/queries/eager_load.go index 962d722..8992290 100644 --- a/queries/eager_load.go +++ b/queries/eager_load.go @@ -5,9 +5,9 @@ import ( "reflect" "strings" - "github.com/pkg/errors" "github.com/lbryio/sqlboiler/boil" "github.com/lbryio/sqlboiler/strmangle" + "github.com/pkg/errors" ) type loadRelationshipState struct { @@ -259,9 +259,13 @@ func collectLoaded(key string, loadingFrom reflect.Value) (reflect.Value, bindKi for { switch bkind { case kindStruct: - collection = reflect.Append(collection, loadedObject) + if !loadedObject.IsNil() { + collection = reflect.Append(collection, loadedObject) + } case kindPtrSliceStruct: - collection = reflect.AppendSlice(collection, loadedObject) + if !loadedObject.IsNil() { + collection = reflect.AppendSlice(collection, loadedObject) + } } i++