github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/lib/pq/copy.go (about) 1 package pq 2 3 import ( 4 "database/sql/driver" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "sync" 9 ) 10 11 var ( 12 errCopyInClosed = errors.New("pq: copyin statement has already been closed") 13 errBinaryCopyNotSupported = errors.New("pq: only text format supported for COPY") 14 errCopyToNotSupported = errors.New("pq: COPY TO is not supported") 15 errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction") 16 ) 17 18 // CopyIn creates a COPY FROM statement which can be prepared with 19 // Tx.Prepare(). The target table should be visible in search_path. 20 func CopyIn(table string, columns ...string) string { 21 stmt := "COPY " + QuoteIdentifier(table) + " (" 22 for i, col := range columns { 23 if i != 0 { 24 stmt += ", " 25 } 26 stmt += QuoteIdentifier(col) 27 } 28 stmt += ") FROM STDIN" 29 return stmt 30 } 31 32 // CopyInSchema creates a COPY FROM statement which can be prepared with 33 // Tx.Prepare(). 34 func CopyInSchema(schema, table string, columns ...string) string { 35 stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " (" 36 for i, col := range columns { 37 if i != 0 { 38 stmt += ", " 39 } 40 stmt += QuoteIdentifier(col) 41 } 42 stmt += ") FROM STDIN" 43 return stmt 44 } 45 46 type copyin struct { 47 cn *conn 48 buffer []byte 49 rowData chan []byte 50 done chan bool 51 52 closed bool 53 54 sync.Mutex // guards err 55 err error 56 } 57 58 const ciBufferSize = 64 * 1024 59 60 // flush buffer before the buffer is filled up and needs reallocation 61 const ciBufferFlushSize = 63 * 1024 62 63 func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) { 64 if !cn.isInTransaction() { 65 return nil, errCopyNotSupportedOutsideTxn 66 } 67 68 ci := ©in{ 69 cn: cn, 70 buffer: make([]byte, 0, ciBufferSize), 71 rowData: make(chan []byte), 72 done: make(chan bool, 1), 73 } 74 // add CopyData identifier + 4 bytes for message length 75 ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0) 76 77 b := cn.writeBuf('Q') 78 b.string(q) 79 cn.send(b) 80 81 awaitCopyInResponse: 82 for { 83 t, r := cn.recv1() 84 switch t { 85 case 'G': 86 if r.byte() != 0 { 87 err = errBinaryCopyNotSupported 88 break awaitCopyInResponse 89 } 90 go ci.resploop() 91 return ci, nil 92 case 'H': 93 err = errCopyToNotSupported 94 break awaitCopyInResponse 95 case 'E': 96 err = parseError(r) 97 case 'Z': 98 if err == nil { 99 cn.bad = true 100 errorf("unexpected ReadyForQuery in response to COPY") 101 } 102 cn.processReadyForQuery(r) 103 return nil, err 104 default: 105 cn.bad = true 106 errorf("unknown response for copy query: %q", t) 107 } 108 } 109 110 // something went wrong, abort COPY before we return 111 b = cn.writeBuf('f') 112 b.string(err.Error()) 113 cn.send(b) 114 115 for { 116 t, r := cn.recv1() 117 switch t { 118 case 'c', 'C', 'E': 119 case 'Z': 120 // correctly aborted, we're done 121 cn.processReadyForQuery(r) 122 return nil, err 123 default: 124 cn.bad = true 125 errorf("unknown response for CopyFail: %q", t) 126 } 127 } 128 } 129 130 func (ci *copyin) flush(buf []byte) { 131 // set message length (without message identifier) 132 binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1)) 133 134 _, err := ci.cn.c.Write(buf) 135 if err != nil { 136 panic(err) 137 } 138 } 139 140 func (ci *copyin) resploop() { 141 for { 142 var r readBuf 143 t, err := ci.cn.recvMessage(&r) 144 if err != nil { 145 ci.cn.bad = true 146 ci.setError(err) 147 ci.done <- true 148 return 149 } 150 switch t { 151 case 'C': 152 // complete 153 case 'N': 154 // NoticeResponse 155 case 'Z': 156 ci.cn.processReadyForQuery(&r) 157 ci.done <- true 158 return 159 case 'E': 160 err := parseError(&r) 161 ci.setError(err) 162 default: 163 ci.cn.bad = true 164 ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t)) 165 ci.done <- true 166 return 167 } 168 } 169 } 170 171 func (ci *copyin) isErrorSet() bool { 172 ci.Lock() 173 isSet := (ci.err != nil) 174 ci.Unlock() 175 return isSet 176 } 177 178 // setError() sets ci.err if one has not been set already. Caller must not be 179 // holding ci.Mutex. 180 func (ci *copyin) setError(err error) { 181 ci.Lock() 182 if ci.err == nil { 183 ci.err = err 184 } 185 ci.Unlock() 186 } 187 188 func (ci *copyin) NumInput() int { 189 return -1 190 } 191 192 func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) { 193 return nil, ErrNotSupported 194 } 195 196 // Exec inserts values into the COPY stream. The insert is asynchronous 197 // and Exec can return errors from previous Exec calls to the same 198 // COPY stmt. 199 // 200 // You need to call Exec(nil) to sync the COPY stream and to get any 201 // errors from pending data, since Stmt.Close() doesn't return errors 202 // to the user. 203 func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) { 204 if ci.closed { 205 return nil, errCopyInClosed 206 } 207 208 if ci.cn.bad { 209 return nil, driver.ErrBadConn 210 } 211 defer ci.cn.errRecover(&err) 212 213 if ci.isErrorSet() { 214 return nil, ci.err 215 } 216 217 if len(v) == 0 { 218 return nil, ci.Close() 219 } 220 221 numValues := len(v) 222 for i, value := range v { 223 ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value) 224 if i < numValues-1 { 225 ci.buffer = append(ci.buffer, '\t') 226 } 227 } 228 229 ci.buffer = append(ci.buffer, '\n') 230 231 if len(ci.buffer) > ciBufferFlushSize { 232 ci.flush(ci.buffer) 233 // reset buffer, keep bytes for message identifier and length 234 ci.buffer = ci.buffer[:5] 235 } 236 237 return driver.RowsAffected(0), nil 238 } 239 240 func (ci *copyin) Close() (err error) { 241 if ci.closed { // Don't do anything, we're already closed 242 return nil 243 } 244 ci.closed = true 245 246 if ci.cn.bad { 247 return driver.ErrBadConn 248 } 249 defer ci.cn.errRecover(&err) 250 251 if len(ci.buffer) > 0 { 252 ci.flush(ci.buffer) 253 } 254 // Avoid touching the scratch buffer as resploop could be using it. 255 err = ci.cn.sendSimpleMessage('c') 256 if err != nil { 257 return err 258 } 259 260 <-ci.done 261 262 if ci.isErrorSet() { 263 err = ci.err 264 return err 265 } 266 return nil 267 }