xorm/dialects/postgres.go

1531 lines
52 KiB
Go
Raw Normal View History

2015-04-28 08:25:04 +00:00
// 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 dialects
import (
"context"
"database/sql"
2017-03-23 06:05:32 +00:00
"errors"
2013-12-18 03:31:32 +00:00
"fmt"
2017-03-23 06:05:32 +00:00
"net/url"
2013-12-18 03:31:32 +00:00
"strconv"
"strings"
"xorm.io/xorm/core"
"xorm.io/xorm/schemas"
2014-01-07 09:33:27 +00:00
)
2014-09-22 01:47:30 +00:00
// from http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html
var (
2014-09-07 00:32:58 +00:00
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,
2014-09-07 00:32:58 +00:00
"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,
2014-09-07 00:32:58 +00:00
}
postgresQuoter = schemas.Quoter{
Prefix: '"',
Suffix: '"',
IsReserved: schemas.AlwaysReserve,
}
)
2013-10-12 15:16:51 +00:00
var (
// DefaultPostgresSchema default postgres schema
DefaultPostgresSchema = "public"
postgresColAliases = map[string]string{
"numeric": "decimal",
}
)
2014-01-07 09:33:27 +00:00
type postgres struct {
Base
2013-12-17 09:30:05 +00:00
}
// Alias returns a alias of column
func (db *postgres) Alias(col string) string {
v, ok := postgresColAliases[strings.ToLower(col)]
if ok {
return v
}
return col
}
func (db *postgres) Init(uri *URI) error {
db.quoter = postgresQuoter
return db.Base.Init(db, uri)
}
func (db *postgres) Version(ctx context.Context, queryer core.Queryer) (*schemas.Version, error) {
rows, err := queryer.QueryContext(ctx, "SELECT version()")
if err != nil {
return nil, err
}
defer rows.Close()
var version string
if !rows.Next() {
if rows.Err() != nil {
return nil, rows.Err()
}
return nil, errors.New("unknow version")
}
if err := rows.Scan(&version); err != nil {
return nil, err
}
// Postgres: 9.5.22 on x86_64-pc-linux-gnu (Debian 9.5.22-1.pgdg90+1), compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit
// CockroachDB CCL v19.2.4 (x86_64-unknown-linux-gnu, built
if strings.HasPrefix(version, "CockroachDB") {
versions := strings.Split(strings.TrimPrefix(version, "CockroachDB CCL "), " ")
return &schemas.Version{
Number: strings.TrimPrefix(versions[0], "v"),
Edition: "CockroachDB",
}, nil
} else if strings.HasPrefix(version, "PostgreSQL") {
versions := strings.Split(strings.TrimPrefix(version, "PostgreSQL "), " on ")
return &schemas.Version{
Number: versions[0],
Level: versions[1],
Edition: "PostgreSQL",
}, nil
}
return nil, errors.New("unknow database version")
}
func (db *postgres) getSchema() string {
if db.uri.Schema != "" {
return db.uri.Schema
}
return DefaultPostgresSchema
}
func (db *postgres) needQuote(name string) bool {
if db.IsReserved(name) {
return true
}
for _, c := range name {
if c >= 'A' && c <= 'Z' {
return true
}
}
return false
}
func (db *postgres) SetQuotePolicy(quotePolicy QuotePolicy) {
switch quotePolicy {
case QuotePolicyNone:
var q = postgresQuoter
q.IsReserved = schemas.AlwaysNoReserve
db.quoter = q
case QuotePolicyReserved:
var q = postgresQuoter
q.IsReserved = db.needQuote
db.quoter = q
case QuotePolicyAlways:
fallthrough
default:
db.quoter = postgresQuoter
}
}
func (db *postgres) SQLType(c *schemas.Column) string {
2013-12-18 03:31:32 +00:00
var res string
switch t := c.SQLType.Name; t {
case schemas.TinyInt, schemas.UnsignedTinyInt:
res = schemas.SmallInt
return res
case schemas.Bit:
res = schemas.Boolean
return res
case schemas.MediumInt, schemas.Int, schemas.Integer, schemas.UnsignedMediumInt, schemas.UnsignedSmallInt:
if c.IsAutoIncrement {
return schemas.Serial
}
return schemas.Integer
case schemas.BigInt, schemas.UnsignedBigInt, schemas.UnsignedInt:
2016-12-10 02:53:27 +00:00
if c.IsAutoIncrement {
return schemas.BigSerial
2016-12-10 02:53:27 +00:00
}
return schemas.BigInt
case schemas.Serial, schemas.BigSerial:
2013-12-18 03:31:32 +00:00
c.IsAutoIncrement = true
c.Nullable = false
res = t
case schemas.Binary, schemas.VarBinary:
return schemas.Bytea
case schemas.DateTime:
res = schemas.TimeStamp
case schemas.TimeStampz:
2013-12-18 03:31:32 +00:00
return "timestamp with time zone"
case schemas.Float:
res = schemas.Real
case schemas.TinyText, schemas.MediumText, schemas.LongText:
res = schemas.Text
Fix warnings with schema Sync2 with default varchar as NVARCHAR (#1783) prod CI Merge branch 'fix-1782-MSSQL-nvarchar-fixes' of gitea.com:zeripath/xorm into fix-1782-MSSQL-nvarchar-fixes map "character" to Char for postgres Signed-off-by: Andrew Thornton <art27@cantab.net> Merge branch 'master' into fix-1782-MSSQL-nvarchar-fixes Postgres (and cockroachDB by inheritance) maps Char to Varchar Signed-off-by: Andrew Thornton <art27@cantab.net> fix test Add a few more column testcases in light of postgres weirdness prod CI prod CI prod CI Properly handle MAX Signed-off-by: Andrew Thornton <art27@cantab.net> Add tests for Test and Char columns Signed-off-by: Andrew Thornton <art27@cantab.net> prod CI fix test Signed-off-by: Andrew Thornton <art27@cantab.net> Remove the duplicate mssql drone test and add a specific sync test Signed-off-by: Andrew Thornton <art27@cantab.net> add depends_on (2) Signed-off-by: Andrew Thornton <art27@cantab.net> add depends_on Signed-off-by: Andrew Thornton <art27@cantab.net> add nvarchar as a testcase Signed-off-by: Andrew Thornton <art27@cantab.net> Set defaultVarchar to upper case Signed-off-by: Andrew Thornton <art27@cantab.net> Fix length issue Signed-off-by: Andrew Thornton <art27@cantab.net> schemas.Text should map to db.defaultVarchar Signed-off-by: Andrew Thornton <art27@cantab.net> Add failing test Signed-off-by: Andrew Thornton <art27@cantab.net> Reviewed-on: https://gitea.com/xorm/xorm/pulls/1783 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-09-08 01:36:50 +00:00
case schemas.NChar:
res = schemas.Char
case schemas.NVarchar:
res = schemas.Varchar
case schemas.Uuid:
return schemas.Uuid
case schemas.Blob, schemas.TinyBlob, schemas.MediumBlob, schemas.LongBlob:
return schemas.Bytea
case schemas.Double:
2013-12-18 03:31:32 +00:00
return "DOUBLE PRECISION"
default:
if c.IsAutoIncrement {
return schemas.Serial
2013-12-18 03:31:32 +00:00
}
res = t
}
if strings.EqualFold(res, "bool") {
// for bool, we don't need length information
return res
}
hasLen1 := (c.Length > 0)
hasLen2 := (c.Length2 > 0)
2014-05-29 08:53:23 +00:00
if hasLen2 {
2013-12-18 03:31:32 +00:00
res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
2014-05-29 08:53:23 +00:00
} else if hasLen1 {
res += "(" + strconv.Itoa(c.Length) + ")"
2013-12-18 03:31:32 +00:00
}
return res
}
func (db *postgres) ColumnTypeKind(t string) int {
switch strings.ToUpper(t) {
case "DATETIME", "TIMESTAMP":
return schemas.TIME_TYPE
case "VARCHAR", "TEXT":
return schemas.TEXT_TYPE
case "BIGINT", "BIGSERIAL", "SMALLINT", "INT", "INT8", "INT4", "INTEGER", "SERIAL", "FLOAT", "FLOAT4", "REAL", "DOUBLE PRECISION":
return schemas.NUMERIC_TYPE
case "BOOL":
return schemas.BOOL_TYPE
default:
return schemas.UNKNOW_TYPE
}
}
func (db *postgres) IsReserved(name string) bool {
_, ok := postgresReservedWords[strings.ToUpper(name)]
return ok
}
func (db *postgres) AutoIncrStr() string {
2013-12-18 03:31:32 +00:00
return ""
}
func (db *postgres) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) {
var sql string
sql = "CREATE TABLE IF NOT EXISTS "
if tableName == "" {
tableName = table.Name
}
quoter := db.Quoter()
sql += quoter.Quote(tableName)
sql += " ("
2013-09-26 07:19:39 +00:00
if len(table.ColumnsSeq()) > 0 {
pkList := table.PrimaryKeys
for _, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1)
sql += s
sql = strings.TrimSpace(sql)
sql += ", "
}
if len(pkList) > 1 {
sql += "PRIMARY KEY ( "
sql += quoter.Join(pkList, ",")
sql += " ), "
}
sql = sql[:len(sql)-2]
}
sql += ")"
return []string{sql}, true
2013-09-26 07:19:39 +00:00
}
func (db *postgres) IndexCheckSQL(tableName, idxName string) (string, []interface{}) {
if len(db.getSchema()) == 0 {
args := []interface{}{tableName, idxName}
return `SELECT indexname FROM pg_indexes WHERE tablename = ? AND indexname = ?`, args
}
args := []interface{}{db.getSchema(), tableName, idxName}
2013-12-18 03:31:32 +00:00
return `SELECT indexname FROM pg_indexes ` +
`WHERE schemaname = ? AND tablename = ? AND indexname = ?`, args
}
func (db *postgres) IsTableExist(queryer core.Queryer, ctx context.Context, tableName string) (bool, error) {
if len(db.getSchema()) == 0 {
return db.HasRecords(queryer, ctx, `SELECT tablename FROM pg_tables WHERE tablename = $1`, tableName)
}
return db.HasRecords(queryer, ctx, `SELECT tablename FROM pg_tables WHERE schemaname = $1 AND tablename = $2`,
db.getSchema(), tableName)
}
func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) string {
if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") {
return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s",
tableName, col.Name, db.SQLType(col))
}
return fmt.Sprintf("alter table %s.%s ALTER COLUMN %s TYPE %s",
db.getSchema(), tableName, col.Name, db.SQLType(col))
}
func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string {
idxName := index.Name
tableParts := strings.Split(strings.Replace(tableName, `"`, "", -1), ".")
tableName = tableParts[len(tableParts)-1]
if !strings.HasPrefix(idxName, "UQE_") &&
!strings.HasPrefix(idxName, "IDX_") {
if index.Type == schemas.UniqueType {
idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
} else {
idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
}
}
if db.getSchema() != "" {
idxName = db.getSchema() + "." + idxName
}
Fix join table name quote bug (#1534) Fix test Fix test Add new Quoter object to handle quote Fix join table name quote bug Move reserve words related files into dialects sub package (#1544) Move reserve words related files into dialects sub package Reviewed-on: https://gitea.com/xorm/xorm/pulls/1544 Fix mssql quote (#1535) Fix some quotes Fix mssql quote Merge core package back into the main repository and split into serval sub packages. (#1543) Fix test Improve fmt update go.mod Move core as a sub package Reviewed-on: https://gitea.com/xorm/xorm/pulls/1543 Fix int time deleted bug (#1539) Fix panic Fix test Fix test for mssql time Add sql type check on deleted cond Fix int time deleted bug Reviewed-on: https://gitea.com/xorm/xorm/pulls/1539 Add test for mysql8.0 (#1538) Fix pk order on test Add test for mysql8.0 Reviewed-on: https://gitea.com/xorm/xorm/pulls/1538 Add test for join limit (#1536) Add test for join limit Reviewed-on: https://gitea.com/xorm/xorm/pulls/1536 Improve drone (#1537) Fix drone Improve drone * use traditional positional parameters on inser... Reviewed-on: https://gitea.com/xorm/xorm/pulls/1537 Fix slice of struct not cache bug (#895) Fix failure caused by nil bean Judge both type of struct and pointer in case of out-of-range Fix issue #894 Add test for join subquery (#1528) Fix test Fix subquery with schema Add test for join subquery Add makefile (#1531) Fix drone Fix ci Add deps Improve drone Fix envs Add makefile Reviewed-on: https://gitea.com/xorm/xorm/pulls/1531 Add password for postgres drone image (#1530) Add password for postgres drone image Reviewed-on: https://gitea.com/xorm/xorm/pulls/1530 format time when sqlTypeName is core.Varchar (#1026) fix time test add test for time format sign codes according to contributing rules. format time when sqlTypeName is core.Varchar. Same with core.DateTime or core.TimeStamp Add test for second insert error (#1527) Add test for second insert error Reviewed-on: https://gitea.com/xorm/xorm/pulls/1527 Add tests for table name (#1517) add tests for table name Fix test (#1526) Fix test Reviewed-on: https://gitea.com/xorm/xorm/pulls/1526 Fix test (#1526) Fix test Reviewed-on: https://gitea.com/xorm/xorm/pulls/1526 Fix wrong warning log on autoincrement column when sync table (#1525) improve doc Fix wrong warning log on autoincrement column when sync table Reviewed-on: https://gitea.com/xorm/xorm/pulls/1525 Fixed Join strings on func Exist (#1520) fix test fixed Join strings on func Exist Co-authored-by: Tomofumi Kusana <tkusana@morisawa.co.jp> Reviewed-on: https://gitea.com/xorm/xorm/pulls/1520 For nullable columns, store nil values as NULL (#531) Merge branch 'master' into jcsalem/fix/nil_ptr_is_nullable fix bug when buffersize with iterate (#941) Merge branch 'master' into lunny/fix_buffer_iterate Exclude schema from index name (#1505) Merge branch 'master' into fix-schema-idx SetExpr support more go types (#1499) Improve tests SetExpr support more go types fix vet fix drone lint remove go1.10 test on drone Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499 fix vet fix drone lint remove go1.10 test on drone Exclude schema from the index name Co-authored-by: Guillermo Prandi <guillep2k@users.noreply.github.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Reviewed-on: https://gitea.com/xorm/xorm/pulls/1505 fix test fix bug fix bug when buffersize with iterate SetExpr support more go types (#1499) Improve tests SetExpr support more go types fix vet fix drone lint remove go1.10 test on drone Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499 fix vet fix drone lint remove go1.10 test on drone Fix update with Alias (#1455) Co-authored-by: Guillermo Prandi <guillep2k@noreply.gitea.io> Reviewed-on: https://gitea.com/xorm/xorm/pulls/941 fix update map with version (#1448) fix test fix update map with version SetExpr support more go types (#1499) Improve tests SetExpr support more go types fix vet fix drone lint remove go1.10 test on drone Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499 fix vet fix drone lint remove go1.10 test on drone Fix update with Alias (#1455) Reviewed-on: https://gitea.com/xorm/xorm/pulls/1448 Exclude schema from index name (#1505) Merge branch 'master' into fix-schema-idx SetExpr support more go types (#1499) Improve tests SetExpr support more go types fix vet fix drone lint remove go1.10 test on drone Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499 fix vet fix drone lint remove go1.10 test on drone Exclude schema from the index name Co-authored-by: Guillermo Prandi <guillep2k@users.noreply.github.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Reviewed-on: https://gitea.com/xorm/xorm/pulls/1505 SetExpr support more go types (#1499) Improve tests SetExpr support more go types fix vet fix drone lint remove go1.10 test on drone Reviewed-on: https://gitea.com/xorm/xorm/pulls/1499 For nullable columns, store nil values as NULL fix vet fix drone lint remove go1.10 test on drone Fix update with Alias (#1455) Improve c... Reviewed-on: https://gitea.com/xorm/xorm/pulls/1534
2020-02-25 00:01:36 +00:00
return fmt.Sprintf("DROP INDEX %v", db.Quoter().Quote(idxName))
}
func (db *postgres) IsColumnExist(queryer core.Queryer, ctx context.Context, tableName, colName string) (bool, error) {
args := []interface{}{db.getSchema(), tableName, colName}
query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = $1 AND table_name = $2" +
" AND column_name = $3"
if len(db.getSchema()) == 0 {
args = []interface{}{tableName, colName}
query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" +
" AND column_name = $2"
}
rows, err := queryer.QueryContext(ctx, query, args...)
2014-04-29 07:20:18 +00:00
if err != nil {
return false, err
}
defer rows.Close()
if rows.Next() {
return true, nil
}
return false, rows.Err()
2014-04-29 07:20:18 +00:00
}
func (db *postgres) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) {
args := []interface{}{tableName}
s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, description,
2014-06-23 13:46:08 +00:00
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_description de ON f.attrelid=de.objoid AND f.attnum=de.objsubid
2014-06-23 13:46:08 +00:00
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 n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.attnum;`
schema := db.getSchema()
if schema != "" {
s = fmt.Sprintf(s, " AND s.table_schema = $2")
args = append(args, schema)
} else {
s = fmt.Sprintf(s, "")
}
rows, err := queryer.QueryContext(ctx, s, args...)
2014-06-26 06:35:40 +00:00
if err != nil {
return nil, nil, err
}
defer rows.Close()
cols := make(map[string]*schemas.Column)
2014-06-26 06:35:40 +00:00
colSeq := make([]string, 0)
for rows.Next() {
col := new(schemas.Column)
2016-06-30 08:35:04 +00:00
col.Indexes = make(map[string]int)
2014-06-26 06:35:40 +00:00
var colName, isNullable, dataType string
var maxLenStr, colDefault, description *string
2014-06-26 06:35:40 +00:00
var isPK, isUnique bool
err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &description, &isPK, &isUnique)
2014-06-26 06:35:40 +00:00
if err != nil {
return nil, nil, err
}
2014-08-28 14:48:01 +00:00
2014-06-26 06:35:40 +00:00
var maxLen int
if maxLenStr != nil {
maxLen, err = strconv.Atoi(*maxLenStr)
if err != nil {
return nil, nil, err
}
}
if colDefault != nil && *colDefault == "unique_rowid()" { // ignore the system column added by cockroach
continue
}
2014-06-26 06:35:40 +00:00
col.Name = strings.Trim(colName, `" `)
if colDefault != nil {
var theDefault = *colDefault
// cockroach has type with the default value with :::
// and postgres with ::, we should remove them before store them
idx := strings.Index(theDefault, ":::")
if idx == -1 {
idx = strings.Index(theDefault, "::")
}
if idx > -1 {
theDefault = theDefault[:idx]
}
if strings.HasSuffix(theDefault, "+00:00'") {
theDefault = theDefault[:len(theDefault)-7] + "'"
}
col.Default = theDefault
col.DefaultIsEmpty = false
if strings.HasPrefix(col.Default, "nextval(") {
col.IsAutoIncrement = true
col.Default = ""
col.DefaultIsEmpty = true
2014-06-26 06:35:40 +00:00
}
} else {
col.DefaultIsEmpty = true
2014-06-26 06:35:40 +00:00
}
if description != nil {
col.Comment = *description
}
if isPK {
col.IsPrimaryKey = true
2014-08-28 15:04:28 +00:00
}
2014-06-26 06:35:40 +00:00
col.Nullable = (isNullable == "YES")
switch strings.ToLower(dataType) {
Fix warnings with schema Sync2 with default varchar as NVARCHAR (#1783) prod CI Merge branch 'fix-1782-MSSQL-nvarchar-fixes' of gitea.com:zeripath/xorm into fix-1782-MSSQL-nvarchar-fixes map "character" to Char for postgres Signed-off-by: Andrew Thornton <art27@cantab.net> Merge branch 'master' into fix-1782-MSSQL-nvarchar-fixes Postgres (and cockroachDB by inheritance) maps Char to Varchar Signed-off-by: Andrew Thornton <art27@cantab.net> fix test Add a few more column testcases in light of postgres weirdness prod CI prod CI prod CI Properly handle MAX Signed-off-by: Andrew Thornton <art27@cantab.net> Add tests for Test and Char columns Signed-off-by: Andrew Thornton <art27@cantab.net> prod CI fix test Signed-off-by: Andrew Thornton <art27@cantab.net> Remove the duplicate mssql drone test and add a specific sync test Signed-off-by: Andrew Thornton <art27@cantab.net> add depends_on (2) Signed-off-by: Andrew Thornton <art27@cantab.net> add depends_on Signed-off-by: Andrew Thornton <art27@cantab.net> add nvarchar as a testcase Signed-off-by: Andrew Thornton <art27@cantab.net> Set defaultVarchar to upper case Signed-off-by: Andrew Thornton <art27@cantab.net> Fix length issue Signed-off-by: Andrew Thornton <art27@cantab.net> schemas.Text should map to db.defaultVarchar Signed-off-by: Andrew Thornton <art27@cantab.net> Add failing test Signed-off-by: Andrew Thornton <art27@cantab.net> Reviewed-on: https://gitea.com/xorm/xorm/pulls/1783 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-09-08 01:36:50 +00:00
case "character varying", "string":
col.SQLType = schemas.SQLType{Name: schemas.Varchar, DefaultLength: 0, DefaultLength2: 0}
Fix warnings with schema Sync2 with default varchar as NVARCHAR (#1783) prod CI Merge branch 'fix-1782-MSSQL-nvarchar-fixes' of gitea.com:zeripath/xorm into fix-1782-MSSQL-nvarchar-fixes map "character" to Char for postgres Signed-off-by: Andrew Thornton <art27@cantab.net> Merge branch 'master' into fix-1782-MSSQL-nvarchar-fixes Postgres (and cockroachDB by inheritance) maps Char to Varchar Signed-off-by: Andrew Thornton <art27@cantab.net> fix test Add a few more column testcases in light of postgres weirdness prod CI prod CI prod CI Properly handle MAX Signed-off-by: Andrew Thornton <art27@cantab.net> Add tests for Test and Char columns Signed-off-by: Andrew Thornton <art27@cantab.net> prod CI fix test Signed-off-by: Andrew Thornton <art27@cantab.net> Remove the duplicate mssql drone test and add a specific sync test Signed-off-by: Andrew Thornton <art27@cantab.net> add depends_on (2) Signed-off-by: Andrew Thornton <art27@cantab.net> add depends_on Signed-off-by: Andrew Thornton <art27@cantab.net> add nvarchar as a testcase Signed-off-by: Andrew Thornton <art27@cantab.net> Set defaultVarchar to upper case Signed-off-by: Andrew Thornton <art27@cantab.net> Fix length issue Signed-off-by: Andrew Thornton <art27@cantab.net> schemas.Text should map to db.defaultVarchar Signed-off-by: Andrew Thornton <art27@cantab.net> Add failing test Signed-off-by: Andrew Thornton <art27@cantab.net> Reviewed-on: https://gitea.com/xorm/xorm/pulls/1783 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-09-08 01:36:50 +00:00
case "character":
col.SQLType = schemas.SQLType{Name: schemas.Char, DefaultLength: 0, DefaultLength2: 0}
2014-06-26 06:35:40 +00:00
case "timestamp without time zone":
col.SQLType = schemas.SQLType{Name: schemas.DateTime, DefaultLength: 0, DefaultLength2: 0}
2014-06-26 06:35:40 +00:00
case "timestamp with time zone":
col.SQLType = schemas.SQLType{Name: schemas.TimeStampz, DefaultLength: 0, DefaultLength2: 0}
2014-06-26 06:35:40 +00:00
case "double precision":
col.SQLType = schemas.SQLType{Name: schemas.Double, DefaultLength: 0, DefaultLength2: 0}
2014-06-26 06:35:40 +00:00
case "boolean":
col.SQLType = schemas.SQLType{Name: schemas.Bool, DefaultLength: 0, DefaultLength2: 0}
2014-06-26 06:35:40 +00:00
case "time without time zone":
col.SQLType = schemas.SQLType{Name: schemas.Time, DefaultLength: 0, DefaultLength2: 0}
case "bytes":
col.SQLType = schemas.SQLType{Name: schemas.Binary, DefaultLength: 0, DefaultLength2: 0}
2016-04-22 13:43:22 +00:00
case "oid":
col.SQLType = schemas.SQLType{Name: schemas.BigInt, DefaultLength: 0, DefaultLength2: 0}
case "array":
col.SQLType = schemas.SQLType{Name: schemas.Array, DefaultLength: 0, DefaultLength2: 0}
2014-06-26 06:35:40 +00:00
default:
startIdx := strings.Index(strings.ToLower(dataType), "string(")
if startIdx != -1 && strings.HasSuffix(dataType, ")") {
length := dataType[startIdx+8 : len(dataType)-1]
l, _ := strconv.Atoi(length)
col.SQLType = schemas.SQLType{Name: "STRING", DefaultLength: l, DefaultLength2: 0}
} else {
col.SQLType = schemas.SQLType{Name: strings.ToUpper(dataType), DefaultLength: 0, DefaultLength2: 0}
}
2014-06-26 06:35:40 +00:00
}
if _, ok := schemas.SqlTypes[col.SQLType.Name]; !ok {
return nil, nil, fmt.Errorf("unknown colType: %s - %s", dataType, col.SQLType.Name)
2014-06-26 06:35:40 +00:00
}
col.Length = maxLen
if !col.DefaultIsEmpty {
if col.SQLType.IsText() {
if strings.HasSuffix(col.Default, "::character varying") {
col.Default = strings.TrimSuffix(col.Default, "::character varying")
} else if !strings.HasPrefix(col.Default, "'") {
col.Default = "'" + col.Default + "'"
}
} else if col.SQLType.IsTime() {
if strings.HasSuffix(col.Default, "::timestamp without time zone") {
col.Default = strings.TrimSuffix(col.Default, "::timestamp without time zone")
2014-06-26 06:35:40 +00:00
}
}
}
cols[col.Name] = col
colSeq = append(colSeq, col.Name)
}
if rows.Err() != nil {
return nil, nil, rows.Err()
}
2014-06-26 06:35:40 +00:00
return colSeq, cols, nil
2013-10-12 15:16:51 +00:00
}
func (db *postgres) GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) {
args := []interface{}{}
s := "SELECT tablename FROM pg_tables"
schema := db.getSchema()
if schema != "" {
args = append(args, schema)
s = s + " WHERE schemaname = $1"
}
rows, err := queryer.QueryContext(ctx, s, args...)
2013-12-18 03:31:32 +00:00
if err != nil {
return nil, err
}
2014-04-18 10:39:07 +00:00
defer rows.Close()
2013-12-18 03:31:32 +00:00
tables := make([]*schemas.Table, 0)
2014-01-07 09:33:27 +00:00
for rows.Next() {
table := schemas.NewEmptyTable()
2014-01-07 09:33:27 +00:00
var name string
err = rows.Scan(&name)
if err != nil {
return nil, err
2013-12-18 03:31:32 +00:00
}
2014-01-07 09:33:27 +00:00
table.Name = name
2013-12-18 03:31:32 +00:00
tables = append(tables, table)
}
if rows.Err() != nil {
return nil, rows.Err()
}
2013-12-18 03:31:32 +00:00
return tables, nil
2013-10-12 15:16:51 +00:00
}
func getIndexColName(indexdef string) []string {
var colNames []string
cs := strings.Split(indexdef, "(")
for _, v := range strings.Split(strings.Split(cs[1], ")")[0], ",") {
colNames = append(colNames, strings.Split(strings.TrimLeft(v, " "), " ")[0])
}
return colNames
}
func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableName string) (map[string]*schemas.Index, error) {
args := []interface{}{tableName}
s := "SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1"
if len(db.getSchema()) != 0 {
args = append(args, db.getSchema())
s = s + " AND schemaname=$2"
}
2013-12-18 03:31:32 +00:00
rows, err := queryer.QueryContext(ctx, s, args...)
2013-12-18 03:31:32 +00:00
if err != nil {
return nil, err
}
2014-04-18 10:39:07 +00:00
defer rows.Close()
2013-12-18 03:31:32 +00:00
indexes := make(map[string]*schemas.Index)
2014-01-07 09:33:27 +00:00
for rows.Next() {
2013-12-18 03:31:32 +00:00
var indexType int
2014-01-07 09:33:27 +00:00
var indexName, indexdef string
2013-12-18 03:31:32 +00:00
var colNames []string
2014-01-07 09:33:27 +00:00
err = rows.Scan(&indexName, &indexdef)
if err != nil {
return nil, err
}
if indexName == "primary" {
continue
}
2014-01-07 09:33:27 +00:00
indexName = strings.Trim(indexName, `" `)
// ignore primary index
if strings.HasSuffix(indexName, "_pkey") || strings.EqualFold(indexName, "primary") {
continue
}
2014-01-07 09:33:27 +00:00
if strings.HasPrefix(indexdef, "CREATE UNIQUE INDEX") {
indexType = schemas.UniqueType
2014-01-07 09:33:27 +00:00
} else {
indexType = schemas.IndexType
2013-12-18 03:31:32 +00:00
}
colNames = getIndexColName(indexdef)
2017-04-05 10:17:41 +00:00
var isRegular bool
2013-12-18 03:31:32 +00:00
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
2017-01-09 01:52:23 +00:00
newIdxName := indexName[5+len(tableName):]
2017-04-05 10:17:41 +00:00
isRegular = true
2013-12-18 03:31:32 +00:00
if newIdxName != "" {
indexName = newIdxName
}
}
index := &schemas.Index{Name: indexName, Type: indexType, Cols: make([]string, 0)}
2013-12-18 03:31:32 +00:00
for _, colName := range colNames {
col := strings.TrimSpace(strings.Replace(colName, `"`, "", -1))
fields := strings.Split(col, " ")
index.Cols = append(index.Cols, fields[0])
2013-12-18 03:31:32 +00:00
}
2017-04-05 10:17:41 +00:00
index.IsRegular = isRegular
2013-12-18 03:31:32 +00:00
indexes[index.Name] = index
}
if rows.Err() != nil {
return nil, rows.Err()
}
2013-12-18 03:31:32 +00:00
return indexes, nil
2013-10-12 15:16:51 +00:00
}
2014-01-07 09:33:27 +00:00
func (db *postgres) Filters() []Filter {
return []Filter{&SeqFilter{Prefix: "$", Start: 1}}
2014-01-07 09:33:27 +00:00
}
2017-03-23 06:05:32 +00:00
type pqDriver struct {
baseDriver
2017-03-23 06:05:32 +00:00
}
type values map[string]string
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)
}
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
if u.Path != "" {
return escaper.Replace(u.Path[1:]), nil
2017-03-23 06:05:32 +00:00
}
return "", nil
2017-03-23 06:05:32 +00:00
}
func parseOpts(urlStr string, o values) error {
if len(urlStr) == 0 {
return fmt.Errorf("invalid options: %s", urlStr)
2017-03-23 06:05:32 +00:00
}
urlStr = strings.TrimSpace(urlStr)
var (
inQuote bool
state int // 0 key, 1 space, 2 value, 3 equal
start int
key string
)
for i, c := range urlStr {
switch c {
case ' ':
if !inQuote {
if state == 2 {
state = 1
v := urlStr[start:i]
if strings.HasPrefix(v, "'") && strings.HasSuffix(v, "'") {
v = v[1 : len(v)-1]
} else if strings.HasPrefix(v, "'") || strings.HasSuffix(v, "'") {
return fmt.Errorf("wrong single quote in %d of %s", i, urlStr)
}
o[key] = v
} else if state != 1 {
return fmt.Errorf("wrong format: %v", urlStr)
}
}
case '\'':
if state == 3 {
state = 2
start = i
} else if state != 2 {
return fmt.Errorf("wrong format: %v", urlStr)
}
inQuote = !inQuote
case '=':
if !inQuote {
if state != 0 {
return fmt.Errorf("wrong format: %v", urlStr)
}
key = urlStr[start:i]
state = 3
}
default:
if state == 3 {
state = 2
start = i
} else if state == 1 {
state = 0
start = i
}
}
2017-03-23 06:05:32 +00:00
if i == len(urlStr)-1 {
if state != 2 {
return errors.New("no value matched key")
}
v := urlStr[start : i+1]
if strings.HasPrefix(v, "'") && strings.HasSuffix(v, "'") {
v = v[1 : len(v)-1]
} else if strings.HasPrefix(v, "'") || strings.HasSuffix(v, "'") {
return fmt.Errorf("wrong single quote in %d of %s", i, urlStr)
}
o[key] = v
2017-03-23 06:05:32 +00:00
}
}
return nil
2017-03-23 06:05:32 +00:00
}
func (p *pqDriver) Features() *DriverFeatures {
return &DriverFeatures{
SupportReturnInsertedID: false,
}
}
func (p *pqDriver) Parse(driverName, dataSourceName string) (*URI, error) {
db := &URI{DBType: schemas.POSTGRES}
2017-03-23 06:05:32 +00:00
var err error
if strings.Contains(dataSourceName, "://") {
if !strings.HasPrefix(dataSourceName, "postgresql://") && !strings.HasPrefix(dataSourceName, "postgres://") {
return nil, fmt.Errorf("unsupported protocol %v", dataSourceName)
}
db.DBName, err = parseURL(dataSourceName)
if err != nil {
return nil, err
}
} else {
o := make(values)
err = parseOpts(dataSourceName, o)
2017-03-23 06:05:32 +00:00
if err != nil {
return nil, err
}
db.DBName = o["dbname"]
2017-03-23 06:05:32 +00:00
}
if db.DBName == "" {
2017-03-23 06:05:32 +00:00
return nil, errors.New("dbname is empty")
}
2017-03-23 06:05:32 +00:00
return db, nil
}
func (p *pqDriver) GenScanResult(colType string) (interface{}, error) {
switch colType {
case "VARCHAR", "TEXT":
var s sql.NullString
return &s, nil
case "BIGINT", "BIGSERIAL":
var s sql.NullInt64
return &s, nil
case "SMALLINT", "INT", "INT8", "INT4", "INTEGER", "SERIAL":
var s sql.NullInt32
return &s, nil
case "FLOAT", "FLOAT4", "REAL", "DOUBLE PRECISION":
var s sql.NullFloat64
return &s, nil
case "DATETIME", "TIMESTAMP":
var s sql.NullTime
return &s, nil
case "BOOL":
var s sql.NullBool
return &s, nil
default:
var r sql.RawBytes
return &r, nil
}
}
type pqDriverPgx struct {
pqDriver
}
func (pgx *pqDriverPgx) Parse(driverName, dataSourceName string) (*URI, error) {
// Remove the leading characters for driver to work
if len(dataSourceName) >= 9 && dataSourceName[0] == 0 {
dataSourceName = dataSourceName[9:]
}
return pgx.pqDriver.Parse(driverName, dataSourceName)
}
// QueryDefaultPostgresSchema returns the default postgres schema
func QueryDefaultPostgresSchema(ctx context.Context, queryer core.Queryer) (string, error) {
rows, err := queryer.QueryContext(ctx, "SHOW SEARCH_PATH")
if err != nil {
return "", err
}
defer rows.Close()
if rows.Next() {
var defaultSchema string
if err = rows.Scan(&defaultSchema); err != nil {
return "", err
}
parts := strings.Split(defaultSchema, ",")
return strings.TrimSpace(parts[len(parts)-1]), nil
}
if rows.Err() != nil {
return "", rows.Err()
}
return "", errors.New("no default schema")
}