Merge branch 'dev'

This commit is contained in:
Aaron L 2017-05-01 18:41:02 -07:00
commit 632b89fae0
43 changed files with 1563 additions and 91 deletions

208
.circleci/config.yml Normal file
View file

@ -0,0 +1,208 @@
version: 2
jobs:
build:
working_directory: /root
docker:
- image: aarondl0/sqlboiler-test:latest
- image: postgres:9.6
environment:
POSTGRES_PASSWORD: psqlpassword
- 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 -t
cd $ROOTPATH; go build -v github.com/vattle/sqlboiler
- run:
name: 'Configure SQLBoiler: PSQL'
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"\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"\npass="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: 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
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: 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

View file

@ -107,9 +107,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).
@ -274,6 +277,13 @@ schema="myschema"
user="dbusername"
pass="dbpassword"
sslmode="false"
[mssql]
dbname="dbname"
host="localhost"
port=1433
user="dbusername"
pass="dbpassword"
sslmode="disable"
```
#### Initial Generation
@ -320,6 +330,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.

View file

@ -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.
@ -52,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

View file

@ -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 }

374
bdb/drivers/mssql.go Normal file
View file

@ -0,0 +1,374 @@
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 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
}
// 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.
func (m *MSSQLDriver) TableNames(schema string, whitelist, blacklist []string) ([]string, error) {
var names []string
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:])
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,
COLUMNPROPERTY(object_id($1 + '.' + $2), c.column_name, 'IsIdentity') as is_identity
FROM information_schema.columns c
WHERE table_schema = $1 AND table_name = $2;
`, schema, tableName)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var colName, colType, colFullType string
var nullable, unique, identity, auto bool
var defaultValue *string
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 = strings.EqualFold(colType, "timestamp") || strings.EqualFold(colType, "rowversion")
column := bdb.Column{
Name: colName,
FullDBType: colFullType,
DBType: colType,
Nullable: nullable,
Unique: unique,
AutoGenerated: auto,
}
if defaultValue != nil && *defaultValue != "NULL" {
column.Default = *defaultValue
} else if identity || auto {
column.Default = "auto"
}
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 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 {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
queryColumns := `
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 {
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 ,
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
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":
c.Type = "null.Int8"
case "smallint":
c.Type = "null.Int16"
case "mediumint":
c.Type = "null.Int32"
case "int":
c.Type = "null.Int"
case "bigint":
c.Type = "null.Int64"
case "real":
c.Type = "null.Float32"
case "float":
c.Type = "null.Float64"
case "boolean", "bool", "bit":
c.Type = "null.Bool"
case "date", "datetime", "datetime2", "smalldatetime", "time":
c.Type = "null.Time"
case "binary", "varbinary":
c.Type = "null.Bytes"
case "timestamp", "rowversion":
c.Type = "null.Bytes"
case "xml":
c.Type = "null.String"
case "uniqueidentifier":
c.Type = "null.String"
c.DBType = "uuid"
default:
c.Type = "null.String"
}
} else {
switch c.DBType {
case "tinyint":
c.Type = "int8"
case "smallint":
c.Type = "int16"
case "mediumint":
c.Type = "int32"
case "int":
c.Type = "int"
case "bigint":
c.Type = "int64"
case "real":
c.Type = "float32"
case "float":
c.Type = "float64"
case "boolean", "bool", "bit":
c.Type = "bool"
case "date", "datetime", "datetime2", "smalldatetime", "time":
c.Type = "time.Time"
case "binary", "varbinary":
c.Type = "[]byte"
case "timestamp", "rowversion":
c.Type = "[]byte"
case "xml":
c.Type = "string"
case "uniqueidentifier":
c.Type = "string"
c.DBType = "uuid"
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 true to indicate MS SQL supports indexed placeholders
func (m *MSSQLDriver) IndexPlaceholders() bool {
return true
}

View file

@ -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.

View file

@ -3,6 +3,7 @@ package drivers
import (
"database/sql"
"fmt"
"os"
"strings"
// Side-effect import sql driver
@ -78,6 +79,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.
@ -127,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, ''',''') || ''')'
@ -171,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))
@ -344,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"

View file

@ -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

View file

@ -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() {}

View file

@ -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) {

View file

@ -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{}
}
@ -316,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
}

View file

@ -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
}

View file

@ -285,6 +285,23 @@ func newImporter() importer {
`_ "github.com/go-sql-driver/mysql"`,
},
},
"mssql": {
standard: importList{
`"bytes"`,
`"database/sql"`,
`"fmt"`,
`"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

View file

@ -225,6 +225,7 @@ var templateFunctions = template.FuncMap{
"txtsFromToMany": txtsFromToMany,
// dbdrivers ops
"filterColumnsByAuto": bdb.FilterColumnsByAuto,
"filterColumnsByDefault": bdb.FilterColumnsByDefault,
"filterColumnsByEnum": bdb.FilterColumnsByEnum,
"sqlColDefinitions": bdb.SQLColDefinitions,

View file

@ -1,28 +0,0 @@
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
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
dependencies:
cache_directories:
- ~/download

48
main.go
View file

@ -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")
@ -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()
@ -200,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"),
@ -255,6 +261,46 @@ 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 = 1433
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"),
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
}

View file

@ -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 {

View file

@ -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(")
}
@ -277,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)
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(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, ", "))
@ -308,12 +357,37 @@ func writeModifiers(q *Query, buf *bytes.Buffer, args *[]interface{}) {
buf.WriteString(strings.Join(q.orderBy, ", "))
}
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 {
// 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 {
fmt.Fprintf(buf, " FETCH NEXT %d ROWS ONLY", q.limit)
}
}
}
if len(q.forlock) != 0 {
fmt.Fprintf(buf, " FOR %s", q.forlock)

View file

@ -82,10 +82,11 @@ 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" {
if (driver == "postgres" && schema != "public") || driver == "mssql" {
return fmt.Sprintf(`%s%s%s.%s%s%s`, lq, schema, rq, lq, table, rq)
}
@ -520,6 +521,30 @@ 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 (")
}
startIndex = 0
if start > 0 {
startIndex = start + i*len(cols)
}
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)

View file

@ -4,6 +4,9 @@
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
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 -}}
{{$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 ", "}}{{"}"}}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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}}) %%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 ()"
@ -75,12 +75,22 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string
{{end -}}
}
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, "{{.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}}"))
{{if ne .DriverName "mssql" -}}
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 -}}
}
if len(wl) != 0 {
cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning)
}
}

View file

@ -48,8 +48,15 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string
{{$varNameSingular}}UpdateCacheMut.RUnlock()
if !cached {
wl := strmangle.UpdateColumnSet({{$varNameSingular}}Columns, {{$varNameSingular}}PrimaryKeyColumns, whitelist)
{{- if not .NoAutoTimestamps}}
wl := strmangle.UpdateColumnSet(
{{$varNameSingular}}Columns,
{{$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"})
}
@ -158,11 +165,9 @@ func (o {{$tableNameSingular}}Slice) UpdateAll(exec boil.Executor, cols M) error
args = append(args, pkeyArgs...)
}
sql := fmt.Sprintf(
"UPDATE {{$schemaTable}} SET %s WHERE ({{.LQ}}{{.Table.PKey.Columns | join (printf "%s,%s" .LQ .RQ)}}{{.RQ}}) IN (%s)",
sql := fmt.Sprintf("UPDATE {{$schemaTable}} SET %s WHERE %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)),
)
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)

View file

@ -2,27 +2,27 @@
{{- $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")
}
@ -39,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 {
@ -72,39 +72,62 @@ 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("{{.PkgName}}: 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)
{{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), ","),
)
{{else 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)
cache.valueMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, {{if eq .DriverName "mssql"}}whitelist{{else}}insert{{end}})
if err != nil {
return err
}

View file

@ -135,11 +135,8 @@ func (o {{$tableNameSingular}}Slice) DeleteAll(exec boil.Executor) error {
args = append(args, pkeyArgs...)
}
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)),
)
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)

View file

@ -79,11 +79,8 @@ func (o *{{$tableNameSingular}}Slice) ReloadAll(exec boil.Executor) error {
args = append(args, pkeyArgs...)
}
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)),
)
sql := "SELECT {{$schemaTable}}.* FROM {{$schemaTable}} WHERE " +
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...)

View file

@ -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)

View file

@ -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

View file

@ -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)
}

View file

@ -41,7 +41,7 @@ func test{{$tableNamePlural}}InsertWhitelist(t *testing.T) {
tx := MustTx(boil.Begin())
defer tx.Rollback()
if err = {{$varNameSingular}}.Insert(tx, {{$varNameSingular}}Columns...); err != nil {
if err = {{$varNameSingular}}.Insert(tx, {{$varNameSingular}}ColumnsWithoutDefault...); err != nil {
t.Error(err)
}

View file

@ -0,0 +1,131 @@
type mssqlTester struct {
dbConn *sql.DB
dbName string
host string
user string
pass string
sslmode string
port int
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.dropTestDB(); err != nil {
return err
}
if err = m.createTestDB(); err != nil {
return 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")
}
defer f.Close()
createCmd.Stdin = newFKeyDestroyer(rgxMSSQLkey, f)
if err = createCmd.Start(); err != nil {
return errors.Wrap(err, "failed to start sqlcmd command")
}
if err = createCmd.Wait(); err != nil {
fmt.Println(err)
return errors.Wrap(err, "failed to wait for sqlcmd 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) 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)
}
func (m *mssqlTester) dropTestDB() error {
// 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 {
if m.dbConn != nil {
m.dbConn.Close()
}
if err := m.dropTestDB(); err != nil {
return err
}
return nil
}
func (m *mssqlTester) runCmd(stdin, command string, args ...string) error {
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
}

View file

@ -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()

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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")
}

View file

@ -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 .*ADD\s+CONSTRAINT .* FOREIGN KEY.*?.*\n?REFERENCES.*`)
func newFKeyDestroyer(regex *regexp.Regexp, reader io.Reader) io.Reader {
return &fKeyDestroyer{

View file

@ -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)
}
@ -82,6 +82,12 @@ func test{{$tableNamePlural}}SliceUpdateAll(t *testing.T) {
{{$varNameSingular}}Columns,
{{$varNameSingular}}PrimaryKeyColumns,
)
{{- if eq .DriverName "mssql"}}
fields = strmangle.SetComplement(
fields,
{{$varNameSingular}}ColumnsWithAuto,
)
{{- end}}
}
value := reflect.Indirect(reflect.ValueOf({{$varNameSingular}}))

35
testdata/Dockerfile vendored Normal file
View file

@ -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

439
testdata/mssql_test_schema.sql vendored Normal file
View file

@ -0,0 +1,439 @@
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'
);
GO
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
);
GO
create table owner
(
id int NOT NULL IDENTITY (1,1) PRIMARY KEY,
name varchar(255) not null
);
GO
create table cats
(
id int NOT NULL IDENTITY (1,1) PRIMARY KEY,
name varchar(255) not null,
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,
name varchar(255) not null
);
GO
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)
);
GO
create table dog_toys
(
dog_id int not null,
toy_id int not null,
primary key (dog_id, toy_id)
);
GO
create table dragon_toys
(
dragon_id varchar(100),
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
(
id binary primary key,
name binary not null,
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
);
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
);
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
);
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,
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
(
id binary primary key not null,
name varchar(255),
byte_pilot_id binary unique NOT NULL,
byte_airport_id binary NOT NULL
);
GO
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_jets_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
);
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_languages_byte_pilot_id_fkey FOREIGN KEY (byte_pilot_id) REFERENCES byte_pilots(id);
GO
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
(
id integer not null,
name VARCHAR(MAX),
primary key (id)
);
GO
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)
);
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)
);
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,
race_date datetime,
track VARCHAR(MAX)
);
GO
CREATE TABLE race_results
(
id integer PRIMARY KEY NOT NULL,
race_id integer,
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
);
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,
name VARCHAR(MAX) NOT NULL
);
GO
ALTER TABLE pilots ADD CONSTRAINT pilot_pkey PRIMARY KEY (id);
GO
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
);
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
(
pilot_id integer NOT NULL,
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
(
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)
);
GO