// SetFileName sets the directory alias and file name for the BFILE lob. func (lv *ExternalLobVar) SetFileName(dirAlias, name string) error { var err error // create a string for retrieving the value if err = lv.Verify(); err != nil { return errgo.Mask(err) } nameB := []byte(name) dirAliasB := []byte(dirAlias) if CTrace { ctrace("OCILobSetFilename(conn=%p, lob=%x, dirAlias=%s, dirAliasLen=%d, name=%s, nameLen=%d)", lv.lobVar.connection.handle, lv.getHandleBytes(), dirAliasB, len(dirAlias), nameB, len(nameB)) } lob := lv.getHandle() if err = lv.lobVar.environment.CheckStatus( C.OCILobFileSetName(lv.lobVar.environment.handle, lv.lobVar.environment.errorHandle, &lob, (*C.OraText)(&dirAliasB[0]), C.ub2(len(dirAliasB)), (*C.OraText)(&nameB[0]), C.ub2(len(nameB))), "LobFileSetName"); err != nil { return errgo.Mask(err) } return nil }
func (bnd *bndBfile) bind(value Bfile, position int, stmt *Stmt) error { // DirectoryAlias must be specified to avoid error "ORA-24801: illegal parameter value in OCI lob function" // Raising a driver error clarifies the user error if value.DirectoryAlias == "" { return errNew("DirectoryAlias must be specified when binding a non-null Bfile") } // Filename must be specified to avoid error "ORA-24801: illegal parameter value in OCI lob function" // Raising a driver error clarifies the user error if value.Filename == "" { return errNew("Filename must be specified when binding a non-null Bfile") } bnd.stmt = stmt // Allocate lob locator handle r := C.OCIDescriptorAlloc( unsafe.Pointer(bnd.stmt.ses.srv.env.ocienv), //CONST dvoid *parenth, (*unsafe.Pointer)(unsafe.Pointer(&bnd.ociLobLocator)), //dvoid **descpp, C.OCI_DTYPE_FILE, //ub4 type, 0, //size_t xtramem_sz, nil) //dvoid **usrmempp); if r == C.OCI_ERROR { return bnd.stmt.ses.srv.env.ociError() } else if r == C.OCI_INVALID_HANDLE { return errNew("unable to allocate oci lob handle during bind") } bnd.cDirectoryAlias = C.CString(value.DirectoryAlias) bnd.cFilename = C.CString(value.Filename) r = C.OCILobFileSetName( bnd.stmt.ses.srv.env.ocienv, //OCIEnv *envhp, bnd.stmt.ses.srv.env.ocierr, //OCIError *errhp, &bnd.ociLobLocator, //OCILobLocator **filepp, (*C.OraText)(unsafe.Pointer(bnd.cDirectoryAlias)), //const OraText *dir_alias, C.ub2(len(value.DirectoryAlias)), //ub2 d_length, (*C.OraText)(unsafe.Pointer(bnd.cFilename)), //const OraText *filename, C.ub2(len(value.Filename))) //ub2 f_length ); if r == C.OCI_ERROR { return bnd.stmt.ses.srv.env.ociError() } r = C.OCIBINDBYPOS( bnd.stmt.ocistmt, //OCIStmt *stmtp, (**C.OCIBind)(&bnd.ocibnd), //OCIBind **bindpp, bnd.stmt.ses.srv.env.ocierr, //OCIError *errhp, C.ub4(position), //ub4 position, unsafe.Pointer(&bnd.ociLobLocator), //void *valuep, C.LENGTH_TYPE(unsafe.Sizeof(bnd.ociLobLocator)), //sb8 value_sz, C.SQLT_FILE, //ub2 dty, nil, //void *indp, nil, //ub2 *alenp, nil, //ub2 *rcodep, 0, //ub4 maxarr_len, nil, //ub4 *curelep, C.OCI_DEFAULT) //ub4 mode ); if r == C.OCI_ERROR { return bnd.stmt.ses.srv.env.ociError() } return nil }
// WriteAt writes data in p into the LOB, starting at off. func (lrw *lobReadWriter) WriteAt(p []byte, off int64) (n int, err error) { //Log.Infof("LobWrite2 off=%d len=%d", off, n) byte_amtp := C.oraub8(len(p)) // Write to Oracle if C.OCILobWrite2( lrw.ses.ocisvcctx, //OCISvcCtx *svchp, lrw.ses.srv.env.ocierr, //OCIError *errhp, lrw.ociLobLocator, //OCILobLocator *locp, &byte_amtp, //oraub8 *byte_amtp, nil, //oraub8 *char_amtp, C.oraub8(off)+1, //oraub8 offset, starting position is 1 unsafe.Pointer(&p[0]), //void *bufp, C.oraub8(len(p)), C.OCI_ONE_PIECE, //ub1 piece, nil, //void *ctxp, nil, //OCICallbackLobWrite2 (cbfp) C.ub2(0), //ub2 csid, C.SQLCS_IMPLICIT, //ub1 csfrm ); //fmt.Printf("r %v, current %v, buffer %v\n", r, current, buffer) //fmt.Printf("C.OCI_NEED_DATA %v, C.OCI_SUCCESS %v\n", C.OCI_NEED_DATA, C.OCI_SUCCESS) ) == C.OCI_ERROR { return 0, lrw.ses.srv.env.ociError() } if C.oraub8(off)+byte_amtp > lrw.size { lrw.size = C.oraub8(off) + byte_amtp } return int(byte_amtp), nil }
// ReadAt reads into p, starting from off. func (lrw *lobReadWriter) ReadAt(p []byte, off int64) (n int, err error) { byte_amtp := C.oraub8(len(p)) //Log.Infof("LobRead2 off=%d amt=%d", off, len(p)) r := C.OCILobRead2( lrw.ses.ocisvcctx, //OCISvcCtx *svchp, lrw.ses.srv.env.ocierr, //OCIError *errhp, lrw.ociLobLocator, //OCILobLocator *locp, &byte_amtp, //oraub8 *byte_amtp, nil, //oraub8 *char_amtp, C.oraub8(off)+1, //oraub8 offset, offset is 1-based unsafe.Pointer(&p[0]), //void *bufp, C.oraub8(len(p)), //oraub8 bufl, C.OCI_ONE_PIECE, //ub1 piece, nil, //void *ctxp, nil, //OCICallbackLobRead2 (cbfp) C.ub2(0), //ub2 csid, lrw.charsetForm, //ub1 csfrm ); ) //Log.Infof("LobRead2 returned %d amt=%d", r, byte_amtp) switch r { case C.OCI_ERROR: return 0, lrw.ses.srv.env.ociError() case C.OCI_NO_DATA: return int(byte_amtp), io.EOF case C.OCI_INVALID_HANDLE: return 0, fmt.Errorf("Invalid handle %v", lrw.ociLobLocator) } return int(byte_amtp), nil }
// GetCharacterSetName retrieves the IANA character set name for the attribute. func (env *Environment) GetCharacterSetName(attribute uint32) (string, error) { //, overrideValue string var ( charsetID, vsize C.ub4 status C.sword err error ) /* // if override value specified, use it if (overrideValue) { *result = PyMem_Malloc(strlen(overrideValue) + 1); if (!*result) return -1; strcpy(*result, overrideValue); return 0; } */ // get character set id status = C.OCIAttrGet(unsafe.Pointer(env.handle), //void *trgthndlp C.OCI_HTYPE_ENV, //ub4 trghndltyp unsafe.Pointer(&charsetID), //void *attributep &vsize, //ub4 *sizep C.ub4(attribute), //ub4 attrtype env.errorHandle) //OCIError *errhp if err = env.CheckStatus(status, "GetCharacterSetName[get charset id]"); err != nil { return "", errgo.Mask(err) } charsetName := make([]byte, C.OCI_NLS_MAXBUFSZ) ianaCharsetName := make([]byte, C.OCI_NLS_MAXBUFSZ) // get character set name if err = env.CheckStatus(C.OCINlsCharSetIdToName(unsafe.Pointer(env.handle), (*C.oratext)(&charsetName[0]), C.OCI_NLS_MAXBUFSZ, C.ub2(charsetID)), "GetCharacterSetName[get Oracle charset name]"); err != nil { return "", errgo.Mask( // get IANA character set name err) } status = C.OCINlsNameMap(unsafe.Pointer(env.handle), (*C.oratext)(&ianaCharsetName[0]), C.OCI_NLS_MAXBUFSZ, (*C.oratext)(&charsetName[0]), C.OCI_NLS_CS_ORA_TO_IANA) if err = env.CheckStatus(status, "GetCharacterSetName[translate NLS charset]"); err != nil { return "", errgo.Mask( // store results // oratext = unsigned char err) } return string(ianaCharsetName), nil }
// WriteTo writes all data from the LOB into the given Writer. func (lr *lobReader) WriteTo(w io.Writer) (n int64, err error) { defer func() { if closeErr := lr.Close(); closeErr != nil && err == nil { err = closeErr } }() var byte_amtp C.oraub8 // zero arr := lobChunkPool.Get().([lobChunkSize]byte) defer lobChunkPool.Put(arr) buf := arr[:] var k int for { //Log.Infof("WriteTo LobRead2 off=%d amt=%d", lr.off, len(buf)) r := C.OCILobRead2( lr.ses.ocisvcctx, //OCISvcCtx *svchp, lr.ses.srv.env.ocierr, //OCIError *errhp, lr.ociLobLocator, //OCILobLocator *locp, &byte_amtp, //oraub8 *byte_amtp, nil, //oraub8 *char_amtp, lr.off+1, //oraub8 offset, offset is 1-based unsafe.Pointer(&buf[0]), //void *bufp, C.oraub8(len(buf)), //oraub8 bufl, lr.piece, //ub1 piece, nil, //void *ctxp, nil, //OCICallbackLobRead2 (cbfp) C.ub2(0), //ub2 csid, lr.charsetForm, //ub1 csfrm ); ) //Log.Infof("WriteTo LobRead2 returned %d amt=%d piece=%d", r, byte_amtp, lr.piece) switch r { case C.OCI_SUCCESS: case C.OCI_NO_DATA: break default: return 0, lr.ses.srv.env.ociError() } // byte_amtp represents the amount copied into buffer by oci lr.off += byte_amtp if byte_amtp != 0 { if k, err = w.Write(buf[:int(byte_amtp)]); err != nil { return n, err } n += int64(k) if lr.off == lr.Length { break } } if lr.piece == C.OCI_FIRST_PIECE { lr.piece = C.OCI_NEXT_PIECE } } return n, nil }
// SetFileName sets the directory alias and file name for the BFILE lob. func (lv *ExternalLobVar) SetFileName(dirAlias, name string) error { var err error // create a string for retrieving the value if err = lv.Verify(); err != nil { return err } nameB := []byte(name) dirAliasB := []byte(dirAlias) if err = lv.lobVar.environment.CheckStatus( C.OCILobFileSetName(lv.lobVar.environment.handle, lv.lobVar.environment.errorHandle, (**C.OCILobLocator)(unsafe.Pointer(&lv.lobVar.dataBytes[lv.pos*lv.lobVar.size])), (*C.OraText)(&dirAliasB[0]), C.ub2(len(dirAliasB)), (*C.OraText)(&nameB[0]), C.ub2(len(nameB))), "LobFileSetName"); err != nil { return err } return nil }
// Read into p, the next chunk. func (lr *lobReader) Read(p []byte) (n int, err error) { if lr.ociLobLocator == nil { return 0, io.EOF } defer func() { if err != nil { lr.Close() } }() var byte_amtp C.oraub8 // zero //Log.Infof("LobRead2 piece=%d off=%d amt=%d", lr.piece, lr.off, len(p)) r := C.OCILobRead2( lr.ses.ocisvcctx, //OCISvcCtx *svchp, lr.ses.srv.env.ocierr, //OCIError *errhp, lr.ociLobLocator, //OCILobLocator *locp, &byte_amtp, //oraub8 *byte_amtp, nil, //oraub8 *char_amtp, lr.off+1, //oraub8 offset, offset is 1-based unsafe.Pointer(&p[0]), //void *bufp, C.oraub8(len(p)), //oraub8 bufl, lr.piece, //ub1 piece, nil, //void *ctxp, nil, //OCICallbackLobRead2 (cbfp) C.ub2(0), //ub2 csid, lr.charsetForm, //ub1 csfrm ); ) //Log.Infof("LobRead2 returned %d amt=%d", r, byte_amtp) switch r { case C.OCI_ERROR: lr.interrupted = true return 0, lr.ses.srv.env.ociError() case C.OCI_NO_DATA: return int(byte_amtp), io.EOF case C.OCI_INVALID_HANDLE: return 0, fmt.Errorf("Invalid handle %v", lr.ociLobLocator) } // byte_amtp represents the amount copied into buffer by oci if byte_amtp != 0 { lr.off += byte_amtp if lr.off == lr.Length { return int(byte_amtp), io.EOF } if lr.piece == C.OCI_FIRST_PIECE { lr.piece = C.OCI_NEXT_PIECE } } return int(byte_amtp), nil }
func (def *defBfile) value() (value interface{}, err error) { var bfileValue Bfile bfileValue.IsNull = def.null < C.sb2(0) if !bfileValue.IsNull { // Get directory alias and filename dLength := C.ub2(len(def.directoryAlias)) fLength := C.ub2(len(def.filename)) r := C.OCILobFileGetName( def.rset.stmt.ses.srv.env.ocienv, //OCIEnv *envhp, def.rset.stmt.ses.srv.env.ocierr, //OCIError *errhp, def.ociLobLocator, //const OCILobLocator *filep, (*C.OraText)(unsafe.Pointer(&def.directoryAlias[0])), //OraText *dir_alias, &dLength, //ub2 *d_length, (*C.OraText)(unsafe.Pointer(&def.filename[0])), //OraText *filename, &fLength) //ub2 *f_length ); if r == C.OCI_ERROR { return value, def.rset.stmt.ses.srv.env.ociError() } bfileValue.DirectoryAlias = string(def.directoryAlias[:int(dLength)]) bfileValue.Filename = string(def.filename[:int(fLength)]) } value = bfileValue return value, err }
func (def *defLob) Bytes() (value []byte, err error) { // Open the lob to obtain length; round-trip to database //Log.Infof("Bytes OCILobOpen %p", def.ociLobLocator) lobLength, err := lobOpen(def.rset.stmt.ses, def.ociLobLocator, C.OCI_LOB_READONLY) if err != nil { def.ociLobLocator = nil return nil, err } defer func() { //Log.Infof("Bytes OCILobClose %p", def.ociLobLocator) if closeErr := lobClose(def.rset.stmt.ses, def.ociLobLocator); closeErr != nil && err == nil { err = closeErr } }() if lobLength == 0 { return nil, nil } // Allocate []byte the length of the lob value = make([]byte, int(lobLength)) for off, byte_amtp := 0, lobLength; byte_amtp > 0; byte_amtp = lobLength - C.oraub8(off) { //Log.Infof("LobRead2 off=%d amt=%d", off, byte_amtp) r := C.OCILobRead2( def.rset.stmt.ses.ocisvcctx, //OCISvcCtx *svchp, def.rset.stmt.ses.srv.env.ocierr, //OCIError *errhp, def.ociLobLocator, //OCILobLocator *locp, &byte_amtp, //oraub8 *byte_amtp, nil, //oraub8 *char_amtp, C.oraub8(off+1), //oraub8 offset, offset is 1-based unsafe.Pointer(&value[off]), //void *bufp, C.oraub8(lobChunkSize), //oraub8 bufl, C.OCI_ONE_PIECE, //ub1 piece, nil, //void *ctxp, nil, //OCICallbackLobRead2 (cbfp) C.ub2(0), //ub2 csid, def.charsetForm) //ub1 csfrm ); if r == C.OCI_ERROR { return nil, def.rset.stmt.ses.srv.env.ociError() } // byte_amtp represents the amount copied into buffer by oci off += int(byte_amtp) } return value, nil }
// Set the value of the variable. func stringVarSetValue(v *Variable, pos uint, value interface{}) (err error) { var ( text string buf []byte ok bool length int ) if text, ok = value.(string); !ok { if buf, ok = value.([]byte); !ok { if arr, ok := value.([]string); ok { for i := range arr { if err = stringVarSetValue(v, pos+uint(i), arr[i]); err != nil { return fmt.Errorf("error setting pos=%d + %d. element: %s", pos, i, err) } } return nil } else if arr, ok := value.([]byte); ok { for i := range arr { if err = stringVarSetValue(v, pos+uint(i), arr[i]); err != nil { return fmt.Errorf("error setting pos=%d + %d. element: %s", pos, i, err) } } return nil } // return fmt.Errorf("string or []byte required, got %T", value) log.Panicf("string or []byte required, got %T", value) } else { if v.typ.isCharData { text = string(buf) length = len(text) } else { length = len(buf) } } } else { if v.typ.isCharData { length = len(text) } else { length = len(buf) } buf = []byte(text) } if v.typ.isCharData && length > MaxStringChars { return errors.New("string data too large") } else if !v.typ.isCharData && length > MaxBinaryBytes { return errors.New("binary data too large") } // ensure that the buffer is large enough if length > int(v.bufferSize) { if err := v.resize(uint(length)); err != nil { return err } } // keep a copy of the string v.actualLength[pos] = C.ub2(length) if length > 0 { copy(v.dataBytes[int(v.bufferSize*pos):], buf) } return nil }
func writeLob(ociLobLocator *C.OCILobLocator, stmt *Stmt, r io.Reader, lobBufferSize int) error { var actBuf, nextBuf []byte if lobChunkSize >= lobBufferSize { arr := lobChunkPool.Get().([lobChunkSize]byte) defer lobChunkPool.Put(arr) actBuf = arr[:lobBufferSize] arr = lobChunkPool.Get().([lobChunkSize]byte) defer lobChunkPool.Put(arr) nextBuf = arr[:lobBufferSize] } else { actBuf = make([]byte, lobBufferSize) nextBuf = make([]byte, lobBufferSize) } // write bytes to lob locator - at once, as we already have all bytes in memory var n int var byte_amtp, off C.oraub8 var actPiece, nextPiece C.ub1 = C.OCI_FIRST_PIECE, C.OCI_NEXT_PIECE // OCILobWrite2 doesn't support writing zero bytes // nor is writing 1 byte and erasing the one byte supported // therefore, throw an error var err error if n, err = io.ReadFull(r, actBuf); err != nil { switch err { case io.EOF: // no bytes read return errNew("writing a zero-length BLOB is unsupported") case io.ErrUnexpectedEOF: actPiece = C.OCI_ONE_PIECE default: return err } actBuf = actBuf[:n] } for { n = len(actBuf) if n == lobBufferSize { var n2 int if n2, err = io.ReadFull(r, nextBuf[:]); err != nil { switch err { case io.EOF: // no bytes read, lobSize == len(buffer[0]) if actPiece == C.OCI_FIRST_PIECE { actPiece = C.OCI_ONE_PIECE } else { actPiece = C.OCI_LAST_PIECE } case io.ErrUnexpectedEOF: nextPiece = C.OCI_LAST_PIECE default: return err } nextBuf = nextBuf[:n2] } } //Log.Infof("LobWrite2 off=%d len=%d piece=%d", off, n, actPiece) byte_amtp = 0 if actPiece == C.OCI_ONE_PIECE { byte_amtp = C.oraub8(n) } // Write to Oracle if C.OCILobWrite2( stmt.ses.srv.ocisvcctx, //OCISvcCtx *svchp, stmt.ses.srv.env.ocierr, //OCIError *errhp, ociLobLocator, //OCILobLocator *locp, &byte_amtp, //oraub8 *byte_amtp, nil, //oraub8 *char_amtp, off+1, //oraub8 offset, starting position is 1 unsafe.Pointer(&actBuf[0]), //void *bufp, C.oraub8(n), actPiece, //ub1 piece, nil, //void *ctxp, nil, //OCICallbackLobWrite2 (cbfp) C.ub2(0), //ub2 csid, C.SQLCS_IMPLICIT, //ub1 csfrm ); //fmt.Printf("r %v, current %v, buffer %v\n", r, current, buffer) //fmt.Printf("C.OCI_NEED_DATA %v, C.OCI_SUCCESS %v\n", C.OCI_NEED_DATA, C.OCI_SUCCESS) ) == C.OCI_ERROR { return stmt.ses.srv.env.ociError() } off += byte_amtp if actPiece == C.OCI_LAST_PIECE || actPiece == C.OCI_ONE_PIECE { break } actPiece, actBuf = nextPiece, nextBuf } return nil }
// Set the value of the variable. func stringVarSetValue(v *Variable, pos uint, value interface{}) (err error) { if value == nil { v.actualLength[pos] = C.ub2(0) return nil } var ( buf []byte length int ) switch x := value.(type) { case string: buf = []byte(x) length = len(buf) case []byte: buf = x length = len(buf) case []string: for i := range x { if err = stringVarSetValue(v, pos+uint(i), x[i]); err != nil { return errgo.Newf("error setting pos=%d + %d. element: %s", pos, i, err) } } return nil case [][]byte: for i := range x { if err = stringVarSetValue(v, pos+uint(i), x[i]); err != nil { return errgo.Newf("error setting pos=%d + %d. element: %s", pos, i, err) } } return nil case *Variable: switch { case x == nil: case x.actualLength != nil: length = int(x.actualLength[pos]) buf = v.dataBytes[int(v.bufferSize*pos):] default: length = len(v.dataBytes) / int(v.bufferSize) buf = v.dataBytes } default: log.Panicf("string or []byte required, got %T", value) } if v.typ.isCharData && length > MaxStringChars { return errgo.New("string data too large") } else if !v.typ.isCharData && length > MaxBinaryBytes { return errgo.New("binary data too large") } // ensure that the buffer is large enough if length > int(v.bufferSize) { if err := v.resize(uint(length)); err != nil { return errgo.Mask(err) } } // keep a copy of the string v.actualLength[pos] = C.ub2(length) if length > 0 { copy(v.dataBytes[int(v.bufferSize*pos):], buf) } return nil }
func (s *OCI8Stmt) bind(args []driver.Value) (freeBoundParameters func(), err error) { if args == nil { return func() {}, nil } var ( bp *C.OCIBind dty int data []byte cdata *C.char boundParameters []*C.char ) freeBoundParameters = func() { for _, p := range boundParameters { C.free(unsafe.Pointer(p)) } } for i, v := range args { data = []byte{} switch v.(type) { case nil: dty = C.SQLT_STR data = []byte{0} case time.Time: dty = C.SQLT_DAT now := v.(time.Time).In(s.c.location) //TODO Handle BCE dates (http://docs.oracle.com/cd/B12037_01/appdev.101/b10779/oci03typ.htm#438305) //TODO Handle timezones (http://docs.oracle.com/cd/B12037_01/appdev.101/b10779/oci03typ.htm#443601) data = []byte{ byte(now.Year()/100 + 100), byte(now.Year()%100 + 100), byte(now.Month()), byte(now.Day()), byte(now.Hour() + 1), byte(now.Minute() + 1), byte(now.Second() + 1), } default: dty = C.SQLT_STR data = []byte(fmt.Sprintf("%v", v)) data = append(data, 0) } cdata = C.CString(string(data)) boundParameters = append(boundParameters, cdata) rv := C.OCIBindByPos( (*C.OCIStmt)(s.s), &bp, (*C.OCIError)(s.c.err), C.ub4(i+1), unsafe.Pointer(cdata), C.sb4(len(data)), C.ub2(dty), nil, nil, nil, 0, nil, C.OCI_DEFAULT) if rv == C.OCI_ERROR { defer freeBoundParameters() return nil, ociGetError(s.c.err) } } return freeBoundParameters, nil }