Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds string dimension support #108

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 138 additions & 123 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,65 +254,60 @@ func (a *Array) QueryType() (QueryType, error) {
return QueryType(queryType), nil
}

// makeNonEmptyDomain creates a []NonEmptyDomain from a generic domain-typed slice
func makeNonEmptyDomain(domain *Domain, domainSlice interface{}) ([]NonEmptyDomain, error) {
domainType, err := domain.Type()
// getNonEmptyDomainForDim creates a NonEmptyDomain from a generic dimension-typed slice
func getNonEmptyDomainForDim(dimension *Dimension, dimensionSlice interface{}) (*NonEmptyDomain, error) {
dimensionType, err := dimension.Type()
if err != nil {
return nil, err
}
ndims, err := domain.NDim()

name, err := dimension.Name()
if err != nil {
return nil, err
}

nonEmptyDomains := make([]NonEmptyDomain, 0)
for i := uint(0); i < ndims; i++ {
dimension, err := domain.DimensionFromIndex(i)
if err != nil {
return nil, err
}
name, err := dimension.Name()
if err != nil {
return nil, err
}

switch domainType {
case TILEDB_INT8:
tmpDomain := domainSlice.([]int8)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []int8{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_INT16:
tmpDomain := domainSlice.([]int16)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []int16{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_INT32:
tmpDomain := domainSlice.([]int32)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []int32{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_INT64:
tmpDomain := domainSlice.([]int64)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []int64{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_UINT8:
tmpDomain := domainSlice.([]uint8)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []uint8{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_UINT16:
tmpDomain := domainSlice.([]uint16)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []uint16{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_UINT32:
tmpDomain := domainSlice.([]uint32)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []uint32{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_UINT64:
tmpDomain := domainSlice.([]uint64)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []uint64{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_FLOAT32:
tmpDomain := domainSlice.([]float32)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []float32{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_FLOAT64:
tmpDomain := domainSlice.([]float64)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []float64{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
default:
return nil, fmt.Errorf("error creating non empty domain: unknown domain type")
}
var nonEmptyDomain NonEmptyDomain
switch dimensionType {
case TILEDB_INT8:
tmpDimension := dimensionSlice.([]int8)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []int8{tmpDimension[0], tmpDimension[1]}}
case TILEDB_INT16:
tmpDimension := dimensionSlice.([]int16)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []int16{tmpDimension[0], tmpDimension[1]}}
case TILEDB_INT32:
tmpDimension := dimensionSlice.([]int32)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []int32{tmpDimension[0], tmpDimension[1]}}
case TILEDB_INT64:
tmpDimension := dimensionSlice.([]int64)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []int64{tmpDimension[0], tmpDimension[1]}}
case TILEDB_UINT8:
tmpDimension := dimensionSlice.([]uint8)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []uint8{tmpDimension[0], tmpDimension[1]}}
case TILEDB_UINT16:
tmpDimension := dimensionSlice.([]uint16)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []uint16{tmpDimension[0], tmpDimension[1]}}
case TILEDB_UINT32:
tmpDimension := dimensionSlice.([]uint32)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []uint32{tmpDimension[0], tmpDimension[1]}}
case TILEDB_UINT64:
tmpDimension := dimensionSlice.([]uint64)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []uint64{tmpDimension[0], tmpDimension[1]}}
case TILEDB_FLOAT32:
tmpDimension := dimensionSlice.([]float32)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []float32{tmpDimension[0], tmpDimension[1]}}
case TILEDB_FLOAT64:
tmpDimension := dimensionSlice.([]float64)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []float64{tmpDimension[0], tmpDimension[1]}}
case TILEDB_STRING_ASCII:
tmpDimension := dimensionSlice.([]interface{})
lowBound := tmpDimension[0].([]uint8)
highBound := tmpDimension[1].([]uint8)
nonEmptyDomain = NonEmptyDomain{DimensionName: name, Bounds: []string{string(lowBound), string(highBound)}}
default:
return nil, fmt.Errorf("error creating non empty domain: unknown dimension type")
}

return nonEmptyDomains, nil
return &nonEmptyDomain, nil
}

// NonEmptyDomain retrieves the non-empty domain from an array
Expand All @@ -328,56 +323,82 @@ func (a *Array) NonEmptyDomain() ([]NonEmptyDomain, bool, error) {
return nil, false, err
}

domainType, err := domain.Type()
if err != nil {
return nil, false, err
}

ndims, err := domain.NDim()
if err != nil {
return nil, false, err
}

tmpDomain, tmpDomainPtr, err := domainType.MakeSlice(uint64(2 * ndims))
if err != nil {
return nil, false, err
}
isDomainEmpty := true
nonEmptyDomains := make([]NonEmptyDomain, 0)
for dimIdx := uint(0); dimIdx < ndims; dimIdx++ {
dimension, err := domain.DimensionFromIndex(dimIdx)
if err != nil {
return nil, false, err
}

var isEmpty C.int32_t
ret := C.tiledb_array_get_non_empty_domain(a.context.tiledbContext, a.tiledbArray, tmpDomainPtr, &isEmpty)
if ret != C.TILEDB_OK {
return nil, false, fmt.Errorf("Error in getting non empty domain for array: %s", a.context.LastError())
}
dimensionType, err := dimension.Type()
if err != nil {
return nil, false, err
}

if isEmpty == 1 {
return nil, true, nil
} else {
nonEmptyDomains, err := makeNonEmptyDomain(domain, tmpDomain)
tmpDimension, tmpDimensionPtr, err := dimensionType.MakeSlice(uint64(2))
if err != nil {
return nil, false, err
}
return nonEmptyDomains, false, nil

var isEmpty C.int32_t
ret := C.tiledb_array_get_non_empty_domain_from_index(
a.context.tiledbContext,
a.tiledbArray,
(C.uint32_t)(dimIdx),
tmpDimensionPtr, &isEmpty)
if ret != C.TILEDB_OK {
return nil, false, fmt.Errorf("Error in getting non empty domain for dimension: %s", a.context.LastError())
}

if isEmpty == 1 {
continue
} else {
// If at least one domain for a dimension is empty the union of domains is non-empty
isDomainEmpty = false
nonEmptyDomain, err := getNonEmptyDomainForDim(dimension, tmpDimension)
if err != nil {
return nil, false, err
}
nonEmptyDomains = append(nonEmptyDomains, *nonEmptyDomain)
}
}

if isDomainEmpty {
return nil, isDomainEmpty, nil
}

return nonEmptyDomains, isDomainEmpty, nil
}

// NonEmptyDomainVarFromName retrieves the non-empty domain from an array for a
// given dimension name.
// This returns the bounding coordinates for the dimension
func (a *Array) NonEmptyDomainVarFromName(dimName string) ([]NonEmptyDomain, []NonEmptyDomain, bool, error) {
// given var-sized dimension name. Supports only TILEDB_STRING_ASCII type
// Returns the bounding coordinates for the dimension
func (a *Array) NonEmptyDomainVarFromName(dimName string) (*NonEmptyDomain, bool, error) {

schema, err := a.Schema()
if err != nil {
return nil, nil, false, err
return nil, false, err
}

domain, err := schema.Domain()
if err != nil {
return nil, nil, false, err
return nil, false, err
}

dimension, err := domain.DimensionFromName(dimName)
if err != nil {
return nil, false, fmt.Errorf("Could not get dimension: %s", dimName)
}

domainType, err := domain.Type()
dimType, err := dimension.Type()
if err != nil {
return nil, nil, false, err
return nil, false, err
}

cDimName := C.CString(dimName)
Expand All @@ -393,62 +414,56 @@ func (a *Array) NonEmptyDomainVarFromName(dimName string) ([]NonEmptyDomain, []N
var cstart unsafe.Pointer
var cend unsafe.Pointer

switch domainType {
case TILEDB_STRING_ASCII:
ret := C.tiledb_array_get_non_empty_domain_var_size_from_name(
a.context.tiledbContext,
a.tiledbArray,
cDimName,
&cstartSize,
&cendSize,
&isEmpty)
if ret != C.TILEDB_OK {
return nil, nil, false, fmt.Errorf("Error in getting non empty domain size for dimension %s for array: %s", dimName, a.context.LastError())
}
ret := C.tiledb_array_get_non_empty_domain_var_size_from_name(
a.context.tiledbContext,
a.tiledbArray,
cDimName,
&cstartSize,
&cendSize,
&isEmpty)
if ret != C.TILEDB_OK {
return nil, false, fmt.Errorf("Error in getting non empty domain size for dimension %s for array: %s", dimName, a.context.LastError())
}

if isEmpty == 1 {
return nil, nil, true, nil
}
if isEmpty == 1 {
return nil, true, nil
}

start, cstart, err = domainType.MakeSlice(uint64(cstartSize))
if err != nil {
return nil, nil, false, err
}
bounds := make([]interface{}, 0)

end, cend, err = domainType.MakeSlice(uint64(cendSize))
if err != nil {
return nil, nil, false, err
}
start, cstart, err = dimType.MakeSlice(uint64(cstartSize))
if err != nil {
return nil, false, err
}
bounds = append(bounds, start)

ret = C.tiledb_array_get_non_empty_domain_var_from_name(
a.context.tiledbContext,
a.tiledbArray,
cDimName,
cstart,
cend,
&isEmpty)
if ret != C.TILEDB_OK {
return nil, nil, false, fmt.Errorf("Error in getting non empty domain for dimension %s for array: %s", dimName, a.context.LastError())
}
end, cend, err = dimType.MakeSlice(uint64(cendSize))
if err != nil {
return nil, false, err
}
bounds = append(bounds, end)

if isEmpty == 1 {
return nil, nil, true, nil
} else {
nonEmptyDomainsStart, err := makeNonEmptyDomain(domain, start)
if err != nil {
return nil, nil, false, err
}
ret = C.tiledb_array_get_non_empty_domain_var_from_name(
a.context.tiledbContext,
a.tiledbArray,
cDimName,
cstart,
cend,
&isEmpty)
if ret != C.TILEDB_OK {
return nil, false, fmt.Errorf("Error in getting non empty domain for dimension %s for array: %s", dimName, a.context.LastError())
}

nonEmptyDomainsEnd, err := makeNonEmptyDomain(domain, end)
if err != nil {
return nil, nil, false, err
}
if isEmpty == 1 {
return nil, true, nil
}

return nonEmptyDomainsStart, nonEmptyDomainsEnd, false, nil
}
nonEmptyDomain, err := getNonEmptyDomainForDim(dimension, bounds)
if err != nil {
return nil, false, err
}

return nil, nil, false, nil
return nonEmptyDomain, false, nil
}

// MaxBufferSize computes the upper bound on the buffer size (in bytes)
Expand Down
28 changes: 28 additions & 0 deletions dimension.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,30 @@ func NewDimension(context *Context, name string, domain interface{}, extent inte
return &dimension, nil
}

// NewStringDimension alloc a new string dimension
func NewStringDimension(context *Context, name string) (*Dimension, error) {
dimension := Dimension{context: context}
var cname *C.char = C.CString(name)
defer C.free(unsafe.Pointer(cname))

var datatype Datatype
var ret C.int32_t

datatype = TILEDB_STRING_ASCII
ret = C.tiledb_dimension_alloc(context.tiledbContext, cname, C.tiledb_datatype_t(datatype), nil, nil, &dimension.tiledbDimension)

if ret != C.TILEDB_OK {
return nil, fmt.Errorf("Error creating tiledb dimension: %s", context.LastError())
}

// Set finalizer for free C pointer on gc
runtime.SetFinalizer(&dimension, func(dimension *Dimension) {
dimension.Free()
})

return &dimension, nil
}

// Free tiledb_dimension_t that was allocated on heap in c
func (d *Dimension) Free() {
if d.tiledbDimension != nil {
Expand Down Expand Up @@ -344,6 +368,8 @@ func (d *Dimension) Domain() (interface{}, error) {
tmpDomain[i] = float64(s)
}
domain = tmpDomain
case TILEDB_STRING_ASCII:
domain = nil
default:
return nil, fmt.Errorf("Unrecognized domain type: %d", datatype)
}
Expand Down Expand Up @@ -414,6 +440,8 @@ func (d *Dimension) Extent() (interface{}, error) {
defer C.free(cextent)
ret = C.tiledb_dimension_get_tile_extent(d.context.tiledbContext, d.tiledbDimension, &cextent)
extent = *(*float64)(unsafe.Pointer(cextent))
case TILEDB_STRING_ASCII:
extent = nil
default:
return nil, fmt.Errorf("Unrecognized extent type: %d", datatype)
}
Expand Down
12 changes: 12 additions & 0 deletions dimension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ func TestDimensionDomainTypes(t *testing.T) {
assert.NotNil(t, extent)
assert.EqualValues(t, float64(5), extent)

dimension, err = NewStringDimension(context, "test")
assert.Nil(t, err)
assert.NotNil(t, dimension)
domain, err = dimension.Domain()
assert.Nil(t, err)
// Test getting domain
assert.Nil(t, domain)
// Test getting extent
extent, err = dimension.Extent()
assert.Nil(t, err)
assert.Nil(t, extent)

// Temp path for testing dump
tmpPathDump := os.TempDir() + string(os.PathSeparator) + "tiledb_dimension_dump_test"
// Cleanup tmp file when test ends
Expand Down
Loading