1package pgx 2 3import ( 4 "database/sql/driver" 5 "math" 6 "reflect" 7 "time" 8 9 "github.com/jackc/pgx/pgio" 10 "github.com/jackc/pgx/pgtype" 11) 12 13const ( 14 copyData = 'd' 15 copyFail = 'f' 16 copyDone = 'c' 17 varHeaderSize = 4 18) 19 20type FieldDescription struct { 21 Name string 22 Table pgtype.OID 23 AttributeNumber uint16 24 DataType pgtype.OID 25 DataTypeSize int16 26 DataTypeName string 27 Modifier int32 28 FormatCode int16 29} 30 31func (fd FieldDescription) Length() (int64, bool) { 32 switch fd.DataType { 33 case pgtype.TextOID, pgtype.ByteaOID: 34 return math.MaxInt64, true 35 case pgtype.VarcharOID, pgtype.BPCharArrayOID: 36 return int64(fd.Modifier - varHeaderSize), true 37 default: 38 return 0, false 39 } 40} 41 42func (fd FieldDescription) PrecisionScale() (precision, scale int64, ok bool) { 43 switch fd.DataType { 44 case pgtype.NumericOID: 45 mod := fd.Modifier - varHeaderSize 46 precision = int64((mod >> 16) & 0xffff) 47 scale = int64(mod & 0xffff) 48 return precision, scale, true 49 default: 50 return 0, 0, false 51 } 52} 53 54func (fd FieldDescription) Type() reflect.Type { 55 switch fd.DataType { 56 case pgtype.Float8OID: 57 return reflect.TypeOf(float64(0)) 58 case pgtype.Float4OID: 59 return reflect.TypeOf(float32(0)) 60 case pgtype.Int8OID: 61 return reflect.TypeOf(int64(0)) 62 case pgtype.Int4OID: 63 return reflect.TypeOf(int32(0)) 64 case pgtype.Int2OID: 65 return reflect.TypeOf(int16(0)) 66 case pgtype.VarcharOID, pgtype.BPCharArrayOID, pgtype.TextOID: 67 return reflect.TypeOf("") 68 case pgtype.BoolOID: 69 return reflect.TypeOf(false) 70 case pgtype.NumericOID: 71 return reflect.TypeOf(float64(0)) 72 case pgtype.DateOID, pgtype.TimestampOID, pgtype.TimestamptzOID: 73 return reflect.TypeOf(time.Time{}) 74 case pgtype.ByteaOID: 75 return reflect.TypeOf([]byte(nil)) 76 default: 77 return reflect.TypeOf(new(interface{})).Elem() 78 } 79} 80 81// PgError represents an error reported by the PostgreSQL server. See 82// http://www.postgresql.org/docs/9.3/static/protocol-error-fields.html for 83// detailed field description. 84type PgError struct { 85 Severity string 86 Code string 87 Message string 88 Detail string 89 Hint string 90 Position int32 91 InternalPosition int32 92 InternalQuery string 93 Where string 94 SchemaName string 95 TableName string 96 ColumnName string 97 DataTypeName string 98 ConstraintName string 99 File string 100 Line int32 101 Routine string 102} 103 104func (pe PgError) Error() string { 105 return pe.Severity + ": " + pe.Message + " (SQLSTATE " + pe.Code + ")" 106} 107 108// Notice represents a notice response message reported by the PostgreSQL 109// server. Be aware that this is distinct from LISTEN/NOTIFY notification. 110type Notice PgError 111 112// appendParse appends a PostgreSQL wire protocol parse message to buf and returns it. 113func appendParse(buf []byte, name string, query string, parameterOIDs []pgtype.OID) []byte { 114 buf = append(buf, 'P') 115 sp := len(buf) 116 buf = pgio.AppendInt32(buf, -1) 117 buf = append(buf, name...) 118 buf = append(buf, 0) 119 buf = append(buf, query...) 120 buf = append(buf, 0) 121 122 buf = pgio.AppendInt16(buf, int16(len(parameterOIDs))) 123 for _, oid := range parameterOIDs { 124 buf = pgio.AppendUint32(buf, uint32(oid)) 125 } 126 pgio.SetInt32(buf[sp:], int32(len(buf[sp:]))) 127 128 return buf 129} 130 131// appendDescribe appends a PostgreSQL wire protocol describe message to buf and returns it. 132func appendDescribe(buf []byte, objectType byte, name string) []byte { 133 buf = append(buf, 'D') 134 sp := len(buf) 135 buf = pgio.AppendInt32(buf, -1) 136 buf = append(buf, objectType) 137 buf = append(buf, name...) 138 buf = append(buf, 0) 139 pgio.SetInt32(buf[sp:], int32(len(buf[sp:]))) 140 141 return buf 142} 143 144// appendSync appends a PostgreSQL wire protocol sync message to buf and returns it. 145func appendSync(buf []byte) []byte { 146 buf = append(buf, 'S') 147 buf = pgio.AppendInt32(buf, 4) 148 149 return buf 150} 151 152// appendBind appends a PostgreSQL wire protocol bind message to buf and returns it. 153func appendBind( 154 buf []byte, 155 destinationPortal, 156 preparedStatement string, 157 connInfo *pgtype.ConnInfo, 158 parameterOIDs []pgtype.OID, 159 arguments []interface{}, 160 resultFormatCodes []int16, 161) ([]byte, error) { 162 buf = append(buf, 'B') 163 sp := len(buf) 164 buf = pgio.AppendInt32(buf, -1) 165 buf = append(buf, destinationPortal...) 166 buf = append(buf, 0) 167 buf = append(buf, preparedStatement...) 168 buf = append(buf, 0) 169 170 var err error 171 arguments, err = convertDriverValuers(arguments) 172 if err != nil { 173 return nil, err 174 } 175 176 buf = pgio.AppendInt16(buf, int16(len(parameterOIDs))) 177 for i, oid := range parameterOIDs { 178 buf = pgio.AppendInt16(buf, chooseParameterFormatCode(connInfo, oid, arguments[i])) 179 } 180 181 buf = pgio.AppendInt16(buf, int16(len(arguments))) 182 for i, oid := range parameterOIDs { 183 var err error 184 buf, err = encodePreparedStatementArgument(connInfo, buf, oid, arguments[i]) 185 if err != nil { 186 return nil, err 187 } 188 } 189 190 buf = pgio.AppendInt16(buf, int16(len(resultFormatCodes))) 191 for _, fc := range resultFormatCodes { 192 buf = pgio.AppendInt16(buf, fc) 193 } 194 pgio.SetInt32(buf[sp:], int32(len(buf[sp:]))) 195 196 return buf, nil 197} 198 199func convertDriverValuers(args []interface{}) ([]interface{}, error) { 200 for i, arg := range args { 201 switch arg := arg.(type) { 202 case pgtype.BinaryEncoder: 203 case pgtype.TextEncoder: 204 case driver.Valuer: 205 v, err := callValuerValue(arg) 206 if err != nil { 207 return nil, err 208 } 209 args[i] = v 210 } 211 } 212 return args, nil 213} 214 215// appendExecute appends a PostgreSQL wire protocol execute message to buf and returns it. 216func appendExecute(buf []byte, portal string, maxRows uint32) []byte { 217 buf = append(buf, 'E') 218 sp := len(buf) 219 buf = pgio.AppendInt32(buf, -1) 220 221 buf = append(buf, portal...) 222 buf = append(buf, 0) 223 buf = pgio.AppendUint32(buf, maxRows) 224 225 pgio.SetInt32(buf[sp:], int32(len(buf[sp:]))) 226 227 return buf 228} 229 230// appendQuery appends a PostgreSQL wire protocol query message to buf and returns it. 231func appendQuery(buf []byte, query string) []byte { 232 buf = append(buf, 'Q') 233 sp := len(buf) 234 buf = pgio.AppendInt32(buf, -1) 235 buf = append(buf, query...) 236 buf = append(buf, 0) 237 pgio.SetInt32(buf[sp:], int32(len(buf[sp:]))) 238 239 return buf 240} 241