188 lines
5.1 KiB
Odin
188 lines
5.1 KiB
Odin
|
|
// gsi_metadata.odin — GSI metadata parsing for serialize/deserialize_table_metadata
|
||
|
|
//
|
||
|
|
// Parses GSI definitions from the embedded JSON string stored in table metadata.
|
||
|
|
// This file lives in the dynamodb/ package.
|
||
|
|
package dynamodb
|
||
|
|
|
||
|
|
import "core:encoding/json"
|
||
|
|
import "core:mem"
|
||
|
|
import "core:strings"
|
||
|
|
|
||
|
|
// Parse GlobalSecondaryIndexes from a JSON string like:
|
||
|
|
// [{"IndexName":"email-index","KeySchema":[{"AttributeName":"email","KeyType":"HASH"}],
|
||
|
|
// "Projection":{"ProjectionType":"ALL"}}]
|
||
|
|
//
|
||
|
|
// Allocates all strings with the given allocator (engine.allocator for long-lived data).
|
||
|
|
parse_gsis_json :: proc(json_str: string, allocator: mem.Allocator) -> ([]Global_Secondary_Index, bool) {
|
||
|
|
data, parse_err := json.parse(transmute([]byte)json_str, allocator = context.temp_allocator)
|
||
|
|
if parse_err != nil {
|
||
|
|
return nil, false
|
||
|
|
}
|
||
|
|
defer json.destroy_value(data)
|
||
|
|
|
||
|
|
arr, ok := data.(json.Array)
|
||
|
|
if !ok {
|
||
|
|
return nil, false
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(arr) == 0 {
|
||
|
|
return nil, true // Empty is valid
|
||
|
|
}
|
||
|
|
|
||
|
|
result := make([]Global_Secondary_Index, len(arr), allocator)
|
||
|
|
|
||
|
|
for elem, i in arr {
|
||
|
|
obj, obj_ok := elem.(json.Object)
|
||
|
|
if !obj_ok {
|
||
|
|
cleanup_gsis(result[:i], allocator)
|
||
|
|
delete(result, allocator)
|
||
|
|
return nil, false
|
||
|
|
}
|
||
|
|
|
||
|
|
gsi, gsi_ok := parse_single_gsi_json(obj, allocator)
|
||
|
|
if !gsi_ok {
|
||
|
|
cleanup_gsis(result[:i], allocator)
|
||
|
|
delete(result, allocator)
|
||
|
|
return nil, false
|
||
|
|
}
|
||
|
|
|
||
|
|
result[i] = gsi
|
||
|
|
}
|
||
|
|
|
||
|
|
return result, true
|
||
|
|
}
|
||
|
|
|
||
|
|
// Parse a single GSI object from JSON
|
||
|
|
@(private = "file")
|
||
|
|
parse_single_gsi_json :: proc(obj: json.Object, allocator: mem.Allocator) -> (Global_Secondary_Index, bool) {
|
||
|
|
gsi: Global_Secondary_Index
|
||
|
|
|
||
|
|
// IndexName
|
||
|
|
idx_val, idx_found := obj["IndexName"]
|
||
|
|
if !idx_found {
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
idx_str, idx_ok := idx_val.(json.String)
|
||
|
|
if !idx_ok {
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
gsi.index_name = strings.clone(string(idx_str), allocator)
|
||
|
|
|
||
|
|
// KeySchema
|
||
|
|
ks_val, ks_found := obj["KeySchema"]
|
||
|
|
if !ks_found {
|
||
|
|
delete(gsi.index_name, allocator)
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
ks_arr, ks_ok := ks_val.(json.Array)
|
||
|
|
if !ks_ok || len(ks_arr) == 0 || len(ks_arr) > 2 {
|
||
|
|
delete(gsi.index_name, allocator)
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
|
||
|
|
key_schema := make([]Key_Schema_Element, len(ks_arr), allocator)
|
||
|
|
for ks_elem, j in ks_arr {
|
||
|
|
ks_obj, kobj_ok := ks_elem.(json.Object)
|
||
|
|
if !kobj_ok {
|
||
|
|
for k in 0..<j {
|
||
|
|
delete(key_schema[k].attribute_name, allocator)
|
||
|
|
}
|
||
|
|
delete(key_schema, allocator)
|
||
|
|
delete(gsi.index_name, allocator)
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
|
||
|
|
an_val, an_found := ks_obj["AttributeName"]
|
||
|
|
if !an_found {
|
||
|
|
for k in 0..<j { delete(key_schema[k].attribute_name, allocator) }
|
||
|
|
delete(key_schema, allocator)
|
||
|
|
delete(gsi.index_name, allocator)
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
an_str, an_ok := an_val.(json.String)
|
||
|
|
if !an_ok {
|
||
|
|
for k in 0..<j { delete(key_schema[k].attribute_name, allocator) }
|
||
|
|
delete(key_schema, allocator)
|
||
|
|
delete(gsi.index_name, allocator)
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
|
||
|
|
kt_val, kt_found := ks_obj["KeyType"]
|
||
|
|
if !kt_found {
|
||
|
|
for k in 0..<j { delete(key_schema[k].attribute_name, allocator) }
|
||
|
|
delete(key_schema, allocator)
|
||
|
|
delete(gsi.index_name, allocator)
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
kt_str, kt_ok := kt_val.(json.String)
|
||
|
|
if !kt_ok {
|
||
|
|
for k in 0..<j { delete(key_schema[k].attribute_name, allocator) }
|
||
|
|
delete(key_schema, allocator)
|
||
|
|
delete(gsi.index_name, allocator)
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
|
||
|
|
kt, kt_parse_ok := key_type_from_string(string(kt_str))
|
||
|
|
if !kt_parse_ok {
|
||
|
|
for k in 0..<j { delete(key_schema[k].attribute_name, allocator) }
|
||
|
|
delete(key_schema, allocator)
|
||
|
|
delete(gsi.index_name, allocator)
|
||
|
|
return {}, false
|
||
|
|
}
|
||
|
|
|
||
|
|
key_schema[j] = Key_Schema_Element{
|
||
|
|
attribute_name = strings.clone(string(an_str), allocator),
|
||
|
|
key_type = kt,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
gsi.key_schema = key_schema
|
||
|
|
|
||
|
|
// Projection
|
||
|
|
gsi.projection.projection_type = .ALL // default
|
||
|
|
if proj_val, proj_found := obj["Projection"]; proj_found {
|
||
|
|
if proj_obj, proj_ok := proj_val.(json.Object); proj_ok {
|
||
|
|
if pt_val, pt_found := proj_obj["ProjectionType"]; pt_found {
|
||
|
|
if pt_str, pt_ok := pt_val.(json.String); pt_ok {
|
||
|
|
switch string(pt_str) {
|
||
|
|
case "ALL": gsi.projection.projection_type = .ALL
|
||
|
|
case "KEYS_ONLY": gsi.projection.projection_type = .KEYS_ONLY
|
||
|
|
case "INCLUDE": gsi.projection.projection_type = .INCLUDE
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// NonKeyAttributes
|
||
|
|
if nka_val, nka_found := proj_obj["NonKeyAttributes"]; nka_found {
|
||
|
|
if nka_arr, nka_ok := nka_val.(json.Array); nka_ok && len(nka_arr) > 0 {
|
||
|
|
nka := make([]string, len(nka_arr), allocator)
|
||
|
|
for attr_val, k in nka_arr {
|
||
|
|
if attr_str, attr_ok := attr_val.(json.String); attr_ok {
|
||
|
|
nka[k] = strings.clone(string(attr_str), allocator)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
gsi.projection.non_key_attributes = nka
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return gsi, true
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clean up partially-constructed GSI array
|
||
|
|
cleanup_gsis :: proc(gsis: []Global_Secondary_Index, allocator: mem.Allocator) {
|
||
|
|
for gsi in gsis {
|
||
|
|
delete(gsi.index_name, allocator)
|
||
|
|
for ks in gsi.key_schema {
|
||
|
|
delete(ks.attribute_name, allocator)
|
||
|
|
}
|
||
|
|
delete(gsi.key_schema, allocator)
|
||
|
|
if nka, has_nka := gsi.projection.non_key_attributes.?; has_nka {
|
||
|
|
for attr in nka {
|
||
|
|
delete(attr, allocator)
|
||
|
|
}
|
||
|
|
delete(nka, allocator)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|