Transform ->> expressions into @> containment for JSONB index usage
| 350 | |
| 351 | // Transform ->> expressions into @> containment for JSONB index usage |
| 352 | void transform_jsonb_arrow_quals(Node** nodeptr) |
| 353 | { |
| 354 | if (!nodeptr || !*nodeptr) |
| 355 | return; |
| 356 | Node* node = *nodeptr; |
| 357 | |
| 358 | if (IsA(node, BoolExpr)) { |
| 359 | BoolExpr* b = (BoolExpr*)node; |
| 360 | ListCell* lc = nullptr; |
| 361 | foreach (lc, b->args) { |
| 362 | transform_jsonb_arrow_quals((Node**)&lfirst(lc)); |
| 363 | } |
| 364 | } else if (IsA(node, OpExpr)) { |
| 365 | OpExpr* op = (OpExpr*)node; |
| 366 | char* opname = get_opname(op->opno); |
| 367 | |
| 368 | // Look for: (data -> 'a' -> 'b' ->> 'c') = 'value' or (data ->> 'key') = 'value' |
| 369 | // Transform to: data @> '{"a": {"b": {"c": "value"}}}'::jsonb |
| 370 | if (opname && strcmp(opname, "=") == 0 && list_length(op->args) == 2) { |
| 371 | Node* left = (Node*)linitial(op->args); |
| 372 | Node* right = (Node*)lsecond(op->args); |
| 373 | |
| 374 | // Check if left side has -> or ->> operators and right is a constant |
| 375 | if (IsA(left, OpExpr) && IsA(right, Const)) { |
| 376 | // Extract the full path from nested operators |
| 377 | std::vector<std::string> path; |
| 378 | Node* base_col = nullptr; |
| 379 | extract_jsonb_path(left, path, &base_col); |
| 380 | |
| 381 | if (base_col && !path.empty()) { |
| 382 | Oid col_type = exprType(base_col); |
| 383 | Const* val = (Const*)right; |
| 384 | |
| 385 | if (col_type == JSONBOID && !val->constisnull && val->consttype == TEXTOID) { |
| 386 | // Build nested JSON |
| 387 | char* vs = text_to_cstring(DatumGetTextPP(val->constvalue)); |
| 388 | std::string json_str = build_nested_json(path, vs); |
| 389 | |
| 390 | if (!json_str.empty()) { |
| 391 | // Convert to JSONB |
| 392 | Jsonb* jb = |
| 393 | DatumGetJsonbP(DirectFunctionCall1(jsonb_in, CStringGetDatum(json_str.c_str()))); |
| 394 | |
| 395 | // Create JSONB constant |
| 396 | Const* jc = makeNode(Const); |
| 397 | jc->consttype = JSONBOID; |
| 398 | jc->consttypmod = -1; |
| 399 | jc->constcollid = InvalidOid; |
| 400 | jc->constlen = -1; |
| 401 | jc->constvalue = JsonbPGetDatum(jb); |
| 402 | jc->constisnull = false; |
| 403 | jc->constbyval = false; |
| 404 | jc->location = val->location; |
| 405 | |
| 406 | // Look up @> operator |
| 407 | Oid cop = OpernameGetOprid(list_make1(makeString(pstrdup("@>"))), JSONBOID, JSONBOID); |
| 408 | if (OidIsValid(cop)) { |
| 409 | // Create new @> operator expression |
no test coverage detected