2014-04-25 03:20:23 +04:00
package gorm
2013-11-14 14:59:11 +04:00
import (
"fmt"
2014-12-08 14:03:42 +03:00
"reflect"
2015-10-28 18:05:09 +03:00
"strings"
2015-02-17 17:55:14 +03:00
"time"
2013-11-14 14:59:11 +04:00
)
type postgres struct {
2015-03-17 05:40:42 +03:00
commonDialect
2013-11-14 14:59:11 +04:00
}
2016-02-14 13:06:42 +03:00
func init ( ) {
RegisterDialect ( "postgres" , & postgres { } )
2017-09-04 17:39:19 +03:00
RegisterDialect ( "cloudsqlpostgres" , & postgres { } )
2016-02-14 13:06:42 +03:00
}
2016-03-05 16:24:54 +03:00
func ( postgres ) GetName ( ) string {
return "postgres"
}
2016-01-19 06:53:53 +03:00
func ( postgres ) BindVar ( i int ) string {
2013-11-16 16:47:25 +04:00
return fmt . Sprintf ( "$%v" , i )
2013-11-14 14:59:11 +04:00
}
2017-01-15 16:24:53 +03:00
func ( s * postgres ) DataTypeOf ( field * StructField ) string {
var dataValue , sqlType , size , additionalType = ParseFieldStructForDialect ( field , s )
2016-02-13 18:51:36 +03:00
2016-02-14 08:51:34 +03:00
if sqlType == "" {
switch dataValue . Kind ( ) {
case reflect . Bool :
sqlType = "boolean"
2017-07-23 11:05:43 +03:00
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uintptr :
2016-02-14 08:51:34 +03:00
if _ , ok := field . TagSettings [ "AUTO_INCREMENT" ] ; ok || field . IsPrimaryKey {
2016-05-04 05:37:31 +03:00
field . TagSettings [ "AUTO_INCREMENT" ] = "AUTO_INCREMENT"
2016-02-14 08:51:34 +03:00
sqlType = "serial"
} else {
sqlType = "integer"
}
2017-07-23 11:05:43 +03:00
case reflect . Int64 , reflect . Uint32 , reflect . Uint64 :
2016-02-14 08:51:34 +03:00
if _ , ok := field . TagSettings [ "AUTO_INCREMENT" ] ; ok || field . IsPrimaryKey {
2016-05-04 05:37:31 +03:00
field . TagSettings [ "AUTO_INCREMENT" ] = "AUTO_INCREMENT"
2016-02-14 08:51:34 +03:00
sqlType = "bigserial"
} else {
sqlType = "bigint"
}
case reflect . Float32 , reflect . Float64 :
sqlType = "numeric"
case reflect . String :
2016-03-16 18:06:15 +03:00
if _ , ok := field . TagSettings [ "SIZE" ] ; ! ok {
size = 0 // if SIZE haven't been set, use `text` as the default type, as there are no performance different
}
2016-02-14 08:51:34 +03:00
if size > 0 && size < 65532 {
sqlType = fmt . Sprintf ( "varchar(%d)" , size )
} else {
sqlType = "text"
}
case reflect . Struct :
if _ , ok := dataValue . Interface ( ) . ( time . Time ) ; ok {
sqlType = "timestamp with time zone"
}
case reflect . Map :
2016-03-05 13:54:59 +03:00
if dataValue . Type ( ) . Name ( ) == "Hstore" {
2016-02-14 08:51:34 +03:00
sqlType = "hstore"
}
default :
2017-04-19 10:21:56 +03:00
if IsByteArrayOrSlice ( dataValue ) {
2016-02-14 08:51:34 +03:00
sqlType = "bytea"
2017-09-04 17:23:42 +03:00
if isUUID ( dataValue ) {
sqlType = "uuid"
}
2016-02-14 08:51:34 +03:00
}
}
2016-01-19 15:58:38 +03:00
}
2016-02-14 08:51:34 +03:00
if sqlType == "" {
panic ( fmt . Sprintf ( "invalid sql type %s (%s) for postgres" , dataValue . Type ( ) . Name ( ) , dataValue . Kind ( ) . String ( ) ) )
}
if strings . TrimSpace ( additionalType ) == "" {
return sqlType
2013-11-14 14:59:11 +04:00
}
2016-02-14 08:51:34 +03:00
return fmt . Sprintf ( "%v %v" , sqlType , additionalType )
2013-11-14 14:59:11 +04:00
}
2016-02-15 09:09:24 +03:00
func ( s postgres ) HasIndex ( tableName string , indexName string ) bool {
2016-01-18 15:32:52 +03:00
var count int
2017-09-04 17:22:02 +03:00
s . db . QueryRow ( "SELECT count(*) FROM pg_indexes WHERE tablename = $1 AND indexname = $2 AND schemaname = CURRENT_SCHEMA()" , tableName , indexName ) . Scan ( & count )
2016-01-18 15:32:52 +03:00
return count > 0
2015-10-28 18:05:09 +03:00
}
2016-03-05 17:50:49 +03:00
func ( s postgres ) HasForeignKey ( tableName string , foreignKeyName string ) bool {
var count int
2016-04-11 16:18:39 +03:00
s . db . QueryRow ( "SELECT count(con.conname) FROM pg_constraint con WHERE $1::regclass::oid = con.conrelid AND con.conname = $2 AND con.contype='f'" , tableName , foreignKeyName ) . Scan ( & count )
2016-03-05 17:50:49 +03:00
return count > 0
}
2016-02-15 09:09:24 +03:00
func ( s postgres ) HasTable ( tableName string ) bool {
2014-04-25 05:08:48 +04:00
var count int
2017-09-04 17:22:02 +03:00
s . db . QueryRow ( "SELECT count(*) FROM INFORMATION_SCHEMA.tables WHERE table_name = $1 AND table_type = 'BASE TABLE' AND table_schema = CURRENT_SCHEMA()" , tableName ) . Scan ( & count )
2014-04-25 05:08:48 +04:00
return count > 0
}
2016-02-15 09:09:24 +03:00
func ( s postgres ) HasColumn ( tableName string , columnName string ) bool {
2014-04-25 05:08:48 +04:00
var count int
2017-09-04 17:22:02 +03:00
s . db . QueryRow ( "SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_name = $1 AND column_name = $2 AND table_schema = CURRENT_SCHEMA()" , tableName , columnName ) . Scan ( & count )
2014-04-25 05:08:48 +04:00
return count > 0
}
2014-06-08 03:15:58 +04:00
2016-07-11 16:37:44 +03:00
func ( s postgres ) CurrentDatabase ( ) ( name string ) {
2016-02-15 09:09:24 +03:00
s . db . QueryRow ( "SELECT CURRENT_DATABASE()" ) . Scan ( & name )
2016-01-18 15:32:52 +03:00
return
2014-07-29 08:02:03 +04:00
}
2016-03-07 09:54:20 +03:00
func ( s postgres ) LastInsertIDReturningSuffix ( tableName , key string ) string {
2016-01-18 15:32:52 +03:00
return fmt . Sprintf ( "RETURNING %v.%v" , tableName , key )
2015-03-02 18:02:40 +03:00
}
2016-03-07 09:54:20 +03:00
func ( postgres ) SupportLastInsertID ( ) bool {
2016-01-18 15:32:52 +03:00
return false
2015-08-06 22:37:26 +03:00
}
2016-01-18 15:32:52 +03:00
func isUUID ( value reflect . Value ) bool {
if value . Kind ( ) != reflect . Array || value . Type ( ) . Len ( ) != 16 {
return false
}
typename := value . Type ( ) . Name ( )
lower := strings . ToLower ( typename )
return "uuid" == lower || "guid" == lower
}