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 := &copyin{
    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  }