global secondary indexes
This commit is contained in:
175
main.odin
175
main.odin
@@ -173,8 +173,25 @@ handle_create_table :: proc(engine: ^dynamodb.Storage_Engine, request: ^HTTP_Req
|
||||
return
|
||||
}
|
||||
|
||||
// Parse GlobalSecondaryIndexes (optional)
|
||||
gsis := parse_global_secondary_indexes(root, attr_defs)
|
||||
defer {
|
||||
if gsi_list, has := gsis.?; has {
|
||||
for &g in gsi_list {
|
||||
delete(g.index_name)
|
||||
for &ks in g.key_schema { delete(ks.attribute_name) }
|
||||
delete(g.key_schema)
|
||||
if nka, has_nka := g.projection.non_key_attributes.?; has_nka {
|
||||
for a in nka { delete(a) }
|
||||
delete(nka)
|
||||
}
|
||||
}
|
||||
delete(gsi_list)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the table
|
||||
desc, create_err := dynamodb.create_table(engine, string(table_name), key_schema, attr_defs)
|
||||
desc, create_err := dynamodb.create_table(engine, string(table_name), key_schema, attr_defs, gsis)
|
||||
if create_err != .None {
|
||||
#partial switch create_err {
|
||||
case .Table_Already_Exists:
|
||||
@@ -261,7 +278,30 @@ handle_describe_table :: proc(engine: ^dynamodb.Storage_Engine, request: ^HTTP_R
|
||||
ad.attribute_name, dynamodb.scalar_type_to_string(ad.attribute_type))
|
||||
}
|
||||
|
||||
strings.write_string(&builder, `]}}`)
|
||||
strings.write_string(&builder, `]`)
|
||||
|
||||
// Include GSI Info — INSIDE the Table object, before the closing braces
|
||||
if gsis, has_gsis := metadata.global_secondary_indexes.?; has_gsis && len(gsis) > 0 {
|
||||
strings.write_string(&builder, `,"GlobalSecondaryIndexes":[`)
|
||||
for gsi, gi in gsis {
|
||||
if gi > 0 do strings.write_string(&builder, ",")
|
||||
strings.write_string(&builder, `{"IndexName":"`)
|
||||
strings.write_string(&builder, gsi.index_name)
|
||||
strings.write_string(&builder, `","KeySchema":[`)
|
||||
for ks, ki in gsi.key_schema {
|
||||
if ki > 0 do strings.write_string(&builder, ",")
|
||||
fmt.sbprintf(&builder, `{"AttributeName":"%s","KeyType":"%s"}`,
|
||||
ks.attribute_name, dynamodb.key_type_to_string(ks.key_type))
|
||||
}
|
||||
strings.write_string(&builder, `],"Projection":{"ProjectionType":"`)
|
||||
strings.write_string(&builder, projection_type_to_string(gsi.projection.projection_type))
|
||||
strings.write_string(&builder, `"},"IndexStatus":"ACTIVE"}`)
|
||||
}
|
||||
strings.write_string(&builder, "]")
|
||||
}
|
||||
|
||||
// Close Table object and root object
|
||||
strings.write_string(&builder, `}}`)
|
||||
|
||||
resp_body := strings.to_string(builder)
|
||||
response_set_body(response, transmute([]byte)resp_body)
|
||||
@@ -1054,6 +1094,9 @@ handle_query :: proc(engine: ^dynamodb.Storage_Engine, request: ^HTTP_Request, r
|
||||
return
|
||||
}
|
||||
|
||||
// Grab index name from request body
|
||||
index_name := parse_index_name(request.body)
|
||||
|
||||
// Fetch table metadata early for ExclusiveStartKey parsing
|
||||
metadata, meta_err := dynamodb.get_table_metadata(engine, table_name)
|
||||
if meta_err != .None {
|
||||
@@ -1081,6 +1124,8 @@ handle_query :: proc(engine: ^dynamodb.Storage_Engine, request: ^HTTP_Request, r
|
||||
copy(pk_owned, pk_bytes)
|
||||
defer delete(pk_owned)
|
||||
|
||||
// ---- Parse shared parameters BEFORE the GSI/table branch ----
|
||||
|
||||
// Parse Limit
|
||||
limit := dynamodb.parse_limit(request.body)
|
||||
if limit == 0 {
|
||||
@@ -1107,13 +1152,6 @@ handle_query :: proc(engine: ^dynamodb.Storage_Engine, request: ^HTTP_Request, r
|
||||
sk_condition = skc
|
||||
}
|
||||
|
||||
result, err := dynamodb.query(engine, table_name, pk_owned, exclusive_start_key, limit, sk_condition)
|
||||
if err != .None {
|
||||
handle_storage_error(response, err)
|
||||
return
|
||||
}
|
||||
defer dynamodb.query_result_destroy(&result)
|
||||
|
||||
// ---- Parse ExpressionAttributeNames/Values for filter/projection ----
|
||||
attr_names := dynamodb.parse_expression_attribute_names(request.body)
|
||||
defer {
|
||||
@@ -1137,6 +1175,62 @@ handle_query :: proc(engine: ^dynamodb.Storage_Engine, request: ^HTTP_Request, r
|
||||
delete(attr_values)
|
||||
}
|
||||
|
||||
// ---- GSI query path ----
|
||||
if idx_name, has_idx := index_name.?; has_idx {
|
||||
_, gsi_found := dynamodb.find_gsi(&metadata, idx_name)
|
||||
if !gsi_found {
|
||||
make_error_response(response, .ValidationException,
|
||||
fmt.tprintf("The table does not have the specified index: %s", idx_name))
|
||||
return
|
||||
}
|
||||
|
||||
result, err := dynamodb.gsi_query(engine, table_name, idx_name,
|
||||
pk_owned, exclusive_start_key, limit, sk_condition)
|
||||
if err != .None {
|
||||
handle_storage_error(response, err)
|
||||
return
|
||||
}
|
||||
defer dynamodb.query_result_destroy(&result)
|
||||
|
||||
// Apply FilterExpression
|
||||
filtered_items := apply_filter_to_items(request.body, result.items, attr_names, attr_values)
|
||||
scanned_count := len(result.items)
|
||||
|
||||
// Apply ProjectionExpression
|
||||
projection, has_proj := dynamodb.parse_projection_expression(request.body, attr_names)
|
||||
final_items: []dynamodb.Item
|
||||
|
||||
if has_proj && len(projection) > 0 {
|
||||
projected := make([]dynamodb.Item, len(filtered_items))
|
||||
for item, i in filtered_items {
|
||||
projected[i] = dynamodb.apply_projection(item, projection)
|
||||
}
|
||||
final_items = projected
|
||||
} else {
|
||||
final_items = filtered_items
|
||||
}
|
||||
|
||||
write_items_response_with_pagination_ex(
|
||||
response, final_items, result.last_evaluated_key, &metadata, scanned_count,
|
||||
)
|
||||
|
||||
if has_proj && len(projection) > 0 {
|
||||
for &item in final_items {
|
||||
dynamodb.item_destroy(&item)
|
||||
}
|
||||
delete(final_items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ---- Main table query path ----
|
||||
result, err := dynamodb.query(engine, table_name, pk_owned, exclusive_start_key, limit, sk_condition)
|
||||
if err != .None {
|
||||
handle_storage_error(response, err)
|
||||
return
|
||||
}
|
||||
defer dynamodb.query_result_destroy(&result)
|
||||
|
||||
// ---- Apply FilterExpression (post-query filter) ----
|
||||
filtered_items := apply_filter_to_items(request.body, result.items, attr_names, attr_values)
|
||||
scanned_count := len(result.items)
|
||||
@@ -1177,6 +1271,9 @@ handle_scan :: proc(engine: ^dynamodb.Storage_Engine, request: ^HTTP_Request, re
|
||||
return
|
||||
}
|
||||
|
||||
// Grab index name from request body
|
||||
index_name := parse_index_name(request.body)
|
||||
|
||||
metadata, meta_err := dynamodb.get_table_metadata(engine, table_name)
|
||||
if meta_err != .None {
|
||||
handle_storage_error(response, meta_err)
|
||||
@@ -1202,13 +1299,6 @@ handle_scan :: proc(engine: ^dynamodb.Storage_Engine, request: ^HTTP_Request, re
|
||||
}
|
||||
}
|
||||
|
||||
result, err := dynamodb.scan(engine, table_name, exclusive_start_key, limit)
|
||||
if err != .None {
|
||||
handle_storage_error(response, err)
|
||||
return
|
||||
}
|
||||
defer dynamodb.scan_result_destroy(&result)
|
||||
|
||||
// ---- Parse ExpressionAttributeNames/Values for filter/projection ----
|
||||
attr_names := dynamodb.parse_expression_attribute_names(request.body)
|
||||
defer {
|
||||
@@ -1232,6 +1322,59 @@ handle_scan :: proc(engine: ^dynamodb.Storage_Engine, request: ^HTTP_Request, re
|
||||
delete(attr_values)
|
||||
}
|
||||
|
||||
// ---- GSI scan path ----
|
||||
if idx_name, has_idx := index_name.?; has_idx {
|
||||
_, gsi_found := dynamodb.find_gsi(&metadata, idx_name)
|
||||
if !gsi_found {
|
||||
make_error_response(response, .ValidationException,
|
||||
fmt.tprintf("The table does not have the specified index: %s", idx_name))
|
||||
return
|
||||
}
|
||||
|
||||
result, err := dynamodb.gsi_scan(engine, table_name, idx_name, exclusive_start_key, limit)
|
||||
if err != .None {
|
||||
handle_storage_error(response, err)
|
||||
return
|
||||
}
|
||||
defer dynamodb.scan_result_destroy(&result)
|
||||
|
||||
filtered_items := apply_filter_to_items(request.body, result.items, attr_names, attr_values)
|
||||
scanned_count := len(result.items)
|
||||
|
||||
projection, has_proj := dynamodb.parse_projection_expression(request.body, attr_names)
|
||||
final_items: []dynamodb.Item
|
||||
|
||||
if has_proj && len(projection) > 0 {
|
||||
projected := make([]dynamodb.Item, len(filtered_items))
|
||||
for item, i in filtered_items {
|
||||
projected[i] = dynamodb.apply_projection(item, projection)
|
||||
}
|
||||
final_items = projected
|
||||
} else {
|
||||
final_items = filtered_items
|
||||
}
|
||||
|
||||
write_items_response_with_pagination_ex(
|
||||
response, final_items, result.last_evaluated_key, &metadata, scanned_count,
|
||||
)
|
||||
|
||||
if has_proj && len(projection) > 0 {
|
||||
for &item in final_items {
|
||||
dynamodb.item_destroy(&item)
|
||||
}
|
||||
delete(final_items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ---- Main table scan path ----
|
||||
result, err := dynamodb.scan(engine, table_name, exclusive_start_key, limit)
|
||||
if err != .None {
|
||||
handle_storage_error(response, err)
|
||||
return
|
||||
}
|
||||
defer dynamodb.scan_result_destroy(&result)
|
||||
|
||||
// ---- Apply FilterExpression ----
|
||||
filtered_items := apply_filter_to_items(request.body, result.items, attr_names, attr_values)
|
||||
scanned_count := len(result.items)
|
||||
|
||||
Reference in New Issue
Block a user