package xugusql import ( "C" "database/sql/driver" "errors" "io" "reflect" "time" "unsafe" ) type Row struct { // Data information storage carrier of each column in the result set columns []xugusqlField // The name of each column in the result set of the current query names []string done bool } type xugusqlRows struct { // A context handle pointer, which can be used to obtain // information about the result set result unsafe.Pointer // The return value of the function cgo_xgc_read_next() lastRowRelt int // The return value of the function cgo_xgc_next_result() lastRelt int // Boolean value, used to identify whether the executed // SQL statement has been prepared prepared bool // Context connection handle pointer rows_conn unsafe.Pointer rowset Row } func (self *xugusqlRows) get_error() error { conn := self.rows_conn message := cgo_c_calloc(ERROR_BUFF_SIZE) defer func() { cgo_c_free(unsafe.Pointer(message)) }() var length C.int cgo_xgc_error(&conn, message, &length) return errors.New(C.GoString(message)) } /* * Columns returns the column names. * Columns returns an error if the rows are closed. */ func (self *xugusqlRows) Columns() []string { var FieldCount C.int result := self.result if self.rowset.names != nil { return self.rowset.names } re := cgo_xgc_get_fields_count(&result, &FieldCount) if re < 0 { return self.rowset.names } column_name := cgo_c_calloc(COLUMN_NAME_BUFF_SIZE) defer func() { cgo_c_free(unsafe.Pointer(column_name)) }() columns := make([]string, int(FieldCount)) fields := make([]xugusqlField, int(FieldCount)) for j := range columns { cgo_c_memset(column_name, COLUMN_NAME_BUFF_SIZE) re = cgo_xgc_get_column_name(&result, j+1, column_name) if re < 0 { return columns } columns[j] = C.GoString(column_name) fields[j].name = C.GoString(column_name) var dtype C.int re = cgo_xgc_get_column_type(&result, j+1, &dtype) if re < 0 { return columns } fields[j].fieldType = fieldType(dtype) } self.rowset.columns = fields self.rowset.names = columns return columns } func (self *xugusqlRows) Close() error { result := self.result self.rowset.columns = nil self.rowset.names = nil if result != nil { re := cgo_xgc_free_rowset(&result) if re < 0 { return self.get_error() } self.result = nil } return nil } // TODO(bradfitz): for now we need to defensively clone all // []byte that the driver returned (not permitting // *RawBytes in Rows.Scan), since we're about to close // the Rows in our defer, when we return from this function. // the contract with the driver.Next(...) interface is that it // can return slices into read-only temporary memory that's // only valid until the next Scan/Close. But the TODO is that // for a lot of drivers, this copy will be unnecessary. We // should provide an optional interface for drivers to // implement to say, "don't worry, the []bytes that I return // from Next will not be modified again." (for instance, if // they were obtained from the network anyway) But for now we // don't care. func (self *xugusqlRows) Next(dest []driver.Value) error { if self.result == nil { return errors.New("The result set has been released") } result := self.result self.lastRowRelt = cgo_xgc_read_next(&result) if self.lastRowRelt < 0 { return self.get_error() } if self.lastRowRelt == RET_NO_DATA { return io.EOF } pVal := cgo_c_calloc(FIELD_BUFF_SIZE) defer func() { cgo_c_free(unsafe.Pointer(pVal)) }() var FieldCount = len(self.rowset.names) var length C.int for j := 0; j < FieldCount; j++ { coluType := self.rowset.columns[j].fieldType switch coluType { case fieldTypeBinary, fieldTypeLob, fieldTypeClob, fieldTypeBlob: var pLob unsafe.Pointer cgo_xgc_new_lob(&pLob) re := cgo_xgc_get_lob(&result, j+1, int(coluType), &pLob, LOB_BUFF_SIZE, &length) if re < 0 && re != SQL_XG_C_NULL { return self.get_error() } dest[j] = make([]byte, int(length)+1) if re == SQL_XG_C_NULL { dest[j] = nil } else { data := make([]byte, int(length)) cgo_xgc_get_lob_data(&pLob, unsafe.Pointer(&data[0]), length) dest[j] = data } cgo_xgc_lob_distroy(&pLob) case fieldTypeDate: cgo_c_memset(pVal, FIELD_BUFF_SIZE) re := cgo_xgc_get_data(&result, j+1, int(fieldTypeChar), pVal, FIELD_BUFF_SIZE, &length) if re < 0 && re != SQL_XG_C_NULL { return self.get_error() } if re == SQL_XG_C_NULL { dest[j] = nil } else { //tzone, _ := time.LoadLocation("Asia/Shanghai") //tv, _ := time.ParseInLocation("2006-01-02", C.GoString(pVal), tzone) tv, _ := time.Parse("2006-01-02", C.GoString(pVal)) dest[j] = tv } case fieldTypeTime, fieldTypeTimeTZ: cgo_c_memset(pVal, FIELD_BUFF_SIZE) re := cgo_xgc_get_data(&result, j+1, int(fieldTypeChar), pVal, FIELD_BUFF_SIZE, &length) if re < 0 && re != SQL_XG_C_NULL { return self.get_error() } if re == SQL_XG_C_NULL { dest[j] = nil } else { //tzone, _ := time.LoadLocation("Asia/Shanghai") //tv, _ := time.ParseInLocation("15:04:05", C.GoString(pVal), tzone) tv, _ := time.Parse("15:04:05", C.GoString(pVal)) dest[j] = tv } case fieldTypeDatetime, fieldTypeDatetimeTZ: cgo_c_memset(pVal, FIELD_BUFF_SIZE) re := cgo_xgc_get_data(&result, j+1, int(fieldTypeChar), pVal, FIELD_BUFF_SIZE, &length) if re < 0 && re != SQL_XG_C_NULL { return self.get_error() } if re == SQL_XG_C_NULL { dest[j] = nil } else { //tzone, _ := time.LoadLocation("Asia/Shanghai") //tv, _ := time.ParseInLocation("2006-01-02 15:04:05", C.GoString(pVal), tzone) tv, _ := time.Parse("2006-01-02 15:04:05", C.GoString(pVal)) dest[j] = tv } default: cgo_c_memset(pVal, FIELD_BUFF_SIZE) re := cgo_xgc_get_data(&result, j+1, int(fieldTypeChar), pVal, FIELD_BUFF_SIZE, &length) if re < 0 && re != SQL_XG_C_NULL { return self.get_error() } dest[j] = make([]byte, int(length)+1) if re == SQL_XG_C_NULL { dest[j] = nil } else { dest[j] = []byte(C.GoString(pVal)) } } } return nil } // The driver is at the end of the current result set. // Test to see if there is another result set after the current one. // Only close Rows if there is no further result sets to read. func (self *xugusqlRows) HasNextResultSet() bool { result := self.result if self.prepared { return false } if self.lastRowRelt == RET_NO_DATA { self.lastRelt = cgo_xgc_next_result(&result) if self.lastRelt == RET_NO_DATA { return false } self.rowset.columns = nil self.rowset.names = nil self.result = result return true } return false } // NextResultSet prepares the next result set for reading. It reports whether // there is further result sets, or false if there is no further result set // or if there is an error advancing to it. The Err method should be consulted // to distinguish between the two cases. // // After calling NextResultSet, the Next method should always be called before // scanning. If there are further result sets they may not have rows in the result // set. func (self *xugusqlRows) NextResultSet() error { if self.result == nil { return errors.New("The result set has been released") } result := self.result if self.prepared { return io.EOF } if self.lastRelt == RET_NO_DATA { return io.EOF } self.result = result return nil } /* {{ */ /* {{ type ColumnTypeScanType interface }} */ func (self *xugusqlRows) ColumnTypeScanType(index int) reflect.Type { return self.rowset.columns[index].scanType() } /* {{ */ /* {{ RowsColumnTypeDatabaseTypeName }} */ func (self *xugusqlRows) ColumnTypeDatabaseTypeName(index int) string { return self.rowset.columns[index].typeDatabaseName() } /* {{ */ /* {{ RowsColumnTypeLength }} */ func (self *xugusqlRows) ColumnTypeLength(index int) (int64, bool) { return 0, false } /* {{ */ /* {{ RowsColumnTypeNullable */ func (self *xugusqlRows) ColumnTypeNullable(index int) (nullable, ok bool) { /* not support */ return false, false } /* {{ */ /* {{ RowsColumnTypePrecisionScale */ func (self *xugusqlRows) ColumnTypePrecisionScale(index int) (int64, int64, bool) { /* not support */ return 0, 0, false }