gitea/vendor/github.com/go-xorm/xorm/dialect_postgres.go
Lunny Xiao 005900baea Use created & updated instead BeforeInsert & BeforeUpdate (#2482)
* use created & updated instead BeforeInsert & BeforeUpdate

* fix vendor checksum

* only show generated SQL when development mode

* remove extra update column updated_unix

* remove trace config
2017-09-13 08:18:22 +03:00

1209 lines
40 KiB
Go

// Copyright 2015 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xorm
import (
"errors"
"fmt"
"net/url"
"sort"
"strconv"
"strings"
"github.com/go-xorm/core"
)
// from http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html
var (
postgresReservedWords = map[string]bool{
"A": true,
"ABORT": true,
"ABS": true,
"ABSENT": true,
"ABSOLUTE": true,
"ACCESS": true,
"ACCORDING": true,
"ACTION": true,
"ADA": true,
"ADD": true,
"ADMIN": true,
"AFTER": true,
"AGGREGATE": true,
"ALL": true,
"ALLOCATE": true,
"ALSO": true,
"ALTER": true,
"ALWAYS": true,
"ANALYSE": true,
"ANALYZE": true,
"AND": true,
"ANY": true,
"ARE": true,
"ARRAY": true,
"ARRAY_AGG": true,
"ARRAY_MAX_CARDINALITY": true,
"AS": true,
"ASC": true,
"ASENSITIVE": true,
"ASSERTION": true,
"ASSIGNMENT": true,
"ASYMMETRIC": true,
"AT": true,
"ATOMIC": true,
"ATTRIBUTE": true,
"ATTRIBUTES": true,
"AUTHORIZATION": true,
"AVG": true,
"BACKWARD": true,
"BASE64": true,
"BEFORE": true,
"BEGIN": true,
"BEGIN_FRAME": true,
"BEGIN_PARTITION": true,
"BERNOULLI": true,
"BETWEEN": true,
"BIGINT": true,
"BINARY": true,
"BIT": true,
"BIT_LENGTH": true,
"BLOB": true,
"BLOCKED": true,
"BOM": true,
"BOOLEAN": true,
"BOTH": true,
"BREADTH": true,
"BY": true,
"C": true,
"CACHE": true,
"CALL": true,
"CALLED": true,
"CARDINALITY": true,
"CASCADE": true,
"CASCADED": true,
"CASE": true,
"CAST": true,
"CATALOG": true,
"CATALOG_NAME": true,
"CEIL": true,
"CEILING": true,
"CHAIN": true,
"CHAR": true,
"CHARACTER": true,
"CHARACTERISTICS": true,
"CHARACTERS": true,
"CHARACTER_LENGTH": true,
"CHARACTER_SET_CATALOG": true,
"CHARACTER_SET_NAME": true,
"CHARACTER_SET_SCHEMA": true,
"CHAR_LENGTH": true,
"CHECK": true,
"CHECKPOINT": true,
"CLASS": true,
"CLASS_ORIGIN": true,
"CLOB": true,
"CLOSE": true,
"CLUSTER": true,
"COALESCE": true,
"COBOL": true,
"COLLATE": true,
"COLLATION": true,
"COLLATION_CATALOG": true,
"COLLATION_NAME": true,
"COLLATION_SCHEMA": true,
"COLLECT": true,
"COLUMN": true,
"COLUMNS": true,
"COLUMN_NAME": true,
"COMMAND_FUNCTION": true,
"COMMAND_FUNCTION_CODE": true,
"COMMENT": true,
"COMMENTS": true,
"COMMIT": true,
"COMMITTED": true,
"CONCURRENTLY": true,
"CONDITION": true,
"CONDITION_NUMBER": true,
"CONFIGURATION": true,
"CONNECT": true,
"CONNECTION": true,
"CONNECTION_NAME": true,
"CONSTRAINT": true,
"CONSTRAINTS": true,
"CONSTRAINT_CATALOG": true,
"CONSTRAINT_NAME": true,
"CONSTRAINT_SCHEMA": true,
"CONSTRUCTOR": true,
"CONTAINS": true,
"CONTENT": true,
"CONTINUE": true,
"CONTROL": true,
"CONVERSION": true,
"CONVERT": true,
"COPY": true,
"CORR": true,
"CORRESPONDING": true,
"COST": true,
"COUNT": true,
"COVAR_POP": true,
"COVAR_SAMP": true,
"CREATE": true,
"CROSS": true,
"CSV": true,
"CUBE": true,
"CUME_DIST": true,
"CURRENT": true,
"CURRENT_CATALOG": true,
"CURRENT_DATE": true,
"CURRENT_DEFAULT_TRANSFORM_GROUP": true,
"CURRENT_PATH": true,
"CURRENT_ROLE": true,
"CURRENT_ROW": true,
"CURRENT_SCHEMA": true,
"CURRENT_TIME": true,
"CURRENT_TIMESTAMP": true,
"CURRENT_TRANSFORM_GROUP_FOR_TYPE": true,
"CURRENT_USER": true,
"CURSOR": true,
"CURSOR_NAME": true,
"CYCLE": true,
"DATA": true,
"DATABASE": true,
"DATALINK": true,
"DATE": true,
"DATETIME_INTERVAL_CODE": true,
"DATETIME_INTERVAL_PRECISION": true,
"DAY": true,
"DB": true,
"DEALLOCATE": true,
"DEC": true,
"DECIMAL": true,
"DECLARE": true,
"DEFAULT": true,
"DEFAULTS": true,
"DEFERRABLE": true,
"DEFERRED": true,
"DEFINED": true,
"DEFINER": true,
"DEGREE": true,
"DELETE": true,
"DELIMITER": true,
"DELIMITERS": true,
"DENSE_RANK": true,
"DEPTH": true,
"DEREF": true,
"DERIVED": true,
"DESC": true,
"DESCRIBE": true,
"DESCRIPTOR": true,
"DETERMINISTIC": true,
"DIAGNOSTICS": true,
"DICTIONARY": true,
"DISABLE": true,
"DISCARD": true,
"DISCONNECT": true,
"DISPATCH": true,
"DISTINCT": true,
"DLNEWCOPY": true,
"DLPREVIOUSCOPY": true,
"DLURLCOMPLETE": true,
"DLURLCOMPLETEONLY": true,
"DLURLCOMPLETEWRITE": true,
"DLURLPATH": true,
"DLURLPATHONLY": true,
"DLURLPATHWRITE": true,
"DLURLSCHEME": true,
"DLURLSERVER": true,
"DLVALUE": true,
"DO": true,
"DOCUMENT": true,
"DOMAIN": true,
"DOUBLE": true,
"DROP": true,
"DYNAMIC": true,
"DYNAMIC_FUNCTION": true,
"DYNAMIC_FUNCTION_CODE": true,
"EACH": true,
"ELEMENT": true,
"ELSE": true,
"EMPTY": true,
"ENABLE": true,
"ENCODING": true,
"ENCRYPTED": true,
"END": true,
"END-EXEC": true,
"END_FRAME": true,
"END_PARTITION": true,
"ENFORCED": true,
"ENUM": true,
"EQUALS": true,
"ESCAPE": true,
"EVENT": true,
"EVERY": true,
"EXCEPT": true,
"EXCEPTION": true,
"EXCLUDE": true,
"EXCLUDING": true,
"EXCLUSIVE": true,
"EXEC": true,
"EXECUTE": true,
"EXISTS": true,
"EXP": true,
"EXPLAIN": true,
"EXPRESSION": true,
"EXTENSION": true,
"EXTERNAL": true,
"EXTRACT": true,
"FALSE": true,
"FAMILY": true,
"FETCH": true,
"FILE": true,
"FILTER": true,
"FINAL": true,
"FIRST": true,
"FIRST_VALUE": true,
"FLAG": true,
"FLOAT": true,
"FLOOR": true,
"FOLLOWING": true,
"FOR": true,
"FORCE": true,
"FOREIGN": true,
"FORTRAN": true,
"FORWARD": true,
"FOUND": true,
"FRAME_ROW": true,
"FREE": true,
"FREEZE": true,
"FROM": true,
"FS": true,
"FULL": true,
"FUNCTION": true,
"FUNCTIONS": true,
"FUSION": true,
"G": true,
"GENERAL": true,
"GENERATED": true,
"GET": true,
"GLOBAL": true,
"GO": true,
"GOTO": true,
"GRANT": true,
"GRANTED": true,
"GREATEST": true,
"GROUP": true,
"GROUPING": true,
"GROUPS": true,
"HANDLER": true,
"HAVING": true,
"HEADER": true,
"HEX": true,
"HIERARCHY": true,
"HOLD": true,
"HOUR": true,
"ID": true,
"IDENTITY": true,
"IF": true,
"IGNORE": true,
"ILIKE": true,
"IMMEDIATE": true,
"IMMEDIATELY": true,
"IMMUTABLE": true,
"IMPLEMENTATION": true,
"IMPLICIT": true,
"IMPORT": true,
"IN": true,
"INCLUDING": true,
"INCREMENT": true,
"INDENT": true,
"INDEX": true,
"INDEXES": true,
"INDICATOR": true,
"INHERIT": true,
"INHERITS": true,
"INITIALLY": true,
"INLINE": true,
"INNER": true,
"INOUT": true,
"INPUT": true,
"INSENSITIVE": true,
"INSERT": true,
"INSTANCE": true,
"INSTANTIABLE": true,
"INSTEAD": true,
"INT": true,
"INTEGER": true,
"INTEGRITY": true,
"INTERSECT": true,
"INTERSECTION": true,
"INTERVAL": true,
"INTO": true,
"INVOKER": true,
"IS": true,
"ISNULL": true,
"ISOLATION": true,
"JOIN": true,
"K": true,
"KEY": true,
"KEY_MEMBER": true,
"KEY_TYPE": true,
"LABEL": true,
"LAG": true,
"LANGUAGE": true,
"LARGE": true,
"LAST": true,
"LAST_VALUE": true,
"LATERAL": true,
"LC_COLLATE": true,
"LC_CTYPE": true,
"LEAD": true,
"LEADING": true,
"LEAKPROOF": true,
"LEAST": true,
"LEFT": true,
"LENGTH": true,
"LEVEL": true,
"LIBRARY": true,
"LIKE": true,
"LIKE_REGEX": true,
"LIMIT": true,
"LINK": true,
"LISTEN": true,
"LN": true,
"LOAD": true,
"LOCAL": true,
"LOCALTIME": true,
"LOCALTIMESTAMP": true,
"LOCATION": true,
"LOCATOR": true,
"LOCK": true,
"LOWER": true,
"M": true,
"MAP": true,
"MAPPING": true,
"MATCH": true,
"MATCHED": true,
"MATERIALIZED": true,
"MAX": true,
"MAXVALUE": true,
"MAX_CARDINALITY": true,
"MEMBER": true,
"MERGE": true,
"MESSAGE_LENGTH": true,
"MESSAGE_OCTET_LENGTH": true,
"MESSAGE_TEXT": true,
"METHOD": true,
"MIN": true,
"MINUTE": true,
"MINVALUE": true,
"MOD": true,
"MODE": true,
"MODIFIES": true,
"MODULE": true,
"MONTH": true,
"MORE": true,
"MOVE": true,
"MULTISET": true,
"MUMPS": true,
"NAME": true,
"NAMES": true,
"NAMESPACE": true,
"NATIONAL": true,
"NATURAL": true,
"NCHAR": true,
"NCLOB": true,
"NESTING": true,
"NEW": true,
"NEXT": true,
"NFC": true,
"NFD": true,
"NFKC": true,
"NFKD": true,
"NIL": true,
"NO": true,
"NONE": true,
"NORMALIZE": true,
"NORMALIZED": true,
"NOT": true,
"NOTHING": true,
"NOTIFY": true,
"NOTNULL": true,
"NOWAIT": true,
"NTH_VALUE": true,
"NTILE": true,
"NULL": true,
"NULLABLE": true,
"NULLIF": true,
"NULLS": true,
"NUMBER": true,
"NUMERIC": true,
"OBJECT": true,
"OCCURRENCES_REGEX": true,
"OCTETS": true,
"OCTET_LENGTH": true,
"OF": true,
"OFF": true,
"OFFSET": true,
"OIDS": true,
"OLD": true,
"ON": true,
"ONLY": true,
"OPEN": true,
"OPERATOR": true,
"OPTION": true,
"OPTIONS": true,
"OR": true,
"ORDER": true,
"ORDERING": true,
"ORDINALITY": true,
"OTHERS": true,
"OUT": true,
"OUTER": true,
"OUTPUT": true,
"OVER": true,
"OVERLAPS": true,
"OVERLAY": true,
"OVERRIDING": true,
"OWNED": true,
"OWNER": true,
"P": true,
"PAD": true,
"PARAMETER": true,
"PARAMETER_MODE": true,
"PARAMETER_NAME": true,
"PARAMETER_ORDINAL_POSITION": true,
"PARAMETER_SPECIFIC_CATALOG": true,
"PARAMETER_SPECIFIC_NAME": true,
"PARAMETER_SPECIFIC_SCHEMA": true,
"PARSER": true,
"PARTIAL": true,
"PARTITION": true,
"PASCAL": true,
"PASSING": true,
"PASSTHROUGH": true,
"PASSWORD": true,
"PATH": true,
"PERCENT": true,
"PERCENTILE_CONT": true,
"PERCENTILE_DISC": true,
"PERCENT_RANK": true,
"PERIOD": true,
"PERMISSION": true,
"PLACING": true,
"PLANS": true,
"PLI": true,
"PORTION": true,
"POSITION": true,
"POSITION_REGEX": true,
"POWER": true,
"PRECEDES": true,
"PRECEDING": true,
"PRECISION": true,
"PREPARE": true,
"PREPARED": true,
"PRESERVE": true,
"PRIMARY": true,
"PRIOR": true,
"PRIVILEGES": true,
"PROCEDURAL": true,
"PROCEDURE": true,
"PROGRAM": true,
"PUBLIC": true,
"QUOTE": true,
"RANGE": true,
"RANK": true,
"READ": true,
"READS": true,
"REAL": true,
"REASSIGN": true,
"RECHECK": true,
"RECOVERY": true,
"RECURSIVE": true,
"REF": true,
"REFERENCES": true,
"REFERENCING": true,
"REFRESH": true,
"REGR_AVGX": true,
"REGR_AVGY": true,
"REGR_COUNT": true,
"REGR_INTERCEPT": true,
"REGR_R2": true,
"REGR_SLOPE": true,
"REGR_SXX": true,
"REGR_SXY": true,
"REGR_SYY": true,
"REINDEX": true,
"RELATIVE": true,
"RELEASE": true,
"RENAME": true,
"REPEATABLE": true,
"REPLACE": true,
"REPLICA": true,
"REQUIRING": true,
"RESET": true,
"RESPECT": true,
"RESTART": true,
"RESTORE": true,
"RESTRICT": true,
"RESULT": true,
"RETURN": true,
"RETURNED_CARDINALITY": true,
"RETURNED_LENGTH": true,
"RETURNED_OCTET_LENGTH": true,
"RETURNED_SQLSTATE": true,
"RETURNING": true,
"RETURNS": true,
"REVOKE": true,
"RIGHT": true,
"ROLE": true,
"ROLLBACK": true,
"ROLLUP": true,
"ROUTINE": true,
"ROUTINE_CATALOG": true,
"ROUTINE_NAME": true,
"ROUTINE_SCHEMA": true,
"ROW": true,
"ROWS": true,
"ROW_COUNT": true,
"ROW_NUMBER": true,
"RULE": true,
"SAVEPOINT": true,
"SCALE": true,
"SCHEMA": true,
"SCHEMA_NAME": true,
"SCOPE": true,
"SCOPE_CATALOG": true,
"SCOPE_NAME": true,
"SCOPE_SCHEMA": true,
"SCROLL": true,
"SEARCH": true,
"SECOND": true,
"SECTION": true,
"SECURITY": true,
"SELECT": true,
"SELECTIVE": true,
"SELF": true,
"SENSITIVE": true,
"SEQUENCE": true,
"SEQUENCES": true,
"SERIALIZABLE": true,
"SERVER": true,
"SERVER_NAME": true,
"SESSION": true,
"SESSION_USER": true,
"SET": true,
"SETOF": true,
"SETS": true,
"SHARE": true,
"SHOW": true,
"SIMILAR": true,
"SIMPLE": true,
"SIZE": true,
"SMALLINT": true,
"SNAPSHOT": true,
"SOME": true,
"SOURCE": true,
"SPACE": true,
"SPECIFIC": true,
"SPECIFICTYPE": true,
"SPECIFIC_NAME": true,
"SQL": true,
"SQLCODE": true,
"SQLERROR": true,
"SQLEXCEPTION": true,
"SQLSTATE": true,
"SQLWARNING": true,
"SQRT": true,
"STABLE": true,
"STANDALONE": true,
"START": true,
"STATE": true,
"STATEMENT": true,
"STATIC": true,
"STATISTICS": true,
"STDDEV_POP": true,
"STDDEV_SAMP": true,
"STDIN": true,
"STDOUT": true,
"STORAGE": true,
"STRICT": true,
"STRIP": true,
"STRUCTURE": true,
"STYLE": true,
"SUBCLASS_ORIGIN": true,
"SUBMULTISET": true,
"SUBSTRING": true,
"SUBSTRING_REGEX": true,
"SUCCEEDS": true,
"SUM": true,
"SYMMETRIC": true,
"SYSID": true,
"SYSTEM": true,
"SYSTEM_TIME": true,
"SYSTEM_USER": true,
"T": true,
"TABLE": true,
"TABLES": true,
"TABLESAMPLE": true,
"TABLESPACE": true,
"TABLE_NAME": true,
"TEMP": true,
"TEMPLATE": true,
"TEMPORARY": true,
"TEXT": true,
"THEN": true,
"TIES": true,
"TIME": true,
"TIMESTAMP": true,
"TIMEZONE_HOUR": true,
"TIMEZONE_MINUTE": true,
"TO": true,
"TOKEN": true,
"TOP_LEVEL_COUNT": true,
"TRAILING": true,
"TRANSACTION": true,
"TRANSACTIONS_COMMITTED": true,
"TRANSACTIONS_ROLLED_BACK": true,
"TRANSACTION_ACTIVE": true,
"TRANSFORM": true,
"TRANSFORMS": true,
"TRANSLATE": true,
"TRANSLATE_REGEX": true,
"TRANSLATION": true,
"TREAT": true,
"TRIGGER": true,
"TRIGGER_CATALOG": true,
"TRIGGER_NAME": true,
"TRIGGER_SCHEMA": true,
"TRIM": true,
"TRIM_ARRAY": true,
"TRUE": true,
"TRUNCATE": true,
"TRUSTED": true,
"TYPE": true,
"TYPES": true,
"UESCAPE": true,
"UNBOUNDED": true,
"UNCOMMITTED": true,
"UNDER": true,
"UNENCRYPTED": true,
"UNION": true,
"UNIQUE": true,
"UNKNOWN": true,
"UNLINK": true,
"UNLISTEN": true,
"UNLOGGED": true,
"UNNAMED": true,
"UNNEST": true,
"UNTIL": true,
"UNTYPED": true,
"UPDATE": true,
"UPPER": true,
"URI": true,
"USAGE": true,
"USER": true,
"USER_DEFINED_TYPE_CATALOG": true,
"USER_DEFINED_TYPE_CODE": true,
"USER_DEFINED_TYPE_NAME": true,
"USER_DEFINED_TYPE_SCHEMA": true,
"USING": true,
"VACUUM": true,
"VALID": true,
"VALIDATE": true,
"VALIDATOR": true,
"VALUE": true,
"VALUES": true,
"VALUE_OF": true,
"VARBINARY": true,
"VARCHAR": true,
"VARIADIC": true,
"VARYING": true,
"VAR_POP": true,
"VAR_SAMP": true,
"VERBOSE": true,
"VERSION": true,
"VERSIONING": true,
"VIEW": true,
"VOLATILE": true,
"WHEN": true,
"WHENEVER": true,
"WHERE": true,
"WHITESPACE": true,
"WIDTH_BUCKET": true,
"WINDOW": true,
"WITH": true,
"WITHIN": true,
"WITHOUT": true,
"WORK": true,
"WRAPPER": true,
"WRITE": true,
"XML": true,
"XMLAGG": true,
"XMLATTRIBUTES": true,
"XMLBINARY": true,
"XMLCAST": true,
"XMLCOMMENT": true,
"XMLCONCAT": true,
"XMLDECLARATION": true,
"XMLDOCUMENT": true,
"XMLELEMENT": true,
"XMLEXISTS": true,
"XMLFOREST": true,
"XMLITERATE": true,
"XMLNAMESPACES": true,
"XMLPARSE": true,
"XMLPI": true,
"XMLQUERY": true,
"XMLROOT": true,
"XMLSCHEMA": true,
"XMLSERIALIZE": true,
"XMLTABLE": true,
"XMLTEXT": true,
"XMLVALIDATE": true,
"YEAR": true,
"YES": true,
"ZONE": true,
}
)
type postgres struct {
core.Base
}
func (db *postgres) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
return db.Base.Init(d, db, uri, drivername, dataSourceName)
}
func (db *postgres) SqlType(c *core.Column) string {
var res string
switch t := c.SQLType.Name; t {
case core.TinyInt:
res = core.SmallInt
return res
case core.Bit:
res = core.Boolean
return res
case core.MediumInt, core.Int, core.Integer:
if c.IsAutoIncrement {
return core.Serial
}
return core.Integer
case core.BigInt:
if c.IsAutoIncrement {
return core.BigSerial
}
return core.BigInt
case core.Serial, core.BigSerial:
c.IsAutoIncrement = true
c.Nullable = false
res = t
case core.Binary, core.VarBinary:
return core.Bytea
case core.DateTime:
res = core.TimeStamp
case core.TimeStampz:
return "timestamp with time zone"
case core.Float:
res = core.Real
case core.TinyText, core.MediumText, core.LongText:
res = core.Text
case core.NVarchar:
res = core.Varchar
case core.Uuid:
res = core.Uuid
case core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob:
return core.Bytea
case core.Double:
return "DOUBLE PRECISION"
default:
if c.IsAutoIncrement {
return core.Serial
}
res = t
}
hasLen1 := (c.Length > 0)
hasLen2 := (c.Length2 > 0)
if hasLen2 {
res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
} else if hasLen1 {
res += "(" + strconv.Itoa(c.Length) + ")"
}
return res
}
func (db *postgres) SupportInsertMany() bool {
return true
}
func (db *postgres) IsReserved(name string) bool {
_, ok := postgresReservedWords[name]
return ok
}
func (db *postgres) Quote(name string) string {
name = strings.Replace(name, ".", `"."`, -1)
return "\"" + name + "\""
}
func (db *postgres) QuoteStr() string {
return "\""
}
func (db *postgres) AutoIncrStr() string {
return ""
}
func (db *postgres) SupportEngine() bool {
return false
}
func (db *postgres) SupportCharset() bool {
return false
}
func (db *postgres) IndexOnTable() bool {
return false
}
func (db *postgres) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
args := []interface{}{tableName, idxName}
return `SELECT indexname FROM pg_indexes ` +
`WHERE tablename = ? AND indexname = ?`, args
}
func (db *postgres) TableCheckSql(tableName string) (string, []interface{}) {
args := []interface{}{tableName}
return `SELECT tablename FROM pg_tables WHERE tablename = ?`, args
}
/*func (db *postgres) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
args := []interface{}{tableName, colName}
return "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = ?" +
" AND column_name = ?", args
}*/
func (db *postgres) ModifyColumnSql(tableName string, col *core.Column) string {
return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s",
tableName, col.Name, db.SqlType(col))
}
func (db *postgres) DropIndexSql(tableName string, index *core.Index) string {
//var unique string
quote := db.Quote
idxName := index.Name
if !strings.HasPrefix(idxName, "UQE_") &&
!strings.HasPrefix(idxName, "IDX_") {
if index.Type == core.UniqueType {
idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
} else {
idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
}
}
return fmt.Sprintf("DROP INDEX %v", quote(idxName))
}
func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) {
args := []interface{}{tableName, colName}
query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" +
" AND column_name = $2"
db.LogSQL(query, args)
rows, err := db.DB().Query(query, args...)
if err != nil {
return false, err
}
defer rows.Close()
return rows.Next(), nil
}
func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
// FIXME: the schema should be replaced by user custom's
args := []interface{}{tableName, "public"}
s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix ,
CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey,
CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey
FROM pg_attribute f
JOIN pg_class c ON c.oid = f.attrelid JOIN pg_type t ON t.oid = f.atttypid
LEFT JOIN pg_attrdef d ON d.adrelid = c.oid AND d.adnum = f.attnum
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey)
LEFT JOIN pg_class AS g ON p.confrelid = g.oid
LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name
WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.attnum > 0 ORDER BY f.attnum;`
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, nil, err
}
defer rows.Close()
cols := make(map[string]*core.Column)
colSeq := make([]string, 0)
for rows.Next() {
col := new(core.Column)
col.Indexes = make(map[string]int)
var colName, isNullable, dataType string
var maxLenStr, colDefault, numPrecision, numRadix *string
var isPK, isUnique bool
err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &numPrecision, &numRadix, &isPK, &isUnique)
if err != nil {
return nil, nil, err
}
//fmt.Println(args, colName, isNullable, dataType, maxLenStr, colDefault, numPrecision, numRadix, isPK, isUnique)
var maxLen int
if maxLenStr != nil {
maxLen, err = strconv.Atoi(*maxLenStr)
if err != nil {
return nil, nil, err
}
}
col.Name = strings.Trim(colName, `" `)
if colDefault != nil || isPK {
if isPK {
col.IsPrimaryKey = true
} else {
col.Default = *colDefault
}
}
if colDefault != nil && strings.HasPrefix(*colDefault, "nextval(") {
col.IsAutoIncrement = true
}
col.Nullable = (isNullable == "YES")
switch dataType {
case "character varying", "character":
col.SQLType = core.SQLType{Name: core.Varchar, DefaultLength: 0, DefaultLength2: 0}
case "timestamp without time zone":
col.SQLType = core.SQLType{Name: core.DateTime, DefaultLength: 0, DefaultLength2: 0}
case "timestamp with time zone":
col.SQLType = core.SQLType{Name: core.TimeStampz, DefaultLength: 0, DefaultLength2: 0}
case "double precision":
col.SQLType = core.SQLType{Name: core.Double, DefaultLength: 0, DefaultLength2: 0}
case "boolean":
col.SQLType = core.SQLType{Name: core.Bool, DefaultLength: 0, DefaultLength2: 0}
case "time without time zone":
col.SQLType = core.SQLType{Name: core.Time, DefaultLength: 0, DefaultLength2: 0}
case "oid":
col.SQLType = core.SQLType{Name: core.BigInt, DefaultLength: 0, DefaultLength2: 0}
default:
col.SQLType = core.SQLType{Name: strings.ToUpper(dataType), DefaultLength: 0, DefaultLength2: 0}
}
if _, ok := core.SqlTypes[col.SQLType.Name]; !ok {
return nil, nil, fmt.Errorf("Unknown colType: %v", dataType)
}
col.Length = maxLen
if col.SQLType.IsText() || col.SQLType.IsTime() {
if col.Default != "" {
col.Default = "'" + col.Default + "'"
} else {
if col.DefaultIsEmpty {
col.Default = "''"
}
}
}
cols[col.Name] = col
colSeq = append(colSeq, col.Name)
}
return colSeq, cols, nil
}
func (db *postgres) GetTables() ([]*core.Table, error) {
// FIXME: replace public to user customrize schema
args := []interface{}{"public"}
s := fmt.Sprintf("SELECT tablename FROM pg_tables WHERE schemaname = $1")
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
tables := make([]*core.Table, 0)
for rows.Next() {
table := core.NewEmptyTable()
var name string
err = rows.Scan(&name)
if err != nil {
return nil, err
}
table.Name = name
tables = append(tables, table)
}
return tables, nil
}
func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) {
// FIXME: replace the public schema to user specify schema
args := []interface{}{"public", tableName}
s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE schemaname=$1 AND tablename=$2")
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
indexes := make(map[string]*core.Index, 0)
for rows.Next() {
var indexType int
var indexName, indexdef string
var colNames []string
err = rows.Scan(&indexName, &indexdef)
if err != nil {
return nil, err
}
indexName = strings.Trim(indexName, `" `)
if strings.HasSuffix(indexName, "_pkey") {
continue
}
if strings.HasPrefix(indexdef, "CREATE UNIQUE INDEX") {
indexType = core.UniqueType
} else {
indexType = core.IndexType
}
cs := strings.Split(indexdef, "(")
colNames = strings.Split(cs[1][0:len(cs[1])-1], ",")
var isRegular bool
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
newIdxName := indexName[5+len(tableName):]
isRegular = true
if newIdxName != "" {
indexName = newIdxName
}
}
index := &core.Index{Name: indexName, Type: indexType, Cols: make([]string, 0)}
for _, colName := range colNames {
index.Cols = append(index.Cols, strings.Trim(colName, `" `))
}
index.IsRegular = isRegular
indexes[index.Name] = index
}
return indexes, nil
}
func (db *postgres) Filters() []core.Filter {
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{Prefix: "$", Start: 1}}
}
type pqDriver struct {
}
type values map[string]string
func (vs values) Set(k, v string) {
vs[k] = v
}
func (vs values) Get(k string) (v string) {
return vs[k]
}
func errorf(s string, args ...interface{}) {
panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
}
func parseURL(connstr string) (string, error) {
u, err := url.Parse(connstr)
if err != nil {
return "", err
}
if u.Scheme != "postgresql" && u.Scheme != "postgres" {
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
}
var kvs []string
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
accrue := func(k, v string) {
if v != "" {
kvs = append(kvs, k+"="+escaper.Replace(v))
}
}
if u.User != nil {
v := u.User.Username()
accrue("user", v)
v, _ = u.User.Password()
accrue("password", v)
}
i := strings.Index(u.Host, ":")
if i < 0 {
accrue("host", u.Host)
} else {
accrue("host", u.Host[:i])
accrue("port", u.Host[i+1:])
}
if u.Path != "" {
accrue("dbname", u.Path[1:])
}
q := u.Query()
for k := range q {
accrue(k, q.Get(k))
}
sort.Strings(kvs) // Makes testing easier (not a performance concern)
return strings.Join(kvs, " "), nil
}
func parseOpts(name string, o values) {
if len(name) == 0 {
return
}
name = strings.TrimSpace(name)
ps := strings.Split(name, " ")
for _, p := range ps {
kv := strings.Split(p, "=")
if len(kv) < 2 {
errorf("invalid option: %q", p)
}
o.Set(kv[0], kv[1])
}
}
func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
db := &core.Uri{DbType: core.POSTGRES}
o := make(values)
var err error
if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
dataSourceName, err = parseURL(dataSourceName)
if err != nil {
return nil, err
}
}
parseOpts(dataSourceName, o)
db.DbName = o.Get("dbname")
if db.DbName == "" {
return nil, errors.New("dbname is empty")
}
/*db.Schema = o.Get("schema")
if len(db.Schema) == 0 {
db.Schema = "public"
}*/
return db, nil
}