Merge branch 'dev'
This commit is contained in:
commit
632b89fae0
43 changed files with 1563 additions and 91 deletions
208
.circleci/config.yml
Normal file
208
.circleci/config.yml
Normal 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
|
13
README.md
13
README.md
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
374
bdb/drivers/mssql.go
Normal 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
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -225,6 +225,7 @@ var templateFunctions = template.FuncMap{
|
|||
"txtsFromToMany": txtsFromToMany,
|
||||
|
||||
// dbdrivers ops
|
||||
"filterColumnsByAuto": bdb.FilterColumnsByAuto,
|
||||
"filterColumnsByDefault": bdb.FilterColumnsByDefault,
|
||||
"filterColumnsByEnum": bdb.FilterColumnsByEnum,
|
||||
"sqlColDefinitions": bdb.SQLColDefinitions,
|
||||
|
|
28
circle.yml
28
circle.yml
|
@ -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
48
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")
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 ", "}}{{"}"}}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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...)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
131
templates_test/main_test/mssql_main.tpl
Normal file
131
templates_test/main_test/mssql_main.tpl
Normal 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
|
||||
}
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
35
testdata/Dockerfile
vendored
Normal 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
439
testdata/mssql_test_schema.sql
vendored
Normal 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
|
Loading…
Reference in a new issue