)]}'
{
  "log": [
    {
      "commit": "14732bfaf4a63a1ac9775d7dc7a2c0eed3f47ab2",
      "tree": "de5171bfe34086d070cbae6ea6a9f709c8019a23",
      "parents": [
        "12e2a31c6716073fa046efe6fd3b1f930efabca6"
      ],
      "author": {
        "name": "serdarmumcu",
        "email": "serdar.mumcu@udemy.com",
        "time": "Fri Jun 12 00:05:26 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jun 11 14:05:26 2026 -0700"
      },
      "message": "Fix locale-dependent string comparison in direct_field_access test (#2439)\n\nReplace \u0027test\u0027 \u003e \u0027TEST\u0027 with deterministic \u0027abd\u0027 \u003e \u0027abc\u0027. The original\ncomparison returns different results depending on system collation\nsettings (true under C locale, false under en_US.UTF-8)."
    },
    {
      "commit": "12e2a31c6716073fa046efe6fd3b1f930efabca6",
      "tree": "c3a31c98c86398d780556d27d72fe33ba2b0c41e",
      "parents": [
        "23cbe57a1eedee9167221a0359b09e538ff6fc87"
      ],
      "author": {
        "name": "Prashant Chinnam",
        "email": "5108573+crprashant@users.noreply.github.com",
        "time": "Thu Jun 11 06:58:30 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jun 11 06:58:30 2026 -0700"
      },
      "message": "Fix VLE [*0..N] zero-hop self-binding when edge label is missing (#2382) (#2419)\n\nA variable-length relationship pattern with a zero lower bound, e.g.\n`(p)-[:LABEL*0..N]-(f)`, must produce the zero-hop self-binding row\n(`f` \u003d `p`) regardless of whether any edge of `LABEL` exists in the\ngraph. This matches Neo4j/openCypher semantics. Previously, when the\nedge label did not exist in the label cache, AGE short-circuited the\nentire MATCH to zero rows (or NULL-extended rows for OPTIONAL MATCH).\n\nThe fix has three parts:\n\n1. parser/cypher_clause.c: A new helper `is_zero_lower_bound_vle()`\n   inspects the FuncCall produced by `build_VLE_relation()` and reports\n   whether the relationship is a zero-bound VLE. It is intentionally\n   defensive about the FuncCall shape so that any future parser changes\n   fall back to the existing short-circuit safely.\n\n   `match_check_valid_label()` and `path_check_valid_label()` now treat\n   a missing edge label as fatal only when the relationship requires at\n   least one edge of that label. Patterns mixing a zero-bound segment\n   with another impossible segment (e.g. `(a)-[:NOEXIST*0..1]-(b)-[:STILL_MISSING]-(c)`)\n   still correctly resolve to zero rows because the second segment\n   independently fails the label check.\n\n2. utils/adt/age_vle.c: `is_an_edge_match()` now returns false early\n   when the user requested a specific label that does not exist\n   (`edge_label_name !\u003d NULL \u0026\u0026 edge_label_name_oid \u003d\u003d InvalidOid`).\n   This prevents a zero-bound traversal of `[:NOEXIST*0..N]` from\n   incorrectly walking arbitrary other-label edges via the existing\n   \"no constraints -\u003e match all\" fast path. The zero-hop case itself\n   is unaffected because it is generated by `build_VLE_zero_container()`\n   without ever consulting `is_an_edge_match()`.\n\n3. regress/sql/cypher_vle.sql: Adds seven regression cases that lock\n   in the new behaviour, including the rubber-duck scenarios where\n   another label exists in the graph (must NOT be matched by the\n   missing-label VLE), where another segment is unsatisfiable\n   (must still produce zero rows), and where the label exists\n   (sanity check, unchanged behaviour)."
    },
    {
      "commit": "23cbe57a1eedee9167221a0359b09e538ff6fc87",
      "tree": "1629a0931e4967090ac89af2e44335a3aa48029b",
      "parents": [
        "639c8d5ac1cc930095caccb58b90dd65d9035e12"
      ],
      "author": {
        "name": "crdv7",
        "email": "49877075+crdv7@users.noreply.github.com",
        "time": "Wed Jun 10 01:44:15 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 09 10:44:15 2026 -0700"
      },
      "message": "fix: remove pthread_mutex that causes self-deadlock on VLE queries (#2433)\n\nThe pthread_mutex in manage_GRAPH_global_contexts() causes permanent\nself-deadlock when ereport(ERROR) triggers siglongjmp while the mutex\nis held, skipping pthread_mutex_unlock(). Any subsequent VLE query on\nthe same backend connection hangs forever in pthread_mutex_lock() with\n__owner \u003d\u003d own PID.\n\nThe mutex was introduced in PR #1881 (fix for issue #1878) but is\nunnecessary: it protects a process-local static variable in\nPostgreSQL\u0027s single-threaded backend model where no concurrent access\nexists. The actual fix for #1878 was the Assert-to-runtime-check\nconversion and strndup defensive copy, which remain untouched."
    },
    {
      "commit": "639c8d5ac1cc930095caccb58b90dd65d9035e12",
      "tree": "e5dd18a05c182c063eb1bbec085f222b2fe7a5b5",
      "parents": [
        "73d0705e4ca7b3866b30436248b292b0170fb67b"
      ],
      "author": {
        "name": "Prashant Chinnam",
        "email": "5108573+crprashant@users.noreply.github.com",
        "time": "Mon Jun 08 16:55:50 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 08 16:55:50 2026 -0700"
      },
      "message": "Restore contsel/contjoinsel for containment \u0026 key-existence operators (#2356) (#2417)\n\nThe containment (`@\u003e`, `\u003c@`, `@\u003e\u003e`, `\u003c\u003c@`) and key-existence (`?`, `?|`,\n`?\u0026`) operators on `agtype` were bound to `matchingsel`/`matchingjoinsel`\non the PG14+ source tree. `matchingsel` is built for pattern operators\n(LIKE/regex) and during planning invokes the operator\u0027s underlying\nfunction (`agtype_contains`) once per `pg_statistic` MCV. With realistic\nstatistics targets that produces a planner-time regression that\ndominates simple OLTP-style point queries.\n\nRebind those operators to the lighter `contsel`/`contjoinsel` estimators,\nwhich return fixed selectivity constants without invoking the operator\nfunction during planning. This is a deliberate planning-speed vs.\nestimate-accuracy trade-off. Note it DIVERGES from PostgreSQL core, which\nkeeps jsonb\u0027s `@\u003e`, `\u003c@`, `?`, `?|`, `?\u0026` on `matchingsel`/`matchingjoinsel`\n(verified on REL_16/17/18_STABLE in `pg_operator.dat`); it is an\nAGE-specific choice favoring workloads where these operators appear in\nselective point lookups. A future improvement could add a custom `agtype`\nselectivity function that is both cheap and statistics-aware.\n\nChanges:\n\n* `sql/agtype_operators.sql`, `sql/agtype_exists.sql`: 10 operators\n  flipped from `matchingsel`/`matchingjoinsel` to\n  `contsel`/`contjoinsel`.\n* `age--1.7.0--y.y.y.sql`: appended `ALTER OPERATOR ... SET (RESTRICT,\n  JOIN)` for all 10 operators so existing installs flip on\n  `ALTER EXTENSION age UPDATE`.\n* `regress/sql/containment_selectivity.sql` (+ `expected/.out`): pin\n  the bindings via `pg_operator`, plus a scoped \"no leaked matchingsel\"\n  guard and functional smoke for all 10 operators. Adds an upgrade-path\n  assertion that simulates a stale (pre-fix) install, replays the\n  shipped `ALTER OPERATOR` block, and confirms every overload flips to\n  `contsel`/`contjoinsel` (run in a rolled-back transaction).\n* `regress/expected/cypher_match.out`, `regress/expected/cypher_vle.out`:\n  refresh expected to reflect new (and better) plan shapes that the\n  lower-selectivity helper produces — `test_enable_containment` now\n  picks Nested Loop + Index Only Scans over a Seq Scan/Hash Join, and\n  two `MATCH p\u003d...` and `show_list_use_vle` queries flip row order\n  (queries had no `ORDER BY`; result set is unchanged, only ordering).\n* `Makefile`: register `containment_selectivity` in `REGRESS`.\n\nValidation:\n\n* Build: clean, `-Werror`.\n* Regression: 36/37 tests pass under `EXTRA_TESTS\u003d\"pgvector\n  fuzzystrmatch pg_trgm\"`. Only `age_upgrade` fails — pre-existing on\n  master at 774e781b (verified by `git stash \u0026\u0026 installcheck`).\n* Reporter\u0027s exact methodology (LDBC-SNB-style snb_graph + pgbench on\n  `bench_message_content`) reproduces the regression and the fix:\n\n  | Metric                     | matchingsel | contsel | Delta |\n  |----------------------------|-------------|---------|-------|\n  | EXPLAIN planning time (ms) | 1.42        | 0.97    | -32%  |\n  | EXPLAIN execution time (ms)| 0.34        | 0.31    | ~equal|\n  | pgbench TPS (8c x 30s)     | 5247        | 7378    | +40.6%|\n\n  Run with `default_statistics_target \u003d 1000` to populate MCV lists,\n  matching the reporter\u0027s analyzed-graph conditions.\n\n* Upgrade path: validated end-to-end during the benchmark — operator\n  bindings were flipped from `matchingsel` -\u003e `contsel` via the same\n  `ALTER OPERATOR` statements the upgrade SQL ships, while operators\n  remained functional throughout.\n\nDriver workflows (python/go/node/jdbc) intentionally not run: this PR\nonly adjusts pg_operator selectivity metadata. There is no C, type, or\nprotocol change that drivers could observe.\n\nCloses #2356."
    },
    {
      "commit": "73d0705e4ca7b3866b30436248b292b0170fb67b",
      "tree": "24405ef6e5a95241ded4b8a4cbd09d1d017737d2",
      "parents": [
        "5a254d6869d8b2c271f025ea158c0fee2cfacfa3"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Wed Jun 03 01:08:54 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jun 03 13:08:54 2026 +0500"
      },
      "message": "perf: VLE hash-adjacency overhaul — agehash + flat-array VertexEdgeArray (#2421)\n\nReplaces the per-graph adjacency map with a Robin Hood open-addressing\nhashtable (agehash) and an embedded flat-array edge list, removing the\nhottest dynahash path on IC1 and shrinking the largest hashtable AGE\nkeeps. Stages land as one commit:\n\n  S1  MurmurHash3 fmix64 for graphid hashtables (replaces tag_hash)\n  S2  Precompute graphid hash; share across paired DFS lookups\n  S3  Replace ListGraphId adjacency with embedded flat-array\n      VertexEdgeArray (single palloc, contiguous iteration)\n  S4  Batched MLP lookup pipeline in add_valid_vertex_edges\n  S5/C1  agehash library: INLINE Robin Hood hashtable with\n         _with_hash API, freeze, iter, and a regress-only selftest\n  S5/C2  Wire global graph edge_hashtable through agehash;\n         drop edge_id from edge_entry (key lives in slot header);\n         AGEHASH_MAX_LOAD\u003d0.85; MemoryContextAllocHuge for SF10+\n\nPerformance (SF3 LDBC SNB, 5 runs/3 warmup, vs clean master baseline_v2):\n\n  IC1   8,625 → 7,117 ms   −17.49 %   (the headline; hashtable-bound)\n  IU1      40 →    35 ms   −11.86 %   (heaviest update; lookup-bound)\n  IC sum     198,958 → 197,367 ms     −0.80 %   (suite-level noise)\n  IS sum       1,009 →   1,028 ms     +1.86 %   (IS3 jitter; sub-ms)\n  IU sum          77 →      72 ms     −6.64 %\n  IC2/3/4/5/6/7/8/9/10/11/12: parity (within ±3.3 %, mostly ±1.5 %)\n\nThe VLE-DFS-heavy queries (IC3/5/6/9/11) sit at parity: with\nhash_search_with_hash_value at ≤1 % inclusive on their baseline\nflames, no hashtable swap can recover meaningful wall-time on them.\n\nMemory: removing edge_id from edge_entry saves ~416 MB on SF3 and\n~1.4 GB on SF10 for the global graph\u0027s edge_hashtable. Slot capacity\nuses MemoryContextAllocHuge so SF10+ edge tables can be built.\n\nAdds:\n  src/backend/utils/cache/agehash.c, src/include/utils/agehash.h\n  regress/sql/agehash.sql + expected/agehash.out (boundary selftest)\n  _agehash_self_test() in both fresh-install and upgrade SQL\n\nTested on PostgreSQL 18.3 (REL_18_STABLE): all 35 regression tests\npass (installcheck), warning-free build.\n\nmodified:   Makefile\nmodified:   age--1.7.0--y.y.y.sql\nnew file:   regress/expected/agehash.out\nnew file:   regress/sql/agehash.sql\nmodified:   sql/age_main.sql\nmodified:   src/backend/utils/adt/age_global_graph.c\nmodified:   src/backend/utils/adt/age_vle.c\nnew file:   src/backend/utils/cache/agehash.c\nmodified:   src/include/utils/age_global_graph.h\nnew file:   src/include/utils/agehash.h"
    },
    {
      "commit": "5a254d6869d8b2c271f025ea158c0fee2cfacfa3",
      "tree": "151854aaf8b8d876a098abcf41baf7fc02bd413f",
      "parents": [
        "795a881d20d6b7cf74439a8be06289ac3e390dc4"
      ],
      "author": {
        "name": "Sai Asish Y",
        "email": "say.apm35@gmail.com",
        "time": "Mon Jun 01 17:05:17 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 17:05:17 2026 -0700"
      },
      "message": "python driver: preserve trailing quotes in agtype string values (#2425)\n\n* python driver: preserve trailing quotes in agtype string values\n\nstr.strip(\u0027\"\u0027) in visitStringValue() and visitPair() removes every \u0027\"\u0027\non either side of the token, not just the outer delimiters, so a value\nending in an escaped quote (e.g. \u0027\"foo \\\"bar\\\"\"\u0027) loses its trailing\nbackslash-escaped \u0027\"\u0027 character.  The Agtype grammar guarantees STRING\ntokens always carry exactly one delimiter on each side, so slice with\n[1:-1] to strip them precisely.\n\nFixes #2418\n\nSigned-off-by: SAY-5 \u003csaiasish.cnp@gmail.com\u003e\n\n* fix(python-driver): centralize string delimiter trim and clean test docstring\n\n---------\n\nSigned-off-by: SAY-5 \u003csaiasish.cnp@gmail.com\u003e"
    },
    {
      "commit": "795a881d20d6b7cf74439a8be06289ac3e390dc4",
      "tree": "104abbd13451028a5f53b8e0823b932eb6d05c14",
      "parents": [
        "5ef7d6d55cbf73b359604ff816a8f86580f94b7f"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Jun 01 11:52:20 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 11:52:20 2026 -0700"
      },
      "message": "perf(agtype): arena passthrough + binary-direct id extraction (#2424)\n\nTargets the agtype value-tree allocation cost that dominates LDBC IC4 and\nother sort/comparator-bound queries. Two complementary optimizations\n(A1 arena, A2 fast-id) plus the sanitizer issues uncovered while\nverifying the change.\n\nA1 — per-tuple agtype arena (Pattern B applied):\n  Add agt_arena_begin / AGT_ARENA_BEGIN_SHARED / agt_arena_reset /\n  agt_arena_end helpers (agtype_util.c, agtype.h). Two usage patterns:\n  disposable per-call arenas for one-shot tree builds, and a long-lived\n  shared arena (file-static, parented under CacheMemoryContext) for hot\n  inner loops that would otherwise pay AllocSetCreate cost per call.\n\n  Apply to compare_agtype_scalar_containers: when the comparator\n  deserializes VERTEX/EDGE/PATH composites via fill_agtype_value_no_copy,\n  route those allocations into a shared arena and reset (O(1)) at the\n  end of the comparison instead of recursively pfree\u0027ing the tree (O(N)).\n  Master IC4 spent 17.64% of total CPU in pfree_agtype_value_content from\n  this comparator; the arena collapses that walk to one MemoryContextReset.\n\nA2 — binary-direct id extraction:\n  When compare_agtype_scalar_containers compares two VERTEX or two EDGE\n  values, only the graphid \u0027id\u0027 determines order (compare_agtype_scalar_values\n  ignores all other fields). Master IC4 spent 73.48% inclusive in\n  ag_deserialize_composite called from this comparator, building the entire\n  agtype_value tree (label, properties, nested values) just to read one int64.\n\n  Add extract_composite_id_fast: walks the binary VERTEX/EDGE container\n  directly and reads the int64 id at field index 0 (always \u0027id\u0027 due to\n  length-sorted key ordering established by uniqueify_agtype_object — the\n  same invariant PR #2302 leveraged for direct array indexing).\n\n  Wired into compare_agtype_scalar_containers as a fast path before the\n  existing arena+slow path. Falls through on cross-type comparisons\n  (VERTEX vs EDGE), PATH, and malformed extended-type entries.\n\nASAN/UBSan fixes (10 issues found while verifying A1+A2 under\n-fsanitize\u003daddress -fsanitize\u003dundefined; 1 introduced by A2, 9 pre-existing).\nThe 11th finding (uninitialized parent_cpstate fields in cypher_analyze.c)\nwas independently fixed upstream by PR #2423; this branch is rebased on\ntop of that fix and no longer carries a duplicate change.\n  - HIGH: heap-buffer-overflow in agtype_raw.c:write_container — VARSIZE\n    included the 4-byte varlena header but copy started past it, reading\n    VARHDRSZ bytes past source allocation. Subtract VARHDRSZ. ASan flagged\n    4 times in installcheck.\n  - 7 unaligned int64/float8 reads/writes (AGT_HEADER is 4 bytes, leaving\n    payload 4-byte- but not 8-byte-aligned). UB under strict-alignment\n    rules. Replaced typed loads/stores with memcpy in agtype_ext.c (4 sites),\n    agtype_util.c (3 sites incl. extract_composite_id_fast), agtype_raw.c\n    (write_graphid).\n  - agtype.c:agtype_hash_cmp: reading r-\u003eval.array.raw_scalar at\n    WAGT_END_ARRAY where the iterator does not populate *val. Replaced\n    with explicit raw_scalar bool stack tracked across BEGIN/END pairs.\n    Hash output unchanged.\n  - agtype_util.c:copy_to_buffer: memcpy(NULL, NULL, 0) is UB. Guard with\n    if (len \u003e 0).\n\nPerformance (SF3 LDBC, paired same-session vs master):\n  IC sum:   201,930 -\u003e 157,129 ms \u003d -22.19%\n  IC4:       50,570 -\u003e   7,615 ms \u003d -84.94%\n  IC5:       21,779 -\u003e  20,646 ms \u003d  -5.20%\n  IS, IU:   within noise (\u003c\u003d +/-3% on sub-second queries)\n\nTests:\n  34/34 installcheck on production PG18.3\n  34/34 installcheck on ASAN+UBSan PG18.3 (zero unique runtime errors,\n        zero heap-buffer-overflows; the original audit found 10 unique\n        runtime issues plus 1 HBO with 4 instances, all addressed here\n        or upstream)\n\nCo-authored-by: Claude noreply@anthropic.com\n\nmodified:   src/backend/utils/adt/agtype.c\nmodified:   src/backend/utils/adt/agtype_ext.c\nmodified:   src/backend/utils/adt/agtype_raw.c\nmodified:   src/backend/utils/adt/agtype_util.c\nmodified:   src/include/utils/agtype.h"
    },
    {
      "commit": "5ef7d6d55cbf73b359604ff816a8f86580f94b7f",
      "tree": "6900c406d3fbd54ed2178772e41200b63ae48fff",
      "parents": [
        "f02eda0e0504bf3e941dd4a05e3060d2f3bb4f04"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Mon Jun 01 23:02:07 2026 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 11:02:07 2026 -0700"
      },
      "message": "Add vertex and edge composite types with direct field access optimization (#2303)\n\n- Introduce vertex and edge as pg composite types\n  vertex: (id, label, properties)\n  edge: (id, label, start_id, end_id, properties)\n- end_id precedes start_id intentionally, it matches the agtype edge\u0027s\n  canonical key order (keys sorted by length, end_id\u003d6 \u003c start_id\u003d8),\n  keeping the composite positionally consistent with the agtype\n  representation.\n- Property access (a.name) now directly uses a.properties for\n  agtype_access_operator instead of rebuilding via _agtype_build_vertex/edge\n- Optimize accessor functions (id, properties, label, type, start_id, end_id)\n  to use direct FieldSelect on composite types instead of agtype functions\n- Add casts: vertex/edge to agtype, vertex/edge to json/jsonb\n- Add eq and not eq ops for vertex/edge composite types.\n- Fix label_name specific routine to use cache instead of ag_label scan\n- Write/update clauses have executors strictly tied to agtype, due to which\n  the variables after any write/update clause are carried forward as agtype.\n- Allows users to completely skip agtype build functions and return vertex/edge\n  for pure read queries.\n- Change _label_name to return agtype since record comparisons are not\n  allowed with cstring. Consequently, _agtype_build_vertex/edge now accept\n  agtype as label.\n- Fix MERGE clause type mismatch when accessing properties from previous\n  MATCH clauses by wrapping columns with agtype_volatile_wrapper\n  before namespace lookup.\n- Update expression index in pgvector.sql, since now it uses raw properties column\n  instead of _agtype_build_vertex/edge.\n- Add regression tests\n\nAssisted-by AI"
    },
    {
      "commit": "f02eda0e0504bf3e941dd4a05e3060d2f3bb4f04",
      "tree": "b015f484b424ae82055b61ee4e051036e962cf86",
      "parents": [
        "9960e9c6034ed301b90e6e6777c80b7c95b45cf0"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Jun 01 09:05:49 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 21:05:49 2026 +0500"
      },
      "message": "perf: VLE terminal-qual rewrite (#2420)\n\nperf: VLE terminal-qual rewrite — emit endpoint equalities instead of SRF\nqual functions.\n\nRemoves the per-row age_match_vle_terminal_edge and age_match_two_vle_edges\nqual functions from VLE query plans. The cypher transformer now emits the\nendpoint match as a plain graphid/int8 equality on new SRF output columns,\nevaluated by the planner like any other join clause — no detoasting, no\nper-row C function dispatch. Stages land as one commit:\n\n  S1  Inline start_vid/end_vid in VLE_path_container header\n  S2  Read VLE qual endpoints from header-only TOAST slice\n  S4  Emit start_id/end_id as scalar SRF output columns\n      (age_vle now RETURNS SETOF record with edges/start_id/end_id)\n  S5  Cypher transformer rewrites terminal-edge match quals as\n      integer equalities (drops age_match_vle_terminal_edge call)\n  S6  Cypher transformer emits graphid equality for two-VLE-edge\n      joins (drops age_match_two_vle_edges call)\n\nPerformance (SF3 LDBC SNB, 5 runs/3 warmup, vs clean master baseline_v2):\n\n  IC sum    198,958 → 109,322 ms   −45.05 %  (1.82× end-to-end speedup)\n  IC1   8,625 →  4,600 ms  −46.67 %\n  IC3  21,239 →  9,784 ms  −53.93 %\n  IC5  21,051 →  5,696 ms  −72.94 %\n  IC6  15,916 →  4,447 ms  −72.06 %\n  IC9  44,839 → 21,161 ms  −52.81 %\n  IC10 13,104 →  2,432 ms  −81.44 %\n  IC11 11,676 →    241 ms  −97.93 %  (48× speedup)\n  IC2/4/7/8/12: parity (within ±3.3 %; IC4 is −2.47 %, no regression)\n  IS sum:  1,009 → 1,004 ms   −0.51 %  (no VLE traffic)\n  IU sum:     77 →    71 ms   −8.38 %  (IU1 −16.09 %; incidental)\n\nMemory: header-only TOAST slice for VLE qual evaluation avoids\ndetoasting full path containers on every row; reduces per-call\npalloc/pfree churn in long DFS paths. No measured RSS change.\n\nDead-code removal:\n  - Bodies of age_match_vle_terminal_edge and age_match_two_vle_edges\n    are gone from age_vle.c (~225 lines).  C entry points remain as\n    error-raising stubs solely so the upgrade-test snapshot loader\n    (which sources an older 1.7.0_initial SQL against the current\n    age.so) can resolve the symbols before the immediate ALTER\n    EXTENSION UPDATE drops them.  No regress test references either\n    function.\n  - SQL CREATE FUNCTION declarations removed from fresh install\n    (sql/agtype_typecast.sql).\n  - DROP FUNCTION IF EXISTS for both qual functions added to the\n    upgrade script (age--1.7.0--y.y.y.sql).\n\nAPI change: ag_catalog.age_vle(...) now RETURNS SETOF record with\noutput columns (edges agtype, start_id graphid, end_id graphid)\ninstead of RETURNS SETOF agtype.  Both 7-arg and 8-arg overloads\nare updated in fresh-install (sql/agtype_typecast.sql) and upgrade\n(age--1.7.0--y.y.y.sql) paths.  age_match_vle_terminal_edge and\nage_match_two_vle_edges are dropped on upgrade and absent from\nfresh installs.  Internal AGE callers are unaffected; external SQL\nthat called any of these directly must adapt.\n\nHardening (in response to PR #2420 review feedback):\n  - cypher_clause.c: terminal-edge and two-VLE-edge join-qual\n    emission paths now bracket the existing Assert(vle_alias !\u003d NULL)\n    with a runtime ereport(ERROR, ...) so a missing alias produces a\n    clean error in production builds (where Asserts compile out)\n    instead of a NULL-deref crash inside makeString().\n  - age_vle.c: VLE_path_container struct comment now documents that\n    the layout is transient (consumed within the producing query and\n    never persisted), so the new start_vid/end_vid fields do not\n    require an AGT_FBINARY_TYPE_VLE_PATH version bump or backward-\n    compatible reader. The note also flags the constraint a future\n    change would need to honor if the container ever became\n    persistable.\n\nTested on PostgreSQL 18.3 (REL_18_STABLE): all 34 regression tests\npass (installcheck), warning-free build.\n\nmodified:   age--1.7.0--y.y.y.sql\nmodified:   regress/expected/cypher_match.out\nmodified:   regress/expected/cypher_vle.out\nmodified:   regress/expected/expr.out\nmodified:   sql/agtype_typecast.sql\nmodified:   src/backend/parser/cypher_clause.c\nmodified:   src/backend/parser/cypher_transform_entity.c\nmodified:   src/backend/utils/adt/age_vle.c\nmodified:   src/include/parser/cypher_transform_entity.h"
    },
    {
      "commit": "9960e9c6034ed301b90e6e6777c80b7c95b45cf0",
      "tree": "d1c1e0c89bd8112dbe839328cd2f935ffde722d4",
      "parents": [
        "01ee9414f9b483782cbf50cfe0744860bcf375ae"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Wed May 13 20:02:14 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 13 17:02:14 2026 -0700"
      },
      "message": "Add agtype \u003c-\u003e jsonb bidirectional casts (#350) (#2361)\n\n* Add agtype \u003c-\u003e jsonb bidirectional casts (issue #350)\n\nRegister explicit casts between agtype and jsonb, enabling:\n\n  SELECT properties(n)::jsonb FROM cypher(...) AS (n agtype);\n  SELECT \u0027{\"key\": \"value\"}\u0027::jsonb::agtype;\n\n  -- Use jsonb operators on graph data:\n  SELECT (props::jsonb)-\u003e\u003e\u0027name\u0027 FROM cypher(...) AS (props agtype);\n\nImplementation uses SQL language functions that go through proven\ntext-intermediate paths:\n  agtype -\u003e jsonb:  agtype_to_json() -\u003e json::jsonb\n  jsonb -\u003e agtype:  jsonb::text -\u003e text::agtype\n\nThis approach is safe because agtype extends jsonb\u0027s binary format\nwith types (AGTV_INTEGER, AGTV_FLOAT, AGTV_VERTEX, AGTV_EDGE,\nAGTV_PATH) that jsonb does not recognize, making direct binary\nconversion unreliable.  The text roundtrip handles all value types\ncorrectly including graph types (vertex/edge properties are\nextracted as JSON objects).\n\nAll 31 regression tests pass.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* Address Copilot review: fix comment, add regression tests for jsonb casts\n\n- Fix comment in agtype_coercions.sql: document json-intermediate path\n  (agtype_to_json -\u003e json::jsonb) instead of incorrect \"text intermediate\"\n- Add agtype_jsonb_cast regression test covering: string/null/array/object\n  agtype-\u003ejsonb, all jsonb scalar types-\u003eagtype, roundtrips, vertex/edge\n  -\u003ejsonb with structural key checks, NULL handling\n- Register agtype_jsonb_cast in Makefile REGRESS list\n\nCo-Authored-By: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* Register agtype \u003c-\u003e jsonb casts in upgrade template\n\nThe age_upgrade regression test (added after this PR was originally\nopened) verifies that every SQL object in the install SQL also appears\nin the version upgrade template (age--\u003cINIT\u003e--\u003cCURR\u003e.sql).  Surfaced\nduring rebase onto current master: agtype_to_jsonb / jsonb_to_agtype\nand their casts were missing from the template, causing 4 entries in\nthe \"missing_function\" / \"missing_cast\" check.\n\nAdds the two functions and two casts to the end of the template,\nfollowing the file\u0027s own \"add all additions to the end of this file\"\nconvention.  Cassert installcheck now 34/34 AGE tests green (pgvector\nskipped — env-only).\n\n---------\n\nCo-authored-by: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "01ee9414f9b483782cbf50cfe0744860bcf375ae",
      "tree": "3478b54fda1a562a31aa20172844182e2fb834f4",
      "parents": [
        "a1b749af383dfdf0958ba6c1531b99aeab9207f5"
      ],
      "author": {
        "name": "Ueslei Lima",
        "email": "uesleisantoslima@gmail.com",
        "time": "Wed May 13 12:16:14 2026 -0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 13 08:16:14 2026 -0700"
      },
      "message": "feat(python-driver): add public API for connection pooling and model dict conversion (#2374)\n\n* feat(python-driver): add public API for connection pooling and model dict conversion\n\nAdd two enhancements to the Python driver\u0027s public API:\n\n1. Add configure_connection() function that registers AGE agtype adapters\n   on an existing psycopg connection without creating a new one. This\n   enables use with external connection pools (e.g. psycopg_pool) and\n   managed PostgreSQL services where LOAD \u0027age\u0027 may be restricted.\n   Also explicitly export AgeLoader and ClientCursor as public symbols\n   in age/__init__.py. (#2369)\n\n2. Add to_dict() methods to Vertex, Edge, and Path model classes for\n   conversion to plain Python dicts. This enables direct JSON\n   serialization with json.dumps() without requiring custom conversion\n   logic. (#2371)\n\n   - Vertex.to_dict() returns {id, label, properties}\n   - Edge.to_dict() returns {id, label, start_id, end_id, properties}\n   - Path.to_dict() returns a list of to_dict() results\n\nCloses #2369\nCloses #2371\n\n* Fix configure_connection: correct parameter semantics, add load_from_plugins\n\n- Replace confusing `skip_load` (double-negative) with `load` (positive\n  boolean, default False). The default now correctly matches the intent:\n  no LOAD by default for connection pool / managed PostgreSQL use cases.\n- Add `load_from_plugins` parameter for parity with setUpAge().\n- Fix docstring to accurately describe parameter behavior.\n- Add 6 unit tests for configure_connection covering: default no-load,\n  explicit load, load_from_plugins, search_path always set, adapter\n  registration, and graph_name check delegation.\n\nMade-with: Cursor\n\n* Address review feedback for configure_connection and to_dict\n\n- Move TypeInfo.fetch() inside cursor block so search_path change is\n  visible regardless of transaction isolation mode\n- Raise ValueError when load_from_plugins\u003dTrue but load\u003dFalse\n- Add type annotations to configure_connection signature\n- Document shallow-copy semantics in Vertex/Edge to_dict()\n- Path.to_dict() uses str() fallback for non-AGObj entities to\n  guarantee JSON-serializable output\n- Add test for AgeNotSet when TypeInfo.fetch returns None\n- Add test for load_from_plugins\u003dTrue without load\u003dTrue\n- Replace fragile string assertions with assert_called_with/assert_any_call\n\nMade-with: Cursor\n\n* Fix Path.to_dict() to preserve JSON-native types, add tests to suite\n\n- Path.to_dict(): leave dict/list/str/int/float/bool/None unchanged\n  instead of converting to str(); handle entities\u003dNone safely\n- Add TestModelToDict, TestPublicImports, TestConfigureConnection to\n  the __main__ suite so they run via direct script execution\n\nMade-with: Cursor\n\n* fix(python-driver): Python 3.9-safe hints and correct $user in search_path\n\n- Use Optional[str] for configure_connection graph_name (PEP 604 unions are\n  invalid on Python 3.9).\n- Import Any/Optional from typing for annotations.\n- Quote $user in SET search_path; align unit test expectations.\n\nMade-with: Cursor\n\n* test(python-driver): add configure_connection + to_dict integration test\n\nExisting tests for the new public API are unit-only:\n- TestConfigureConnection mocks the psycopg connection, so it never\n  proves that AgeLoader actually registers against real agtype OIDs.\n- TestModelToDict hand-constructs Vertex/Edge/Path via kwargs, so it\n  never serialises objects produced by the ANTLR parser.\n\nAdd a single TestAgeBasic.testConfigureConnection that:\n- opens a raw psycopg connection (bypassing age.connect()),\n- calls configure_connection(..., load\u003dTrue) on it,\n- runs a Cypher CREATE/RETURN through the configured connection,\n- asserts the returned values are real Vertex/Edge instances and that\n  their to_dict() output is JSON-serialisable with the expected\n  label/start_id/end_id/properties shape,\n- repeats the round-trip for a Path returned by MATCH.\n\nThis is the smallest test that proves the configure_connection +\nto_dict pipeline works end-to-end against a live AGE database.\n\nMade-with: Cursor"
    },
    {
      "commit": "a1b749af383dfdf0958ba6c1531b99aeab9207f5",
      "tree": "b83ca631fbcb0a16389f84427d6e8a42e9a8653b",
      "parents": [
        "e9ef30b177040662a79dcb55307ca3b1a5cf7582"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Tue May 05 14:02:19 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 23:02:19 2026 +0200"
      },
      "message": "Fix upgrade test: allow function removal (#2422)\n\nFix upgrade test: allow function removal and detect more deficiencies.\n\nThe age_upgrade regression test (added in #2364, refined in #2377, #2397,\ninstall and a synthetic-initial -\u003e current upgrade. Three gaps surfaced\nin practice:\n\n1. Function removal forced permanent C stubs.\n   The synthetic \u0027_initial\u0027 install is built from a fixed historical\n   commit. CREATE EXTENSION resolves every CREATE FUNCTION ... AS\n   \u0027$libdir/age\u0027, \u0027\u003csymbol\u003e\u0027 via dlsym at install time when\n   check_function_bodies is on (the default). If a developer retires a\n   C entry point in HEAD\u0027s age.so, step 10 aborts with \"could not find\n   function ... in file age.so\" -- even though the immediately-following\n   ALTER EXTENSION UPDATE would DROP that SQL declaration. The only way\n   to keep the test green was to leave a permanent error-raising stub\n   in age.so, and to remember to add a DROP to the upgrade template.\n\n2. Modifications were under-detected.\n   The function-property-change query did not compare probin or prosrc,\n   so a C function whose symbol was renamed in the upgrade template, or\n   a SQL/plpgsql function whose body changed in either path, slipped\n   through.\n\n3. Extension membership was not checked.\n   A template that CREATEs an object but never ALTER EXTENSION ADDs it\n   leaves a row in pg_proc/pg_class but no pg_depend deptype\u003d\u0027e\u0027 link.\n   pg_dump --extension would diverge, but the existing per-catalog diff\n   queries all returned 0 rows.\n\nChanges (regress/sql/age_upgrade.sql + regress/expected/age_upgrade.out):\n\n* Step 10 wraps the synthetic CREATE EXTENSION in\n  SET check_function_bodies \u003d off; ... RESET check_function_bodies;\n  Symbol resolution is deferred to call time. Step 11\u0027s ALTER EXTENSION\n  UPDATE then DROPs any retired functions before any plan can call them.\n  Step 35\u0027s fresh CREATE EXTENSION runs at the GUC default, so HEAD\u0027s\n  sql/ \u003c-\u003e HEAD\u0027s age.so consistency is still enforced on the production\n  install path.\n\n* Steps 2 and 13 add probin and prosrc to the function snapshot.\n  Step 21 reports probin and prosrc divergences alongside the existing\n  property-change columns.\n\n* Steps 7b and 18b add an extension-membership snapshot from\n  pg_depend deptype\u003d\u0027e\u0027 filtered to the AGE extension OID. Every member\n  is labeled by stable identity (regprocedure, regtype, regoperator,\n  opfname+strategy+types, etc.), never by raw OID, so OID drift between\n  fresh and upgrade installs cannot produce false positives. Steps 33a\n  and 33b report MISSING / EXTRA members. Step 34 adds extmembers_match\n  to the summary row.\n\n* Section-header step ranges updated to include the new sub-steps.\n\nThe change is fully self-contained: only regress/sql/age_upgrade.sql and\nregress/expected/age_upgrade.out are modified. No production C, SQL,\nbuild, or test files are touched. All 34 regression tests pass on a\nclean tree.\n\nMutation-tested with 8 cases against the unmutated tree: baseline pass;\nremove-function-with-DROP pass (no stub needed); remove-function-forget-\nDROP fail; add-function-with-CREATE pass; add-function-forget-CREATE\nfail; volatility-change-no-template fail; volatility-change-with-CREATE-\nOR-REPLACE pass; C-symbol-rename-no-template fail. All eight expected\noutcomes observed.\n\nAll 34 regression tests pass.\n\nCo-authored-by: Claude \u003cnoreply@anthropic.com\u003e\n\nmodified:   regress/expected/age_upgrade.out\nmodified:   regress/sql/age_upgrade.sql"
    },
    {
      "commit": "e9ef30b177040662a79dcb55307ca3b1a5cf7582",
      "tree": "52c3eb125437277704c13c87ec3caa2739bbcca4",
      "parents": [
        "54e19fac9f257ecd173e3dcf647c990e74a28323"
      ],
      "author": {
        "name": "Hari Krishna Sunder",
        "email": "hari90@users.noreply.github.com",
        "time": "Mon May 04 11:23:25 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon May 04 11:23:25 2026 -0700"
      },
      "message": "Zero-initialize parent_cpstate in analyze_cypher (#2423)\n\ncypher_parsestate parent_cpstate is declared on the stack in\nanalyze_cypher() and only pstate is explicitly set before it is passed\nto make_cypher_parsestate(). The latter reads\nparent_cpstate-\u003esubquery_where_flag (and other fields) in\ncypher_parse_node.c, which leaves them with indeterminate values. UBSan\nflagged the garbage bool (value 8) and aborted the backend.\n\nUse MemSet to zero the struct before populating pstate so all remaining\nmembers start with a defined value.\n\nCo-authored-by: Hari Krishna Sunder \u003c12418230+hari90@users.noreply.github.com\u003e"
    },
    {
      "commit": "54e19fac9f257ecd173e3dcf647c990e74a28323",
      "tree": "6b08021af72b952ab1fadfb1f69086c06bea5529",
      "parents": [
        "ff47828b9f1ba474647923bdb0b73299a96a3a79"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Thu Apr 30 00:16:31 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 30 12:16:31 2026 +0500"
      },
      "message": "Fix upgrade test: replace data-integrity checks with catalog comparison (#2403)\n\nThe age_upgrade regression test (added in #2364, improved in #2377, #2397)\nwas designed to validate the upgrade template (age--\u003cVER\u003e--y.y.y.sql) by\ncreating graph data before the upgrade and verifying it survived afterward.\nThis approach had two fundamental problems:\n\n1. It did not detect incomplete upgrade templates. The test verified that\n   graph data (vertices, edges, checksums, GIN indexes) survived ALTER\n   EXTENSION UPDATE, but never checked whether new SQL objects (functions,\n   views, relations, indexes, types, operators, casts, constraints) were\n   actually created by the template. A developer could add a new function\n   to sql/ and sql_files, forget to add it to the upgrade template, and\n   all tests would pass — the function existed via the fresh CREATE\n   EXTENSION install that ran before the upgrade test, but would be\n   missing for users who upgraded via ALTER EXTENSION UPDATE.\n\n2. The data-integrity checks relied on cypher queries (MATCH/RETURN) within\n   the same backend session after DROP EXTENSION + CREATE EXTENSION. This\n   caused intermittent failures on some PostgreSQL versions where AGE\u0027s\n   internal type cache (agtype OID) was not properly refreshed after the\n   extension was dropped and recreated, resulting in \u0027type with OID 0\n   does not exist\u0027 errors. The data-integrity aspect was also redundant —\n   ALTER EXTENSION UPDATE runs DDL statements and does not touch heap data,\n   so data survival is guaranteed by PostgreSQL and not a meaningful test.\n\nThe fix replaces the entire test with a comprehensive catalog comparison:\n\n  1. Snapshot the ag_catalog schema from the fresh install across seven\n     PostgreSQL system catalogs:\n       - pg_proc: functions, aggregates, procedures (name, args, and\n         properties: volatility, strictness, kind, return type, setof)\n       - pg_class: tables, views, sequences, indexes (name, kind)\n       - pg_type: types (name, type category)\n       - pg_operator: operators (name, left/right operand types)\n       - pg_cast: casts involving AGE types (source, target, context)\n       - pg_opclass: operator classes (name, access method)\n       - pg_constraint: constraints (name, type, table, referenced table)\n  2. DROP EXTENSION, CREATE EXTENSION at the synthetic initial version,\n     then ALTER EXTENSION UPDATE to the current version via the stamped\n     upgrade template.\n  3. Snapshot the catalog again after upgrade.\n  4. Compare: any object present in the fresh snapshot but missing after\n     upgrade means the template is incomplete. Any object present after\n     upgrade but not in the fresh snapshot means the template creates\n     something unexpected. Function properties (volatility, strictness,\n     prokind, return type) are also compared for functions that exist in\n     both — catching cases where a CREATE OR REPLACE in the template\n     changes a function\u0027s signature or behavior.\n\nAdditional improvements from code review feedback:\n\n  - Graph cleanup in Step 1 uses a DO block with PERFORM and suppressed\n    NOTICEs to produce deterministic output regardless of prior test state.\n  - The pg_class snapshot includes indexes (relkind \u0027i\u0027) in addition to\n    tables, views, and sequences.\n  - Diagnostic output includes relkind/typtype suffixes for actionable diffs.\n  - Summary uses boolean equality checks (funcs_match, rels_match, etc.)\n    instead of absolute counts, so the expected output does not need\n    updating when new objects are added to AGE. Developers who correctly\n    add objects to both sql/ and the template will never need to modify\n    this test or its expected output.\n\nThis approach:\n  - Catches the actual failure mode: incomplete upgrade templates.\n  - Covers all SQL object categories: functions (including aggregates),\n    relations, types, operators, casts, operator classes, and constraints.\n  - Detects property changes on existing functions (volatility, strictness,\n    kind, return type changes).\n  - Uses only plain SQL catalog queries — no cypher, no .so cache issues.\n  - Works reliably across all PostgreSQL versions.\n  - Reports the exact missing/extra/changed object in the diff output.\n  - Is maintenance-free: no expected output changes needed when AGE grows.\n\nMakefile: updated step 5 comment to reflect catalog comparison approach.\n\nAll 33 regression tests pass.\n\nCo-authored-by: Claude \u003cnoreply@anthropic.com\u003e\n\nmodified:   Makefile\nmodified:   regress/expected/age_upgrade.out\nmodified:   regress/sql/age_upgrade.sql"
    },
    {
      "commit": "ff47828b9f1ba474647923bdb0b73299a96a3a79",
      "tree": "8ed5362a471eebd6a533317898e7f6ce4594853a",
      "parents": [
        "774e781b43878fadf829e185be202dd51eb6def8"
      ],
      "author": {
        "name": "Prashant Chinnam",
        "email": "5108573+crprashant@users.noreply.github.com",
        "time": "Wed Apr 29 15:13:45 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 29 15:13:45 2026 -0700"
      },
      "message": "Allow safe Cypher reserved keywords in alias positions (#2355) (#2415)\n\nCypher productions in `cypher_gram.y` that bind an alias via the AS\nkeyword (RETURN/WITH/YIELD ... AS x and UNWIND ... AS x) only accepted\nplain identifiers. As a result, completely valid Cypher such as\n\n    SELECT * FROM cypher(\u0027g\u0027, $$ RETURN 1 AS count $$) AS (a agtype);\n\nfailed with `syntax error at or near \"count\"`, even though `count` is\nalready accepted in other identifier positions (it appears in the\nexisting `safe_keywords` list and is permitted in `func_name`;\n`schema_name` accepts the broader `reserved_keyword` set).\n\nThis patch introduces a dedicated `var_name_alias` non-terminal used\nonly in the three alias-binding sites (yield_item, return_item,\nunwind). It accepts everything `var_name` accepts, plus the entire\n`safe_keywords` set, so the 49 non-conflicting reserved keywords\n(count, exists, coalesce, match, return, where, order, limit, distinct,\noptional, detach, contains, starts, ends, in, is, not, ...) are now\nusable as aliases.\n\nThe change is intentionally scoped to alias positions:\n\n  * `var_name` itself (used by pattern-variable bindings like\n    `(x:Label)`, edge bindings, named paths, and `expr_var` references)\n    is unchanged. Allowing safe_keywords there triggers 156 shift/reduce\n    conflicts because keyword tokens collide with their roles inside\n    expressions and patterns.\n  * `conflicted_keywords` (END, NULL, TRUE, FALSE) remain rejected in\n    every position; they are genuinely ambiguous with literal/CASE\n    productions.\n\nReading a keyword-named alias back through `expr_var` still fails (e.g.\n`WITH 1 AS count RETURN count`) because `expr_var` reads through\n`var_name`. That asymmetry is captured as a known limitation in the\nregression suite and tracked separately in #2416.\n\nRegression coverage lives in `regress/sql/reserved_keyword_alias.sql`\nand `regress/expected/reserved_keyword_alias.out`, exercising:\n\n  * the original repro,\n  * representative safe_keywords across RETURN/WITH/UNWIND,\n  * multiple keyword aliases in one projection,\n  * a backtick-quoted alias positive case,\n  * the known read-back limitation as a negative test, and\n  * explicit negatives proving END/NULL/TRUE/FALSE and pattern-position\n    keywords still error out.\n\nCloses #2355."
    },
    {
      "commit": "774e781b43878fadf829e185be202dd51eb6def8",
      "tree": "25d4325c5b9ba470b392db3fb5eb811077fb774b",
      "parents": [
        "22f4c94fb821376d9b4de5b31dc3240225c187fa"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Mon Apr 27 23:35:00 2026 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 27 11:35:00 2026 -0700"
      },
      "message": "Propagate null through agtype arithmetic operators (#2405)\n\nThe scalar branch of agtype_add lacked an AGTV_NULL case and fell through\nto the scalar-scalar concat path, so `null + 1` produced the two-element\nlist `[null, 1]` instead of null. This surfaced most visibly inside a\nlist comprehension projection (e.g. `[x IN [1, null, 2] | x + 1]` yielded\n`[2, [null, 1], 3]`) because there both operands are typed agtype and\nbypass the agtype_any_add wrapper that already short-circuits on null.\n\nThe sibling operators (-, *, /, %, ^, unary -) had the same gap but\nsurfaced as \"Invalid input parameter types\" errors rather than concats.\n\nShort-circuit all seven functions to SQL NULL when any operand is\nAGTV_NULL. SQL NULL (not a wrapped AGTV_NULL scalar) matches AGE\u0027s\nexisting convention: `RETURN null AS v` already yields SQL NULL at the\nrow level, and agtype_any_add already returns PG NULL for AGTV_NULL\ninput, so the two operator-resolution paths stay indistinguishable.\nList aggregation re-packs SQL NULL entries as AGTV_NULL, so\n`[x IN [1, null, 2] | x + 1]` now renders as `[2, null, 3]`.\n\nCo-authored-by: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "22f4c94fb821376d9b4de5b31dc3240225c187fa",
      "tree": "ff379a39906ca36ba6f4a8c38bfa73cf38f56911",
      "parents": [
        "9f9d0f3b18b231d229b17b3994a907aba7519695"
      ],
      "author": {
        "name": "Prashant Chinnam",
        "email": "5108573+crprashant@users.noreply.github.com",
        "time": "Mon Apr 27 11:32:27 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 27 11:32:27 2026 -0700"
      },
      "message": "Update README.md (#2414)\n\ndocs: Clarify transaction/commit semantics with non-autocommit clients\r\n\r\nAdds a \"Using AGE with Non-Autocommit Clients\" section explaining\r\nPostgreSQL transaction visibility rules as they apply to AGE DDL-like\r\nfunctions (create_graph, create_vlabel, etc.), with broken/fixed\r\npsycopg v3 examples and a JDBC note.\r\n\r\nRefs apache/age#2195"
    },
    {
      "commit": "9f9d0f3b18b231d229b17b3994a907aba7519695",
      "tree": "098de15a3e105f04ce4ba46ccdb60baa7eec9a9a",
      "parents": [
        "5a74048725ac6ddbe7bc15377c3a42cb0e31f22b"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Fri Apr 24 20:15:43 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Apr 24 17:15:43 2026 -0700"
      },
      "message": "Add VLE semantics and cost model design note (#2349) (#2413)\n\nAdds a block comment at the top of age_vle.c documenting (a) the\nedge-isomorphism semantics that openCypher mandates for variable-length\nrelationship matching, (b) the cost model distinction between bounded\nand unbounded patterns, and (c) a pointer to the enforcement site\n(edge_state_entry.used_in_path, dfs_find_a_path_*, is_edge_in_path).\n\nMotivation: issue #2349 misdiagnosed the VLE as lacking cycle detection\nand proposed a visited-node filter as a fix. Cycle prevention already\nexists via edge-uniqueness, and swapping to vertex-isomorphism would\nsilently drop spec-valid paths (e.g. triangle traversals where the\nendpoint coincides with the start vertex). Capturing the semantics and\ncost model in-source prevents future readers from repeating the same\nmisanalysis.\n\nNo behavior change. Cassert installcheck: 33/33."
    },
    {
      "commit": "5a74048725ac6ddbe7bc15377c3a42cb0e31f22b",
      "tree": "a1e356427a8b2a98e022f0b87ae8c55f1d3ffc8d",
      "parents": [
        "84e29542f0d54c9d848a3d5ebbc7a8f4d5a67437"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Sat Apr 25 05:10:31 2026 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Apr 24 17:10:31 2026 -0700"
      },
      "message": "Fix property access on list comprehension / predicate loop variables (#2402)\n\ntransform_column_ref_for_indirection errored with \"could not find\nproperties for \u003cname\u003e\" when the referenced RTE had no \"properties\"\ncolumn (as is the case for the unnest() RTE used by list comprehension\nand the any/all/none/single predicate functions), breaking queries like\n[x IN list | x.name] and any(x IN list WHERE x.n \u003e 1).\n\nReturn NULL instead so the caller continues transforming the ColumnRef\nas an agtype value and applies the indirection via agtype_access_operator.\n\nCo-authored-by: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "84e29542f0d54c9d848a3d5ebbc7a8f4d5a67437",
      "tree": "671b855dbe739d201839b0de85e9b3aa4ccb0688",
      "parents": [
        "6f520fedda7885efcfc104b925338cdfa933f935"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Wed Apr 22 05:06:59 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 22 14:06:59 2026 +0500"
      },
      "message": "Add MERGE ON CREATE SET / ON MATCH SET support (#2347)\n\n- Add MERGE ON CREATE SET / ON MATCH SET support\n\nImplements the openCypher-standard ON CREATE SET and ON MATCH SET\nclauses for the MERGE statement. This allows conditional property\nupdates depending on whether MERGE created a new path or matched\nan existing one:\n\n  MERGE (n:Person {name: \u0027Alice\u0027})\n    ON CREATE SET n.created \u003d timestamp()\n    ON MATCH SET n.updated \u003d timestamp()\n\nImplementation spans parser, planner, and executor:\n\n- Grammar: new merge_actions_opt/merge_actions/merge_action rules\n  in cypher_gram.y, with ON keyword added to cypher_kwlist.h\n- Nodes: on_match/on_create lists on cypher_merge, corresponding\n  on_match_set_info/on_create_set_info on cypher_merge_information,\n  and prop_expr on cypher_update_item (all serialized through\n  copy/out/read funcs)\n- Transform: cypher_clause.c transforms ON SET items and stores\n  prop_expr for direct expression evaluation\n- Executor: cypher_set.c extracts apply_update_list() from\n  process_update_list(); cypher_merge.c calls it at all merge\n  decision points (simple merge, terminal, non-terminal with\n  eager buffering, and first-clause-with-followers paths)\n\nKey design choice: prop_expr stores the Expr* directly in\ncypher_update_item rather than using prop_position into the scan\ntuple. The planner strips target list entries for SET expressions\nthat CustomScan doesn\u0027t need, making prop_position references\ndangling. By storing the expression directly (only for MERGE ON\nSET items), we evaluate it with ExecInitExpr/ExecEvalExpr\nindependent of the scan tuple layout.\n\nIncludes regression tests covering: basic ON CREATE SET, basic\nON MATCH SET, combined ON CREATE + ON MATCH, multiple SET items,\nexpression evaluation, interaction with WITH clause, and edge\nproperty updates.\n\n- Move ExecInitExpr for ON CREATE/MATCH SET items from per-row\n  execution in apply_update_list() to plan initialization in\n  begin_cypher_merge(). Follows the established pattern used by\n  cypher_target_node (id_expr_state, prop_expr_state).\n- Add prop_expr_state field to cypher_update_item with serialization\n  support in outfuncs/readfuncs/copyfuncs.\n- apply_update_list() uses pre-initialized state when available,\n  falls back to per-row init for plain SET callers.\n- Fix misleading comment: \"ON MATCH SET\" → \"ON CREATE SET\" for Case 1\n  first-run test.\n- Add Case 1 second-run test that triggers ON MATCH SET with a\n  predecessor clause (MATCH ... MERGE ... ON MATCH SET).\n- Add ON to safe_keywords in cypher_gram.y so that property keys\n  and labels named \u0027on\u0027 still work (e.g., n.on, MATCH (n:on)).\n  All other keywords added as tokens are also in safe_keywords.\n- Add chained (non-terminal) MERGE regression tests exercising the\n  eager-buffering code path with ON CREATE SET and ON MATCH SET.\n  First run creates both nodes (ON CREATE SET fires), second run\n  matches both (ON MATCH SET fires).\n- Move ExecStoreVirtualTuple before apply_update_list unconditionally in\n  Case 1 non-terminal and terminal MERGE paths, matching the pattern at\n  Case 3 (line 994). Ensures tts_nvalid is set for downstream ExecProject\n  even when ON CREATE SET is absent.\n\n- Add resolve_merge_set_exprs() helper to deduplicate the prop_expr\n  resolution loops for ON MATCH SET and ON CREATE SET. Includes ereport\n  when target entry is missing (internal error, should never happen).\n\n- Add regression test for ON keyword as label name, confirming backward\n  compatibility via safe_keywords grammar path.\n- The four ExecStoreVirtualTuple calls in exec_cypher_merge were triggering\n  an Assert failure under --enable-cassert:\n\n    TRAP: failed Assert(\"TTS_EMPTY(slot)\"), File: execTuples.c, Line: 1748\n\nExecStoreVirtualTuple (execTuples.c:1748) asserts that its target slot\nis in the TTS_EMPTY state.  In our MERGE executor, process_path writes\ndirectly into the subquery\u0027s scan tuple slot -- which already holds the\nsubquery\u0027s output tuple and therefore is NOT empty.  On a release build\nthe assertion compiles out and ExecStoreVirtualTuple just clears the flag\nand sets tts_nvalid; on an --enable-cassert build the backend aborts and\ntakes down the regression run.\n\nWe only need the bookkeeping half of ExecStoreVirtualTuple (clear\nTTS_FLAG_EMPTY and set tts_nvalid \u003d natts) -- not the \"store semantics\"\nthat motivate the assertion.  Add a small static helper\nmark_scan_slot_valid() that does exactly the bookkeeping, and replace\nthe four call sites.  Release-build behavior is byte-identical since\nAssert() compiles to nothing; cassert-build behavior now matches release.\n\n- Fix MERGE ON CREATE/MATCH SET crash when RHS references a bound variable\n\nWhen MERGE has a previous clause (e.g. MATCH, UNWIND), transform_cypher_merge\ntakes the lateral-left-join path via transform_merge_make_lateral_join. That\nhelper called addRangeTableEntryForJoin with nscolumns\u003dNULL, leaving the join\nParseNamespaceItem\u0027s p_nscolumns unset. For queries that did not subsequently\nresolve a column reference against that nsitem (e.g. RETURN, which runs in a\nfresh namespace built by handle_prev_clause), the NULL was harmless.\n\nOur ON CREATE / ON MATCH SET transform runs in-line, before the MERGE query\nbecomes a subquery, so transform_cypher_set_item_list consulted the join\u0027s\nnsitem directly. colNameToVar -\u003e scanNSItemForColumn then dereferenced\np_nscolumns[attnum-1] \u003d NULL[0] and the backend segfaulted on any ON SET\nwhose RHS referenced a bound variable.\n\nPopulate the join\u0027s p_nscolumns from res_colvars. The Var we end up\nproducing for a bound entity lives inside prop_expr, which is opaque to\nthe planner, so it is not rewritten to match the plan\u0027s output slots. At\nExecEvalScalarVar time only varattno is consulted, and scantuple\u0027s layout\nmirrors the join\u0027s eref-\u003ecolnames (via make_target_list_from_join). Use\nthe join rtindex and 1-based eref position so scantuple[varattno - 1]\nresolves to the correct entity column at runtime; without this, Vars for\na (varno\u003dl_rte) and b (varno\u003dr_rte) with varattno\u003d1 both hit\nscantuple[0] and b.id evaluated to a.id.\n\nAlso initialise apply_update_list\u0027s new_property_value at its declaration.\nAll control paths reach the single alter_property_value call with the\nvariable set, but -Wmaybe-uninitialized fires at -O2 because the compiler\ncannot prove remove_property \u003d\u003d isnull when prop_expr is non-NULL.\n---------\n\nCo-authored-by: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "6f520fedda7885efcfc104b925338cdfa933f935",
      "tree": "04cdb095afd6379058f284bea99b5ee4502ee614",
      "parents": [
        "798917c23e88b5297f5b8f1862418302e7953988"
      ],
      "author": {
        "name": "Sai Asish Y",
        "email": "say.apm35@gmail.com",
        "time": "Tue Apr 21 02:49:59 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 21 14:49:59 2026 +0500"
      },
      "message": "Propagate null in agtype_access_slice bounds (#2400)\n\n* agtype: propagate null through list slice bounds in agtype_access_slice instead of treating AGTV_NULL as omitted\n\nagtype_access_slice() decides what to do with each bound in two steps:\n\n  1. If the SQL argument is NULL (`PG_ARGISNULL(1|2)`), the caller did\n     not supply that bound at all (e.g. `list[..2]`) - treat it as\n     \"no bound\" and fall back to 0 / array_size. This is correct.\n\n  2. Otherwise the argument is non-NULL agtype. If *the agtype value*\n     inside happens to be AGTV_NULL (e.g. `list[null..2]`), the\n     existing code also treated it as \"no bound\" and returned the full\n     slice. This is wrong. Under Cypher null-propagation semantics\n     `list[a..b]` is null whenever either bound is null; Neo4j and\n     Memgraph both return null, and differential testing against both\n     flagged the divergence.\n\nChange step 2 to PG_RETURN_NULL() when the supplied bound is\nAGTV_NULL. Step 1 is untouched, so `[..n]` / `[n..]` still work as\nbefore.\n\nRegression tests are updated accordingly:\n\n  * The two pre-existing agtype_access_slice() calls with an\n    explicit \u0027null\u0027::agtype argument now expect the null result.\n\n- The patch only fixes agtype_access_slice function, not direct\n  list slices in cypher query.\n\nCo-developed with an AI coding assistant (Claude Code).\nSigned-off-by: SAY-5 \u003cSAY-5@users.noreply.github.com\u003e"
    },
    {
      "commit": "798917c23e88b5297f5b8f1862418302e7953988",
      "tree": "f8b3ec998924f174809a86dd83d937c09b1d9274",
      "parents": [
        "ee25e2a381271d60d0fae9ff21a78f8a539bca3d"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Tue Apr 21 00:50:23 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 21 12:50:23 2026 +0500"
      },
      "message": "VLE cache + performance improvements (#2376)\n\nVLE cache + perf: version counter, thin entries, variadic elimination,\nedge cleanup list removal\n\nReplace snapshot-based VLE cache invalidation with per-graph version\ncounters and add performance optimizations for VLE traversal.\n\nCache invalidation:\n- Add DSM (PG 17+) and shmem (PG \u003c17) per-graph monotonic version\n  counters for cross-backend cache invalidation\n- Replace snapshot xmin/xmax/curcid comparison with version counter\n  check in is_ggctx_invalid(), with snapshot fallback for safety\n- Add executor hooks in CREATE/DELETE/SET/MERGE to increment the\n  graph version counter on mutations\n- Add SQL trigger function (age_invalidate_graph_cache) for catching\n  SQL-level mutations (INSERT/UPDATE/DELETE/TRUNCATE)\n- Auto-install trigger on new label tables via label_commands.c\n  with LookupFuncName check for backward compatibility\n- Add TRUNCATE interception in ProcessUtility hook (ag_catalog.c)\n- Add shmem_request/startup hooks for PG \u003c17 (age.c)\n- Use search_label_relation_cache() in get_graph_oid_for_table()\n  for fast label-to-graph lookup instead of catalog table scan\n\nThin entries (lazy property fetch):\n- Replace Datum edge_properties/vertex_properties with 6-byte\n  ItemPointerData TID in vertex_entry and edge_entry\n- Add get_vertex_entry_properties() and get_edge_entry_properties()\n  that do heap_fetch via stored TID on demand\n- Add is_an_edge_match() fast path: skip property access entirely\n  for label-only VLE patterns (the common case)\n\nEdge cleanup list removal:\n- Remove ggctx-\u003eedges linked list from GRAPH_global_context. With\n  thin entries, edge_entry has no palloc\u0027d sub-structures to free\n  (TIDs are inline). The cleanup list existed to pfree datumCopy\u0027d\n  edge_properties Datums, which no longer exist. hash_destroy()\n  handles all edge hash table memory. Saves ~5.6 GB at SF10.\n\nPerformance optimizations:\n- Reduce VERTEX/EDGE_HTAB_INITIAL_SIZE from 1,000,000 to 10,000.\n  PG dynahash grows automatically; large initial size wastes memory.\n- Eliminate extract_variadic_args in age_match_vle_terminal_edge:\n  direct PG_GETARG_DATUM + cached arg types via fn_extra\n- Eliminate extract_variadic_args in age_match_vle_edge_to_id_qual:\n  same pattern\n- Add agtype_access_operator 2-arg fast path: bypasses\n  extract_variadic_args_min for the common property access case\n- Add age_tointeger 1-arg fast path: bypasses extract_variadic_args\n  with cached arg type\n- Add GraphIdStack (flat array-based) DFS stacks replacing\n  ListGraphId linked-list stacks for push/pop without palloc/pfree\n\nUpgrade script:\n- Add trigger installation on pre-existing label tables during\n  extension upgrade via DO block in age--1.7.0--y.y.y.sql. Tables\n  created after upgrade get triggers automatically via label_commands.c.\n\nRegression tests:\n- Add VLE cache invalidation tests (CREATE, DELETE, SET mutations)\n- Add thin entry edge property fetch tests (RETURN p, UNWIND)\n- Add direct SQL trigger tests (INSERT, UPDATE, DELETE, TRUNCATE on\n  label tables with VLE cache invalidation verification)\n\nAll 32 regression tests pass.\n\nSF3 Benchmarks (9.3M vertices, 52.7M edges, warm cache, median):\n\nTotal IC1-IC12: 1,530s -\u003e 1,159s (-24.3%, 371s saved)\n\nVLE-heavy queries:\n  IC3  (KNOWS*1..2 + 2 countries):    34.9s -\u003e 20.7s  (-40.6%)\n  IC5  (forum members, KNOWS*1..2):   46.2s -\u003e 29.9s  (-35.4%)\n  IC6  (tag co-occurrence, KNOWS*1..2): 28.2s -\u003e 19.2s (-31.8%)\n  IC9  (recent messages, KNOWS*1..2): 86.0s -\u003e 60.4s  (-29.7%)\n  IC11 (KNOWS*1..2 + WORK_AT):       18.7s -\u003e 11.0s  (-41.5%)\n  IC1  (KNOWS*1..3 + profile):     1269.4s -\u003e 974.9s  (-23.2%)\n\nShort Reads (IS1-IS7): No meaningful change -- non-VLE queries,\nsub-millisecond to sub-second. Within run-to-run variance.\n\nUpdates (IU1-IU8, SF3, median, 50 ops each):\n\n  IU2 (Like Post):     99.5ms -\u003e  6.3ms  (-93.6%)\n  IU3 (Like Comment): 318.3ms -\u003e  5.7ms  (-98.2%)\n  IU7 (Add Comment):  344.4ms -\u003e 11.3ms  (-96.7%)\n  IU5 (Forum Member):  12.3ms -\u003e  5.9ms  (-52.0%)\n\nVersion counter eliminates redundant VLE cache rebuilds on mutations.\nPreviously, every INSERT/UPDATE/DELETE invalidated the cache via\nsnapshot comparison, forcing a full rebuild on the next VLE query.\nNow, mutations just bump a counter; rebuild only occurs when VLE\nactually runs and finds the counter changed.\n\nGraph cache memory (SF3, calculated):\n\nTotal: ~15.7 GB -\u003e ~8.7 GB (-45%, 7.0 GB saved)\n\n  Thin entries (TID replaces datumCopy\u0027d properties):  -5.3 GB\n  Edge cleanup list removal (no longer needed):        -1.7 GB\n\nCo-authored-by: Claude Opus \u003cnoreply@anthropic.com\u003e\n\nmodified:   age--1.7.0--y.y.y.sql\nmodified:   regress/expected/age_global_graph.out\nmodified:   regress/sql/age_global_graph.sql\nmodified:   sql/age_main.sql\nmodified:   src/backend/age.c\nmodified:   src/backend/catalog/ag_catalog.c\nmodified:   src/backend/commands/label_commands.c\nmodified:   src/backend/executor/cypher_create.c\nmodified:   src/backend/executor/cypher_delete.c\nmodified:   src/backend/executor/cypher_merge.c\nmodified:   src/backend/executor/cypher_set.c\nmodified:   src/backend/utils/adt/age_global_graph.c\nmodified:   src/backend/utils/adt/age_graphid_ds.c\nmodified:   src/backend/utils/adt/age_vle.c\nmodified:   src/backend/utils/adt/agtype.c\nmodified:   src/include/utils/age_global_graph.h\nmodified:   src/include/utils/age_graphid_ds.h"
    },
    {
      "commit": "ee25e2a381271d60d0fae9ff21a78f8a539bca3d",
      "tree": "fff30bb506a30ecfe964d3a79d6a807d949c2744",
      "parents": [
        "26bd4f4d05e519cc340630bb71dc82e419485f31"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Mon Apr 20 20:31:39 2026 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 20 08:31:39 2026 -0700"
      },
      "message": "CI: fail build on compiler and bison warnings (#2398)\n\n- installcheck.yaml: build with COPT\u003d-Werror so compiler warnings fail CI\n- Makefile: add -Werror to BISONFLAGS for cypher_gram.c so bison\n  conflicts/warnings fail the build"
    },
    {
      "commit": "26bd4f4d05e519cc340630bb71dc82e419485f31",
      "tree": "3ef176a7e8c1a1d660977e680eb7f369759c83a2",
      "parents": [
        "908d7b29811a23d7b01c024af5d6697fe670e123"
      ],
      "author": {
        "name": "Ueslei Lima",
        "email": "uesleisantoslima@gmail.com",
        "time": "Mon Apr 20 07:41:03 2026 -0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 20 15:41:03 2026 +0500"
      },
      "message": "fix(python-driver): add null-guards in ANTLR parser and relax runtime version pin (#2372)\n\nFix two related issues in the Python driver\u0027s ANTLR4 parsing pipeline:\n\n- Add null-guards in ResultVisitor methods (visitAgValue, visitFloatLiteral,\n  visitPair, visitObj, handleAnnotatedValue) to prevent AttributeError crashes\n  when the ANTLR4 parse tree contains None child nodes. This occurs with\n  vertices that have complex properties (large arrays, special characters,\n  deeply nested structures).\n\n- Relax antlr4-python3-runtime version constraint from \u003d\u003d4.11.1 to\n  \u003e\u003d4.11.1,\u003c5.0 in both pyproject.toml and requirements.txt. The 4.11.1\n  pin is incompatible with Python \u003e\u003d 3.13. The ANTLR ATN serialized format\n  is unchanged between 4.11 and 4.13, so the generated lexer/parser files\n  are compatible. Validated with antlr4-python3-runtime\u003d\u003d4.13.2 on\n  Python 3.11-3.14.\n\n- Also replaces shadowing of builtin \u0027dict\u0027 in handleAnnotatedValue with \u0027d\u0027,\n  and uses .get() for safer key access on parsed vertex/edge dicts.\n\n- Add tests for malformed/truncated agtype input handling. \n  Verify that malformed and truncated agtype strings raise AGTypeError\n  (or recover gracefully) rather than crashing with AttributeError.\n  This tests the null-guards added to the ANTLR parser visitor.\n\n- visitFloatLiteral: raise AGTypeError on malformed child node instead\n  of silently returning a fallback value\n\n- visitObj: add comment documenting that visitPair\u0027s validation makes\n  the None-guard defensive-only\n\n- handleAnnotatedValue: add comment explaining partial-construction\n  behavior on type-check failure\n\n- pyproject.toml: add comment explaining ANTLR4 version range rationale\n\n- Tests: assert AGTypeError (or graceful recovery) for malformed and\n  truncated inputs, not just absence of AttributeError\n\n- handleAnnotatedValue: default properties to {} when missing from\n  parsed dict, preventing __getitem__ crashes on access\n\n- Tests: replace weak assertNotIsInstance with structural type checks\n\n- Fix truncated test docstring to match actual assertion behavior\n\n- Use PostgreSQL \"$user\" placeholder in SET search_path.\n\n- Exercise real escapes and Unicode in special-characters vertex test (json.dumps).\n\n- Add Python 3.9 trove classifier to match requires-python and dependency comment.\n\n- Build vertex agtype with string concat to avoid invalid f-string braces.\n\n- Assert stored description matches parser behavior: JSON escapes remain\n  literal, UTF-8 decodes normally (ensure_ascii\u003dFalse on json.dumps).\n\n- Regenerate parser with ANTLR 4.13.2 to silence runtime-version-mismatch warning. \n  The generated lexer/parser were hardcoded to check for ANTLR runtime\n  4.11.1, which triggered a noisy \u0027ANTLR runtime and generated code\n  versions disagree\u0027 warning when installed against a newer runtime like\n  4.13.2. Regenerating from Agtype.g4 with the 4.13.2 tool aligns the\n  generated checkVersion() call with the default-installed runtime in\n  the allowed dependency range and eliminates the warning.\n\n- Bumps the declared floor of antlr4-python3-runtime to 4.13.2 so the\n  default install path is warning-free.\n\nMade-with: Cursor"
    },
    {
      "commit": "908d7b29811a23d7b01c024af5d6697fe670e123",
      "tree": "62c73ff47abcec18fb33ba6a249610778c870594",
      "parents": [
        "6c4083862fbf59309a7735aafa7ab1def1bda13d"
      ],
      "author": {
        "name": "Sai Asish Y",
        "email": "say.apm35@gmail.com",
        "time": "Mon Apr 20 02:32:54 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 20 14:32:54 2026 +0500"
      },
      "message": "Return an empty list from tail() for empty and singleton lists (#2399)\n\nage_tail() in src/backend/utils/adt/agtype.c had an explicit early return\nthat mapped both the empty-list and the singleton-list cases to SQL NULL:\n\n    count \u003d AGT_ROOT_COUNT(agt_arg);\n    /* if we have an empty list or only one element in the list, return null */\n    if (count \u003c\u003d 1)\n    {\n        PG_RETURN_NULL();\n    }\n\nThe Cypher specification (and Neo4j / Memgraph) both\ndefine tail(list) as \"the list without its first element\". For a\nzero-or-one-element input that is an empty list [], not null. Differential\ntests against Neo4j surface this divergence immediately.\n\nRemove the early return. The loop below already iterates from i\u003d1 so it\nnaturally emits [] for count \u003c\u003d 1 and emits the correct tail for the\ngeneral case. No behaviour change for count \u003e\u003d 2. Tests and expected\noutput are updated to match the new, spec-correct result."
    },
    {
      "commit": "6c4083862fbf59309a7735aafa7ab1def1bda13d",
      "tree": "09348b7d3a34571e485dede17dae40cd46728bc5",
      "parents": [
        "bdc8b6ddabe210ec02a77d232b742ced9c0d438e"
      ],
      "author": {
        "name": "Sai Asish Y",
        "email": "say.apm35@gmail.com",
        "time": "Mon Apr 20 02:19:01 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 20 14:19:01 2026 +0500"
      },
      "message": "Fix substring() crash when start-offset is NULL and length is supplied (#2401)\n\nage_substring() reads the null map produced by extract_variadic_args()\nand rejects null offset/length with this guard:\n\n    if ((nargs \u003d\u003d 2 \u0026\u0026 nulls[1]) ||\n        (nargs \u003d\u003d 3 \u0026\u0026 nulls[2]))\n    {\n        ereport(ERROR, ..., errmsg(\"substring() offset or length cannot be null\"));\n    }\n\nThe condition only checks nulls[1] in the 2-argument form. When the\ncaller passes `substring(str, null, len)` the function takes nargs \u003d 3,\nnulls[1] \u003d true, but the guard above does not fire. Execution reaches\nthe numeric-parameter loop below, which reads args[1] through\nDatumGetInt32 / DATUM_GET_AGTYPE_P without ever re-checking nulls[i].\nThe Datum in that slot is undefined, the dereference segfaults, and\nthe PostgreSQL backend terminates - not a query error but a\nconnection-level crash (#2386).\n\nWiden the guard to nargs \u003e\u003d 2 \u0026\u0026 nulls[1] so it catches start-is-null\nin both the 2-arg and 3-arg forms. nulls[2] is still only checked\nwhen nargs \u003d\u003d 3. No behaviour change on any non-null path; the\nconnection-crash case is now reported as a normal query error,\nmatching the intent the existing error message already implies."
    },
    {
      "commit": "bdc8b6ddabe210ec02a77d232b742ced9c0d438e",
      "tree": "a427e4d5b526618f1aeca478d42d4cbc7ef0a969",
      "parents": [
        "4f9db377cc5576403e891ba67f2c7d103aaebcd7"
      ],
      "author": {
        "name": "Ueslei Lima",
        "email": "uesleisantoslima@gmail.com",
        "time": "Mon Apr 20 06:16:33 2026 -0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 20 14:16:33 2026 +0500"
      },
      "message": "fix(python-driver): quote-escape column aliases in buildCypher() (#2373)\n\n* fix(python-driver): quote-escape column aliases in buildCypher()\n\nAlways double-quote column names in the AS (...) clause generated by\nbuildCypher() and _validate_column(). This prevents PostgreSQL parse\nerrors when column aliases happen to be reserved words (e.g. \u0027count\u0027,\n\u0027order\u0027, \u0027type\u0027, \u0027group\u0027, \u0027select\u0027).\n\nBefore: SELECT * from cypher(NULL,NULL) as (count agtype);\nAfter:  SELECT * from cypher(NULL,NULL) as (\"count\" agtype);\n\nPostgreSQL always accepts double-quoted identifiers, so quoting\nunconditionally is safe for all names — no reserved word list needed.\n\nCloses #2370\n\n* Address review feedback: document type quoting, improve tests\n\n- _validate_column: add comment explaining why type_name is\n  intentionally left unquoted (PG type names are case-insensitive)\n- buildCypher: fix graphName \u003d\u003d None to idiomatic \u0027is None\u0027\n- test_reserved_word_count: replace fragile assertNotIn with regex\n- Add test for reserved word in \"name type\" pair (e.g. \"order agtype\")\n- Add comment explaining intentional _validate_column private import\n\n* Fix test_security.py for quoted column names, improve comment\n\n- Update test_security.py assertions to expect double-quoted column\n  names (consistent with the always-quote-column-names change)\n- Reword type_name comment to clarify case-folding semantics\n\nMade-with: Cursor"
    },
    {
      "commit": "4f9db377cc5576403e891ba67f2c7d103aaebcd7",
      "tree": "c5103d4abb9e80556dec90931521bb3a359d1927",
      "parents": [
        "15030a038278bf5ad0355ce2b80ccacc27bb3dc2"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Sun Apr 19 09:26:47 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Apr 19 18:26:47 2026 +0500"
      },
      "message": "Implement predicate functions: all(), any(), none(), single() (#2359)\n\n* Implement predicate functions: all(), any(), none(), single()\n\nImplement the four openCypher predicate functions (issues #552, #553,\n#555, #556) that test list elements against a predicate:\n\n  all(x IN list WHERE predicate)    -- true if all elements match\n  any(x IN list WHERE predicate)    -- true if at least one matches\n  none(x IN list WHERE predicate)   -- true if no elements match\n  single(x IN list WHERE predicate) -- true if exactly one matches\n\nImplementation approach:\n- Add cypher_predicate_function node type with CPFK_ALL/ANY/NONE/SINGLE\n  kind enum, reusing the list comprehension\u0027s unnest-based transformation\n- Grammar rules in expr_func_subexpr (alongside EXISTS, COALESCE, COUNT)\n- Transform to efficient SQL sublinks:\n  all()   -\u003e NOT EXISTS (SELECT 1 FROM unnest WHERE NOT pred)\n  any()   -\u003e EXISTS (SELECT 1 FROM unnest WHERE pred)\n  none()  -\u003e NOT EXISTS (SELECT 1 FROM unnest WHERE pred)\n  single() -\u003e (SELECT count(*) FROM unnest WHERE pred) \u003d 1\n- Three new keywords (ANY_P, NONE, SINGLE) added to safe_keywords for\n  backward compatibility as property keys and label names\n- Shared extract_iter_variable_name() helper for variable validation\n\nAll 32 regression tests pass. New predicate_functions test covers basic\nsemantics, empty lists, graph data integration, boolean combinations,\nnested predicates, and keyword backward compatibility.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* Address Copilot review: NULL semantics, iterator validation, single() perf, tests\n\n- Rewrite predicate functions from EXISTS_SUBLINK to EXPR_SUBLINK with\n  aggregate-based CASE expressions (bool_or + IS TRUE/FALSE/NULL) to\n  preserve three-valued Cypher NULL semantics\n- Add list_length check in extract_iter_variable_name() to reject\n  qualified names like x.y as iterator variables\n- Add copy/read support for cypher_predicate_function ExtensibleNode\n  to prevent query rewriter crashes\n- Use IS TRUE filtering in single() count (LIMIT 2 optimization\n  breaks correlated variable refs in graph contexts -- documented)\n- Add 13 NULL regression tests: null list input, null elements,\n  null predicates for all four functions\n\nCo-Authored-By: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* Address Copilot round 2: NULL-list guard, single() comment, pg_aggregate.h\n\n1. Add NULL-list guard for all predicate functions (all/any/none/single).\n   Wraps the result with CASE WHEN list IS NULL THEN NULL ELSE \u003cresult\u003e\n   END in the grammar layer.  This fixes single(x IN null WHERE ...)\n   returning false instead of NULL.  The expr pointer is safely shared\n   between the NullTest and the predicate function node because AGE\u0027s\n   expression transformer creates new nodes without modifying the\n   parse tree in-place.\n\n2. Fix single() block comment in transform_cypher_predicate_function:\n   described LIMIT 2 optimization but implementation uses plain\n   count(*).  Updated comment to match actual implementation.\n\n3. Keep #include \"catalog/pg_aggregate.h\" -- Copilot suggested removal\n   but AGGKIND_NORMAL macro requires it (build fails without it).\n\nRegression test: predicate_functions OK.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* Address Copilot round 3: reuse extract_iter_variable_name for list comprehensions\n\n- Refactor build_list_comprehension_node() to reuse the shared\n  extract_iter_variable_name() helper, so `var IN list` validation\n  is consistent between list comprehensions and predicate functions\n  (all/any/none/single). Qualified ColumnRefs like `x.y IN list`\n  are now rejected in list comprehensions the same way they are\n  in predicate functions.\n- Update list_comprehension expected output for the normalized\n  lowercase \"syntax error at or near IN\" message.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "15030a038278bf5ad0355ce2b80ccacc27bb3dc2",
      "tree": "2c69ae6822b7b673900d0b7f6da3d9c68072d432",
      "parents": [
        "ce5004593ea5acb2cec3a2dc94a06241d7f23c17"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Sun Apr 19 08:48:25 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Apr 19 17:48:25 2026 +0500"
      },
      "message": "Fix OPTIONAL MATCH dropping null-preserving rows with subquery WHERE (#2380)\n\n* Fix OPTIONAL MATCH dropping null-preserving rows with subquery WHERE\n\nCypher OPTIONAL MATCH semantics require that when no right-hand row\nsurvives the WHERE predicate, the outer row is still emitted with\nNULLs in the optional columns.  Before this fix, a WHERE containing\na list comprehension or sub-pattern predicate (EXISTS { ... },\nCOUNT { ... }) would take the transform_cypher_clause_with_where\nrewrite path, which detaches the WHERE, transforms the match clause\nas a subquery, and then attaches the WHERE as an outer filter on that\nsubquery.  For OPTIONAL MATCH, the inner subquery already produced a\nLATERAL LEFT JOIN with null-preserving rows; the outer filter then\nran against those nulled rows and dropped them when the predicate\nevaluated NULL or false on the nulled side, producing zero rows where\nCypher semantics require one null-filled row per outer match.\n\nFix: in transform_cypher_match, the has_list_comp_or_subquery rewrite\nnow only applies to non-optional MATCH.  In the OPTIONAL MATCH path,\ntransform_cypher_optional_match_clause detaches the WHERE from the\ncypher_match node before recursively transforming the right-hand side\n(so the inner transform does not double-apply or misresolve the\npredicate in a fresh namespace), and re-attaches the transformed\npredicate as the LEFT JOIN\u0027s ON condition after both sides are in the\nnamespace.  A LEFT JOIN with a failing ON condition correctly\npreserves left rows with null right columns, which matches Cypher\nOPTIONAL MATCH ... WHERE semantics.\n\nRegression tests cover:\n  - EXISTS { (friend)-[...]-\u003e(...) } referencing the optional variable\n  - EXISTS { (p)-[...]-\u003e(...) } referencing the outer variable\n  - non-correlated EXISTS (previously-working guard)\n  - plain scalar predicate on the optional variable (guard)\n  - constant-false WHERE (guard)\n\nFixes issue #2378.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "ce5004593ea5acb2cec3a2dc94a06241d7f23c17",
      "tree": "6dfb045045ddb1a44bfd16d917777aee7bd64cd8",
      "parents": [
        "f1a9b1d9b8b171656efd82674dec2e6ce06b8f9a"
      ],
      "author": {
        "name": "Ueslei Lima",
        "email": "uesleisantoslima@gmail.com",
        "time": "Sun Apr 19 09:10:39 2026 -0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Apr 19 17:10:39 2026 +0500"
      },
      "message": "Python driver: Add `skip_load` parameter to skip `LOAD \u0027age\u0027` statement (#2366)\n\n* Add skip_load parameter to connect and setUpAge functions to control plugin loading.\n\n* Add tests and README docs for skip_load parameter\n\n- Add 4 unit tests for setUpAge() skip_load behavior:\n  skip_load\u003dTrue skips LOAD, skip_load\u003dFalse executes LOAD,\n  load_from_plugins integration, and search_path always set.\n- Document skip_load in README under new \"Managed PostgreSQL Usage\"\n  section for Azure/AWS RDS/etc. environments.\n- Fix syntax in existing load_from_plugins code example.\n\nMade-with: Cursor\n\n* Address review feedback: ValueError for contradictory flags, e2e test\n\n- Raise ValueError when skip_load\u003dTrue and load_from_plugins\u003dTrue are\n  both set (contradictory combination)\n- Add end-to-end test verifying skip_load is forwarded through the full\n  age.connect() → Age.connect() → setUpAge() call chain\n- Replace fragile string assertions with assert_called_with/assert_any_call\n- README: mention configure_connection() as the pool-based alternative\n  for managed PostgreSQL environments\n\nMade-with: Cursor\n\n* Fix README: use setUpAge(skip_load\u003dTrue) for pool example\n\nconfigure_connection() is not part of this PR; use the available\nsetUpAge() API with skip_load\u003dTrue for the connection pool example.\n\nMade-with: Cursor\n\n* fix(python-driver): use quoted $user in search_path and run TestSetUpAge in CI\n\nPostgreSQL treats single-quoted \u0027$user\u0027 as a literal schema name; use\n\"$user\" so the session user schema is included. Include TestSetUpAge in\nthe test_age_py __main__ suite so skip_load tests run in GitHub Actions.\n\nMade-with: Cursor"
    },
    {
      "commit": "f1a9b1d9b8b171656efd82674dec2e6ce06b8f9a",
      "tree": "a58e3367f867f9e74270e2487c2e20a9a2f35078",
      "parents": [
        "1847644cf7d2f52486f3a6dba90317ad72f178c7"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Thu Apr 16 11:15:34 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 16 23:15:34 2026 +0500"
      },
      "message": "Fix upgrade test: build default install SQL from HEAD, not initial commit (#2397)\n\nThe upgrade test previously built age--\u003cCURR\u003e.sql from the initial\nversion-bump commit, meaning CREATE EXTENSION installed \u0027day-one\u0027\nSQL. This caused all 31 non-upgrade regression tests to run WITHOUT\nSQL functions added after the version bump (e.g., age_invalidate_graph_cache,\nage_prepare_pg_upgrade, age_vertex_stats, etc.). These functions were\nnever registered in pg_proc, so features depending on them (like VLE\ncache invalidation triggers) were silently disabled during testing.\n\nFix by inverting the upgrade test direction:\n\nBefore (installs incomplete SQL, upgrades to complete):\n  age--1.7.0.sql                     built from version-bump commit (incomplete)\n  age--1.7.0_upgrade_test.sql        built from HEAD (complete)\n  age--1.7.0--1.7.0_upgrade_test.sql stamped from template\n  Test: CREATE EXTENSION age -\u003e data -\u003e ALTER EXTENSION UPDATE TO \u00271.7.0_upgrade_test\u0027\n\nAfter (installs complete SQL, upgrades from synthetic initial):\n  age--1.7.0.sql                     built from HEAD (complete)\n  age--1.7.0_initial.sql             built from version-bump commit (synthetic)\n  age--1.7.0_initial--1.7.0.sql      stamped from template\n  Test: CREATE EXTENSION age VERSION \u00271.7.0_initial\u0027 -\u003e data -\u003e ALTER EXTENSION UPDATE TO \u00271.7.0\u0027\n\nThis ensures:\n  - All 31 non-upgrade tests run with every SQL function registered\n  - The upgrade template is still validated (initial -\u003e current)\n  - The test ends at the default version (clean state for drop test)\n  - Tarball builds (no git) work identically (cat sql/sql_files)\n  - All synthetic files are cleaned up after the test\n\nMakefile changes:\n  - age_sql rule: cat current HEAD sql/sql_files (was: git show from commit)\n  - New age_init_sql rule: git show from version-bump commit\n  - age_upgrade_test_sql: stamps template as INIT-\u003eCURR (was CURR-\u003eNEXT)\n  - EXTRA_CLEAN, _install_upgrade_test_files: updated filenames\n  - Removed AGE_NEXT_VER, age_next_sql; added AGE_INIT_VER, age_init_sql\n\nTest SQL changes:\n  - Step 3: CREATE EXTENSION age VERSION \u0027\u003cinit\u003e\u0027 (was: CREATE EXTENSION age)\n  - Step 6: ALTER EXTENSION age UPDATE TO default_version (was: LIKE \u0027%_upgrade_test\u0027)\n  - Step 7: Check installed \u003d default (was: installed \u003c\u003e default)\n  - Comments updated throughout to reflect inverted direction\n\nAll 32 regression tests pass.\n\nmodified:   Makefile\nmodified:   regress/expected/age_upgrade.out\nmodified:   regress/sql/age_upgrade.sql"
    },
    {
      "commit": "1847644cf7d2f52486f3a6dba90317ad72f178c7",
      "tree": "ba09c71b2ebdd330c4d794ced03d5f45f2eafc78",
      "parents": [
        "945a259d86d42204f186f79dec758585afde0eaa"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Wed Apr 15 01:09:50 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 15 13:09:50 2026 +0500"
      },
      "message": "Improve extension upgrade regression test (addendum to #2364) (#2377)\n\nNote: This PR was created with AI tools and a human.\n\nThis is an addendum to PR #2364 with three improvements.\n\nMakefile:\n- Replace awk-based synthetic version (minor+1) with an _upgrade_test\n  suffix (e.g., 1.7.0 -\u003e 1.7.0_upgrade_test). The awk approach produced\n  numeric versions like 1.8.0 that could collide with real future upgrade\n  scripts, and the ::int[] cast in the SQL version lookup fails on\n  non-numeric version strings. The _upgrade_test suffix avoids both\n  issues and is unambiguously synthetic.\n- Extend the generated cleanup script to also remove repo-root copies\n  of the synthetic files and to self-delete, preventing stale artifacts\n  from accumulating across repeated test runs.\n\nRegression test (regress/sql/age_upgrade.sql):\n- Simplify version lookup to directly select the _upgrade_test version\n  via LIKE \u0027%_upgrade_test\u0027 instead of picking the highest non-default\n  version with string_to_array(version, \u0027.\u0027)::int[] DESC. The old\n  approach would fail with a cast error on the _upgrade_test suffix and\n  was unnecessarily indirect — the test knows exactly what synthetic\n  version the Makefile installed.\n\nmodified:   Makefile\nmodified:   regress/expected/age_upgrade.out\nmodified:   regress/sql/age_upgrade.sql\n\nCo-authored-by: Claude \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "945a259d86d42204f186f79dec758585afde0eaa",
      "tree": "3f67a6f4fb810672142c4339529140c4b2511cb5",
      "parents": [
        "b3a00eea32c3faa93c43e0d91c0a75671e48ce65"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Tue Apr 14 12:01:26 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 14 21:01:26 2026 +0200"
      },
      "message": "Add missing include for PR: Add index scan (#2351) (#2379)\n\nThe CI build did not fail the PR (we need to verify why and correct\nit) due to the missing include -\n\nsrc/backend/utils/adt/age_global_graph.c:273:25: error: implicit declaration of\nfunction ‘namestrcpy’; did you mean ‘strcpy’? [-Wimplicit-function-declaration]\n  273 |                         namestrcpy(lval, NameStr(*label_name_ptr));\n      |                         ^~~~~~~~~~\n      |                         strcpy\n\nThe build still works as the linker was able to resolve it. This PR\nwill add it in to correct the error going forward."
    },
    {
      "commit": "b3a00eea32c3faa93c43e0d91c0a75671e48ce65",
      "tree": "f751d94a00c7d91fdf7a323aac510b6f40abe564",
      "parents": [
        "d6f1b7f67087bd86f2e0fe6e1b6c897b7e569926"
      ],
      "author": {
        "name": "a_bondar",
        "email": "s6311704@gmail.com",
        "time": "Wed Apr 15 01:23:53 2026 +0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 14 11:23:53 2026 -0700"
      },
      "message": "Add index scan (#2351)\n\n* Add index scan\n\nThis commit fixes performance degradation during insertion scenarios by replacing SeqScan with IndexScan.\n\nMotivation / Problem:\nAs a result of load testing, a significant performance degradation was found in insertion scenarios. The scenarios\nused were taken from an open-source benchmark and rewritten in pure SQL. Examples of the queries can be \nfound here:\n\nhttps://github.com/ldbc/ldbc_snb_interactive_v1_impls/blob/main/cypher/queries/interactive-update-1.cypher\nhttps://github.com/ldbc/ldbc_snb_interactive_v1_impls/blob/main/cypher/queries/interactive-update-6.cypher\nhttps://github.com/ldbc/ldbc_snb_interactive_v1_impls/blob/main/cypher/queries/interactive-update-7.cypher\n\nPerf analysis showed that the main bottleneck is the entity_exists function. The root cause lies in the use of a\nSequential Scan (SeqScan) to check for the existence of an entity prior to insertion. The time complexity of a\nSeqScan is O(N), meaning the search time grows linearly as the number of rows in the table increases. The\nlarger the graph became, the longer each individual insertion took. This led to a drop in TPS regardless of\nthe concurrency level (the issue was consistently reproduced with both 1 and 30 threads).\n\nCo-authored-by: Daria Barsukova \u003cd.barsukova@g.nsu.ru\u003e\nCo-authored-by: Alexandra Bondar \u003cs6311704@gmail.com\u003e\n\n* Address code review commets\n\n* Address code review commets\n\n* Add brackets\n\n---------\n\nCo-authored-by: Alexandra Bondar \u003ca.bondar@postgrespro.ru\u003e\nCo-authored-by: Daria Barsukova \u003cd.barsukova@g.nsu.ru\u003e"
    },
    {
      "commit": "d6f1b7f67087bd86f2e0fe6e1b6c897b7e569926",
      "tree": "7c8e723de4c82fd28634dc50cf06591c0c88189a",
      "parents": [
        "a29e2810b1ac5a9e4c3e553879b683a6a1902c8b"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Mon Apr 06 11:34:47 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 06 08:34:47 2026 -0700"
      },
      "message": "Fix MATCH on brand-new label after CREATE returning 0 rows (#2341)\n\n* Fix MATCH on brand-new label after CREATE returning 0 rows (issue #2193)\n\nWhen CREATE introduces a new label and a subsequent MATCH references it\n(e.g., CREATE (:Person) WITH ... MATCH (p:Person)), the query returns\n0 rows on first execution but works on the second.\n\nRoot cause: match_check_valid_label() in transform_cypher_match() runs\nbefore transform_prev_cypher_clause() processes the predecessor chain.\nSince CREATE has not yet executed its transform (which creates the label\ntable as a side effect), the label is not in the cache and the check\ngenerates a One-Time Filter: false plan that returns no rows.\n\nFix: Skip the early label validity check when the predecessor clause\nchain contains a data-modifying operation (CREATE, SET, DELETE, MERGE).\nAfter transform_prev_cypher_clause() completes and any new labels exist\nin the cache, run a deferred label check. If the labels are still\ninvalid at that point, generate an empty result via makeBoolConst(false).\n\nThis preserves the existing behavior for MATCH without DML predecessors\n(e.g., MATCH-MATCH chains still get the early check and proper error\nmessages for invalid labels).\n\nDepends on: PR #2340 (clause_chain_has_dml helper)\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n* Address review feedback: fix variable registration for deferred label check\n\nWhen the deferred label validity check (DML predecessor + non-existent\nlabel) found an invalid label, the code skipped transform_match_pattern()\nentirely, which meant MATCH-introduced variables were never registered\nin the namespace. This would cause errors if a later clause referenced\nthose variables (e.g., RETURN p).\n\nFix: mirror the early-check strategy by injecting a paradoxical WHERE\n(true \u003d false) and always calling transform_match_pattern(). Variables\nget registered normally; zero rows are returned via the impossible qual.\n\nAlso add ORDER BY to multi-row regression tests for deterministic output,\nand add a test case for DML predecessor + non-existent label + returning\na MATCH-introduced variable.\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n* Address Copilot review: DRY false-where helper, cache has_dml, ORDER BY in tests\n\n- Factor duplicated WHERE true\u003dfalse construction into\n  make_false_where_clause() helper (used in both early and deferred\n  label validation paths)\n- Compute clause_chain_has_dml() once and reuse, avoiding repeated\n  clause chain traversal\n- Add ORDER BY to the single-CREATE City regression test for\n  deterministic result ordering\n\n* Address Copilot review: volatile false predicate, DML side-effect test\n\n1. Prevent plan elimination of DML predecessor: replace constant\n   (true \u003d false) with volatile (random() IS NULL) in the deferred\n   label check path. PG\u0027s planner can constant-fold the former into\n   a One-Time Filter: false, skipping the DML scan entirely.\n\n2. Unify make_false_where_clause(bool volatile_needed): merge the\n   constant and volatile variants into a single parameterized\n   function. Call sites are now self-documenting:\n   - make_false_where_clause(false) for non-DML path\n   - make_false_where_clause(true) for DML predecessor path\n\n3. Document why add_volatile_wrapper() cannot be reused here (it\n   operates post-transform at the Expr level and returns agtype,\n   while the WHERE clause is built at the parse-tree level).\n\n4. Add regression test verifying CREATE side effects persist when\n   MATCH references a non-existent label after a DML predecessor.\n\nAll regression tests pass (cypher_match: ok).\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n* Replace non-ASCII em dashes with -- in C comments\n\nASCII-only codebase convention; avoids encoding/tooling issues.\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "a29e2810b1ac5a9e4c3e553879b683a6a1902c8b",
      "tree": "cc54935eaba84efe8c9f3b28ef5291828b7e0531",
      "parents": [
        "90c33eb6b7c36f8280893bdc2b64666debb836de"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Apr 06 03:28:46 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 06 15:28:46 2026 +0500"
      },
      "message": "Fix nondeterministic age_global_graph regression test (#2365)\n\nThe age_global_graph test had two issues that could cause intermittent\nfailures:\n\n1. Nondeterministic warning output: The graph_stats() call on a graph\n   with deliberately deleted vertices produces WARNING messages for\n   dangling edges. These warnings are emitted by iterating edge label\n   tables (knows, stalks), and the iteration order is not guaranteed.\n   Since PostgreSQL WARNING messages cannot be caught or counted from\n   SQL (only ERROR and above are catchable via PL/pgSQL exception\n   handling), we suppress them with SET client_min_messages \u003d error.\n   The suppressed warnings are documented verbatim in comments. The\n   graph_stats() result row still validates correct dangling-edge\n   handling.\n\n2. Nondeterministic row ordering: Multiple MATCH...RETURN queries\n   returned multi-row results without ORDER BY, relying on scan order.\n   Added ORDER BY id(u), id(v), id(e), id(n), or id(a) as appropriate\n   to all MATCH...RETURN queries for future-proofing, even those\n   currently returning a single row.\n\nFiles changed:\n  regress/sql/age_global_graph.sql\n  regress/expected/age_global_graph.out\n\nCo-authored-by: GitHub Copilot \u003cnoreply@github.com\u003e"
    },
    {
      "commit": "90c33eb6b7c36f8280893bdc2b64666debb836de",
      "tree": "bae2b661e266b63d326b87e37d425589eb460310",
      "parents": [
        "23146a44c36c0ece003b7b91f7942385ae8ac5e7"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Apr 06 03:27:03 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 06 15:27:03 2026 +0500"
      },
      "message": "Add extension upgrade template regression test (#2364)\n\nNote: This PR was created with AI tools and a human.\n\nAdd a version-agnostic regression test (age_upgrade) that validates the\nupgrade template (age--\u003cVER\u003e--y.y.y.sql) works correctly by simulating a\nfull extension version upgrade within \"make installcheck\".\n\nAdd full upgrade scripts to the install path (DATA) in the Makefile,\nexcluding template upgrade files. This enables the install to copy all\nversion upgrade files into the PG AGE install. This is needed for\nALTER EXTENSION\n\nAdjusted installcheck.yaml to allow git commit history for this test.\n\nMakefile infrastructure:\n- Build the install SQL (age--\u003cCURR\u003e.sql) from the initial version-bump\n  commit in git history, so CREATE EXTENSION installs \"day-one\" SQL while\n  the .so comes from current HEAD — implicitly testing backward compat.\n- Build a synthetic \"next\" version (age--\u003cNEXT\u003e.sql) from HEAD and stamp\n  the upgrade template to produce age--\u003cCURR\u003e--\u003cNEXT\u003e.sql.\n- Add an installcheck prerequisite that temporarily installs both synthetic\n  files into the PG extension directory; a generated cleanup script removes\n  them at the end of the test via \\! shell escape. EXTRA_CLEAN catches\n  stragglers on \"make clean\".\n- Skip the test automatically when: (a) no git history (tarball builds),\n  (b) no upgrade template exists, or (c) a real upgrade script from the\n  current version is already committed (detected via git ls-files).o\n\nRegression test (regress/sql/age_upgrade.sql):\n- Creates 3 graphs (company, network, routes) with 8 vertex labels,\n  8 edge labels, 23 vertices, 28 edges, and 4 GIN indexes.\n- Records integrity checksums (agtype sums), vertex/edge counts, and\n  label counts before the upgrade; repeats all checks after ALTER\n  EXTENSION UPDATE to the synthetic next version.\n- Verifies structural queries: VLE management chains, circular follow\n  chains, flight distances with edge properties.\n- Verifies all 4 GIN indexes survive the upgrade via pg_indexes.\n- Uses ORDER BY on all multi-row queries for deterministic output.\n- Returns agtype natively (no ::numeric casts) for portability.\n- Avoids version-dependent output (checks boolean IS NOT NULL instead\n  of printing the version string).\n- Uses JOIN-based label counts to avoid NULL comparison bugs with the\n  internal _ag_catalog graph.\n- Cleans up all 3 graphs and restores the default AGE version.\n\nmodified:   Makefile\nnew file:   regress/expected/age_upgrade.out\nnew file:   regress/sql/age_upgrade.sql\nmodified:   .github/workflows/installcheck.yaml"
    },
    {
      "commit": "23146a44c36c0ece003b7b91f7942385ae8ac5e7",
      "tree": "534bdd7ace54c6ac8cf6de56281647cc8a255c84",
      "parents": [
        "d0741d8dc90f0c2f636f049b48b9d41eb54516cf"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Tue Mar 03 12:52:48 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 03 09:52:48 2026 -0800"
      },
      "message": "Fix crash in PREPARE with property parameter when enable_containment is off (#2339)\n\n* Fix crash in PREPARE with property parameter when enable_containment is off\n\nWhen age.enable_containment is set to off, executing a PREPARE statement\nwith a property parameter (e.g., MATCH (n $props) RETURN n) causes a\nsegfault. The crash occurs in transform_map_to_ind_recursive because\nthe property_constraints node is a cypher_param, not a cypher_map, but\nis blindly cast to cypher_map and its keyvals field is dereferenced.\n\nThree fixes:\n- In create_property_constraints, when enable_containment is off and the\n  constraint is a cypher_param, fall back to the containment operator\n  (@\u003e) since map decomposition requires known keys at parse time.\n- In transform_match_entities, guard the keep_null assignment for both\n  vertex and edge property constraints with is_ag_node checks to avoid\n  writing to the wrong struct layout.\n\nFixes #1964\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n* Fix @\u003e vs @\u003e\u003e for \u003dproperties form with PREPARE and add tests\n\nWhen MATCH uses the \u003dproperties form (e.g., MATCH (n \u003d $props)), the\nenable_containment\u003don path correctly uses @\u003e\u003e (top-level containment).\nThe parameter fallback path unconditionally used @\u003e (deep containment),\nignoring the use_equals flag. Fix the fallback to mirror the\nenable_containment path by selecting @\u003e\u003e when use_equals is set.\n\nAdd regression tests for \u003dproperties form with PREPARE for both\nvertices and edges, with enable_containment on and off.\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "d0741d8dc90f0c2f636f049b48b9d41eb54516cf",
      "tree": "3937e51275d5ac0b7dd06340fde90d4be1985d32",
      "parents": [
        "a21120bf139b156661ef10374630e688c0cc46fb"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Mon Mar 02 13:09:19 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 02 10:09:19 2026 -0800"
      },
      "message": "Fix entity_exists() CID visibility for CREATE + WITH + MERGE (#2343)\n\n* Fix entity_exists() CID visibility for CREATE + WITH + MERGE (#1954)\n\nWhen a Cypher query chains CREATE ... WITH ... MERGE, vertices created\nby CREATE become invisible to entity_exists() after a threshold number\nof input rows. This causes MERGE to throw \"vertex assigned to variable\nwas deleted\".\n\nRoot cause: CREATE calls CommandCounterIncrement() which advances the\nglobal command ID, but does not update es_snapshot-\u003ecurcid. The\nDecrement/Increment CID macros used by the executors bring curcid back\nto the same value on each iteration. After enough rows, newly inserted\nvertices have a Cmin \u003e\u003d curcid and HeapTupleSatisfiesMVCC rejects them\n(requires Cmin \u003c curcid).\n\nFix: In entity_exists(), temporarily set es_snapshot-\u003ecurcid to the\ncurrent global command ID (via GetCurrentCommandId) for the duration\nof the scan, then restore it. This makes all entities inserted by\npreceding clauses in the same query visible to the existence check.\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n* Use Max() to prevent curcid regression in entity_exists()\n\nAddress review feedback: es_snapshot-\u003ecurcid can be ahead of the\nglobal CID due to Increment_Estate_CommandId macros. Unconditionally\nassigning GetCurrentCommandId(false) could decrease curcid, making\npreviously visible tuples invisible. Use Max(saved_curcid,\nGetCurrentCommandId(false)) to ensure we only ever increase visibility.\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "a21120bf139b156661ef10374630e688c0cc46fb",
      "tree": "e67db7efa4ca9728956810009b1859bac22c5024",
      "parents": [
        "217467a36a1c29df4b918faf6adb6e75aec28817"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Mon Mar 02 11:27:04 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 02 08:27:04 2026 -0800"
      },
      "message": "Support doubled-quote escaping in Cypher string literals (issue #2222) (#2342)\n\nSQL drivers (psycopg2, JDBC, etc.) escape single quotes by doubling\nthem (\u0027isn\u0027\u0027t\u0027) when substituting parameters into queries. When these\nsubstitutions land inside Cypher\u0027s $$ block, the Cypher scanner rejects\nthem because it only recognizes backslash escaping (\\\u0027). This makes it\ndifficult to pass strings containing apostrophes through SQL drivers.\n\nAdd \u0027\u0027 and \"\" as escape sequences in the Cypher scanner, following the\nsame pattern already used for backtick-quoted identifiers (``). Flex\npicks the longer two-character match over the one-character closing\nquote, so the change is backwards-compatible -- \u0027\u0027 was previously a\nsyntax error.\n\nCo-authored-by: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "217467a36a1c29df4b918faf6adb6e75aec28817",
      "tree": "c136a7bc5162ef8e4d7feb1eedc1fbcc6b0d038b",
      "parents": [
        "20ada845280c4370909d82b8652f6a082e5aa5df"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Fri Feb 27 17:10:04 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 27 14:10:04 2026 -0800"
      },
      "message": "Fix MATCH after CREATE returning 0 rows (issue #2308) (#2340)\n\nWhen a MATCH clause follows CREATE + WITH and re-uses bound variables\n(e.g. CREATE (a)-[e]-\u003e(b) WITH a,e,b MATCH p\u003d(a)-[e]-\u003e(b)), the MATCH\ngenerates filter quals (age_start_id(e) \u003d age_id(a), etc.) that\nreference only columns from the predecessor subquery. PostgreSQL\u0027s\noptimizer pushes these quals through the transparent subquery layers\ninto the CREATE\u0027s child plan, where they evaluate on NULL values before\nCREATE has executed — always yielding 0 rows.\n\nFix: mark the predecessor subquery RTE as security_barrier when the\nclause chain contains a data-modifying operation (CREATE, SET, DELETE,\nor MERGE). This prevents PostgreSQL from pushing filter quals into the\nsubquery, ensuring they evaluate after the DML produces output values.\n\nCo-authored-by: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "20ada845280c4370909d82b8652f6a082e5aa5df",
      "tree": "6945374c4ebd9925cc920f966b9bbb2c31856c5e",
      "parents": [
        "346f319459e18db89deae30a3af7ea945bba101d"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Fri Feb 27 15:05:31 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 27 12:05:31 2026 -0800"
      },
      "message": "Fix chained MERGE not seeing sibling MERGE\u0027s changes (#1446) (#2344)\n\n* Fix chained MERGE not seeing sibling MERGE\u0027s changes (#1446)\n\nWhen multiple MERGEs are chained (e.g. MATCH ... MERGE ... MERGE ...),\nthe non-terminal (first) MERGE returned rows one at a time to the parent\nplan node. The parent MERGE\u0027s lateral join would materialize its hash\ntable on the first row, before the child MERGE had finished all its\niterations. This caused the second MERGE to not see entities created by\nthe first MERGE, leading to duplicate nodes.\n\nFix by making non-terminal MERGE eager: it processes ALL input rows and\nbuffers the projected results before returning any to the parent. This\nensures all entity creations are committed before any parent plan node\nscans the tables.\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n* Fix non-terminal MERGE empty-buffer fallthrough and add test\n\nWhen a non-terminal MERGE receives no input rows from its predecessor\n(e.g., MATCH returns 0 rows), the eager buffer is filled but empty.\nThe condition at line 688 checked `css-\u003eeager_tuples !\u003d NIL`, which\nevaluated to false for an empty buffer, causing execution to fall\nthrough to the terminal MERGE code path. This could incorrectly\ncreate entities when none should be created.\n\nFix by checking `css-\u003eeager_buffer_filled` instead, which correctly\ndistinguishes \"buffer not yet filled\" from \"buffer filled but empty\".\n\nAdd regression test for chained MERGE with empty MATCH result.\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "346f319459e18db89deae30a3af7ea945bba101d",
      "tree": "3a770416ae7880ad270ca70fcfb3227cb8d884c8",
      "parents": [
        "5005c21e5c2aa5daaca909fee7c4f9ed8ccdf984"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Thu Feb 26 19:24:58 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Feb 26 16:24:58 2026 -0800"
      },
      "message": "Fix VLE queries failing on read-only replicas (#2160) (#2345)\n\nThe global graph cache used by VLE acquired ShareLock when scanning\nvertex and edge label tables to populate in-memory hashtables. On\nread-only replicas (standby servers in recovery), PostgreSQL only\nallows RowExclusiveLock or less, so VLE queries would fail with:\n\n  \"cannot acquire lock mode ShareLock on database objects while\n   recovery is in progress\"\n\nChange all three functions in age_global_graph.c to use AccessShareLock\ninstead, which is sufficient for read-only table scans and is consistent\nwith the existing ag_cache.c code that performs identical operations.\n\nCo-authored-by: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "5005c21e5c2aa5daaca909fee7c4f9ed8ccdf984",
      "tree": "dcbea7954f9fed7d88c44f41bd412bcb26330d0f",
      "parents": [
        "55476ad2875f37f5b65919b6eeab7d81fd65aa7c"
      ],
      "author": {
        "name": "Greg Felice",
        "email": "gregfelice@gmail.com",
        "time": "Thu Feb 26 18:30:56 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Feb 26 15:30:56 2026 -0800"
      },
      "message": "Fix VLE NULL handling for chained OPTIONAL MATCH (#2337)\n\n* Fix VLE NULL handling for chained OPTIONAL MATCH (#2092)\n\nVLE functions (age_match_vle_terminal_edge, age_match_two_vle_edges,\nage_match_vle_edge_to_id_qual) threw errors when receiving NULL\narguments from OPTIONAL MATCH (LEFT JOIN) contexts. Additionally,\nbuild_local_vle_context crashed with a segfault when dereferencing\na NULL next_vertex pointer in the cached VLE context path.\n\nThese functions are used as join quals. In a LEFT JOIN, NULL arguments\nmean the inner side produced no match. The correct response is FALSE\n(no match), which lets PostgreSQL emit NULL-extended rows — the\nexpected OPTIONAL MATCH behavior. Errors or crashes are incorrect.\n\nChanges:\n- build_local_vle_context: guard against NULL next_vertex in cached\n  path; return NULL when vertex list is exhausted\n- age_vle: handle NULL return from build_local_vle_context with\n  SRF_RETURN_DONE\n- age_match_vle_terminal_edge: return FALSE on NULL arguments instead\n  of ereport(ERROR)\n- age_match_two_vle_edges: return FALSE on NULL arguments\n- age_match_vle_edge_to_id_qual: return FALSE on NULL arguments\n\nAll 32 regression tests pass including new tests for this fix.\n\n* Address review feedback: fix error message and add ORDER BY to tests\n\n- Fix errmsg in age_match_vle_terminal_edge() to use the correct\n  function name (was age_match_terminal_edge)\n- Add ORDER BY p.name to regression test queries to avoid\n  nondeterministic row ordering in expected output\n\nAI-assisted: Claude (Anthropic) was used in developing this fix.\n\nCo-Authored-By: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "55476ad2875f37f5b65919b6eeab7d81fd65aa7c",
      "tree": "cc7a6430d0df83947374b029ecc969b55c8c95bb",
      "parents": [
        "3c4d9cc7456703f37d3f2d02e4cc79731b06542a"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Wed Feb 25 09:41:41 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Feb 25 22:41:41 2026 +0500"
      },
      "message": "fix incorrect variable assignment (#2336)\n\nFixed an incorrect variable assignment, that was causing a warning\nmessage during compilation, on some compilers.\n\nThe create_index_on_column function assigned InvalidOid, instead\nof NIL or NULL.\n\n-    index_col-\u003ecollation \u003d InvalidOid;\n+    index_col-\u003ecollation \u003d NIL;\n\nNo regression tests were impacted.\n\nmodified:   src/backend/commands/label_commands.c"
    },
    {
      "commit": "3c4d9cc7456703f37d3f2d02e4cc79731b06542a",
      "tree": "6f8f5540d908d910a1e6a16136bd8f516804c904",
      "parents": [
        "fa91350b0a524e596e4fc85c9086565c60c00548"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Mon Feb 16 22:09:01 2026 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Feb 16 09:09:01 2026 -0800"
      },
      "message": "Remove labeler github action (#2335)\n\n- dropped the labeler workflow because `pull_request_target` trigger\n  can be dangerous and the workflow wasn’t particularly useful."
    },
    {
      "commit": "fa91350b0a524e596e4fc85c9086565c60c00548",
      "tree": "2c4b38b3d7673207035d4ff3376e169b22bac314",
      "parents": [
        "0c9a527860cde710175fc9c171b6bbe9aaed45d6"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Feb 13 23:24:54 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Feb 14 12:24:54 2026 +0500"
      },
      "message": "Fix ISO C90 forbids mixed declarations and code warning (#2334)\n\nFixed the following ISO C90 warning -\n\nsrc/backend/utils/adt/agtype.c:7503:9: warning: ISO C90 forbids mixed declarations\nand code [-Wdeclaration-after-statement]\n 7503 |         enum agtype_value_type elem_type \u003d elem ? elem-\u003etype : AGTV_NULL;\n      |         ^~~~\n\nNo regression tests impacted.\n\nmodified:   src/backend/utils/adt/agtype.c"
    },
    {
      "commit": "0c9a527860cde710175fc9c171b6bbe9aaed45d6",
      "tree": "3330d15d98f2634e44690b45488be243b36e852e",
      "parents": [
        "77a16ece0bbd8f137ad40b4e203c9622c249352d"
      ],
      "author": {
        "name": "Maxim Korotkov",
        "email": "korotkov.maxim.s@gmail.com",
        "time": "Fri Feb 13 21:57:51 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 13 10:57:51 2026 -0800"
      },
      "message": "Fix null pointer handling in array iteration (#2313)\n\nPreviously, when iterating through an agtype container, the code would\naccess `elem-\u003eval` even when `elem` was null.\nThis adds a null check to set the result type to AGTV_NULL when the\nelement is null, preventing a potential segmentation fault.\n\nFixes: 4274f10 (\"Added the toStringList() function (#1084)\")\nFound by PostgresPro.\n\nSigned-off-by: Maksim Korotkov \u003cm.korotkov@postgrespro.ru\u003e"
    },
    {
      "commit": "77a16ece0bbd8f137ad40b4e203c9622c249352d",
      "tree": "91d8c3563b726194daf25dd2570cc709502b8f77",
      "parents": [
        "5f5b744a08641225652de83332d73bc7acfc889d"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Feb 13 09:31:35 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 13 22:31:35 2026 +0500"
      },
      "message": "Fix security vulnerabilities in Node.js driver (#2329)\n\nFix security vulnerabilities in Node.js driver and harden input\nvalidation and query construction.\n\nNote: This PR was created with AI tools and a human.\n\n- Add input validation for graph names, label names, and column names\n  to prevent SQL injection via string interpolation. Graph name rules\n  are based on Apache AGE\u0027s naming conventions and Neo4j/openCypher\n  compatibility (hyphens and dots permitted, min 3 chars, max 63 chars).\n  Label names follow AGE\u0027s stricter rules (no hyphens or dots).\n- Add design documentation noting intentional ASCII-only restriction\n  in driver-side regex validation as a security hardening measure\n  (homoglyph/encoding attack surface reduction). AGE\u0027s server-side\n  validation (name_validation.h) uses full Unicode ID_Start/ID_Continue\n  and remains the authoritative check for Unicode names.\n- Add safe query helpers: queryCypher(), createGraph(), dropGraph()\n  with graph name validation and dollar-quoting for Cypher strings\n- Add runtime typeof check on dropGraph cascade parameter to prevent\n  injection from plain JavaScript consumers (TypeScript types are\n  erased at runtime)\n- Use BigInt for integer values exceeding Number.MAX_SAFE_INTEGER to\n  prevent silent precision loss with 64-bit AGE graph IDs\n- Make CREATE EXTENSION opt-in via SetAGETypesOptions.createExtension\n  instead of running DDL automatically without user consent\n- Wrap LOAD/search_path setup in try/catch with actionable error\n  message mentioning CREATE EXTENSION and { createExtension: true }\n- Improve agtype-not-found error message with installation guidance\n- Tighten pg dependency from \u003e\u003d6.0.0 to \u003e\u003d8.0.0\n- Add comprehensive test suites covering validation, SQL injection\n  prevention, cascade type safety, hyphenated graph name integration,\n  BigInt parsing, and setAGETypes error handling\n- Add design note in tests documenting why createExtension: false is\n  the correct default (CI image has AGE pre-installed, auto-creating\n  extensions requires SUPERUSER and conflates concerns)\n\nmodified:   drivers/nodejs/package.json\nmodified:   drivers/nodejs/src/antlr4/CustomAgTypeListener.ts\nmodified:   drivers/nodejs/src/index.ts\nmodified:   drivers/nodejs/test/Agtype.test.ts\nmodified:   drivers/nodejs/test/index.test.ts"
    },
    {
      "commit": "5f5b744a08641225652de83332d73bc7acfc889d",
      "tree": "3db8c8e0ef0889e79d8f01ac22f02e251d150d61",
      "parents": [
        "887564d9ce0f153ad7dc9f30223aa303c3fb60d0"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Feb 13 09:30:05 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 13 22:30:05 2026 +0500"
      },
      "message": "Update python-driver security and formatting (#2330)\n\nNote: This PR was created with AI tools and a human.\n\n- Add parameterized query construction using psycopg.sql to prevent\n  SQL injection in all Cypher execution paths (age.py, networkx/lib.py)\n- Replace all %-format and f-string SQL in networkx/lib.py with\n  sql.Identifier() for schema/table names and sql.Literal() for values\n- Add validate_graph_name() with AGE-aligned VALID_GRAPH_NAME regex:\n  start with letter/underscore, allow dots and hyphens in middle positions,\n  end with letter/digit/underscore, min 3 chars, max 63 chars\n- Add validate_identifier() with strict VALID_IDENTIFIER regex for labels,\n  column names, and SQL types (no dots or hyphens)\n- Add validation calls to all networkx/lib.py entry points:\n  graph names validated on entry, labels validated before SQL construction\n- Add _validate_column() to sanitize column specifications in buildCypher()\n- Fix exception constructors (AgeNotSet, GraphNotFound, GraphAlreadyExists)\n  to always call super().__init__() with a meaningful default message so\n  that str(exception) never returns an empty string\n- Add InvalidGraphName and InvalidIdentifier exception classes with\n  structured name/reason/context fields\n- Fix builder.py: change erroneous \u0027return Exception(...)\u0027 to\n  \u0027raise ValueError(...)\u0027 for unknown float expressions\n- Fix copy-paste docstring in create_elabel() (\u0027create_vlabels\u0027 -\u003e \u0027create_elabels\u0027)\n- Remove unused \u0027from psycopg.adapt import Loader\u0027 import in age.py\n- Add design documentation in source explaining:\n  - VALID_GRAPH_NAME regex uses \u0027*\u0027 (not \u0027+\u0027) intentionally so that the\n    min-length check fires first with a clear error message\n  - buildCypher uses string concatenation (not sql.Identifier) because\n    column specs are pre-validated \u0027name type\u0027 pairs that don\u0027t map to\n    sql.Identifier(); graphName and cypherStmt are NOT embedded\n- Update test_networkx.py GraphNotFound assertion to use assertIn()\n  instead of assertEqual() to match the improved exception messages\n- Strip Windows carriage returns (^M) from 7 source files\n- Fix requirements.txt: convert from UTF-16LE+BOM+CRLF to clean UTF-8+LF,\n  move --no-binary flag from requirements.txt to CI workflow pip command\n- Upgrade actions/setup-python from v4 (deprecated) to v5 in CI workflow\n- Add 46 security unit tests in test_security.py covering:\n  - Graph name validation (AGE naming rules, injection, edge cases)\n  - SQL identifier validation (labels, columns, types)\n  - Column spec sanitization\n  - buildCypher injection prevention\n  - Exception constructor correctness (str() never empty)\n- Add test_security.py to CI pipeline (python-driver.yaml)\n- pip-audit: 0 known vulnerabilities in all dependencies\n\nmodified:   .github/workflows/python-driver.yaml\nmodified:   drivers/python/age/VERSION.py\nmodified:   drivers/python/age/__init__.py\nmodified:   drivers/python/age/age.py\nmodified:   drivers/python/age/builder.py\nmodified:   drivers/python/age/exceptions.py\nmodified:   drivers/python/age/models.py\nmodified:   drivers/python/age/networkx/lib.py\nmodified:   drivers/python/requirements.txt\nmodified:   drivers/python/setup.py\nmodified:   drivers/python/test_agtypes.py\nmodified:   drivers/python/test_networkx.py\nnew file:   drivers/python/test_security.py"
    },
    {
      "commit": "887564d9ce0f153ad7dc9f30223aa303c3fb60d0",
      "tree": "46c8503f088337ba2cffaa9cb190093194aaff6b",
      "parents": [
        "5fe2121a06f692efc9a8c3d77b57c67b874e1b69"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Feb 13 01:54:06 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Feb 13 14:54:06 2026 +0500"
      },
      "message": "Fix JDBC driver CI test failures (#2333)\n\nFix JDBC driver CI test failures: encoding, Testcontainers, and\nDocker compatibility.\n\nNote: This PR was created with the help of AI tools and a human.\n\n- Set JavaCompile encoding to UTF-8 to fix unicode test failures in\n  CI environments that default to US-ASCII.\n\n- Add Testcontainers wait strategy (Wait.forLogMessage) to wait for\n  PostgreSQL to be fully ready before connecting.\n\n- Use agensGraphContainer.getHost() instead of hardcoded localhost\n  for Docker-in-Docker compatibility.\n\n- Add sslmode\u003ddisable to JDBC URL since PostgreSQL driver 42.6.0+\n  attempts SSL by default.\n\n- Remove silent exception swallowing around connection setup to fail\n  fast with meaningful errors instead of NullPointerException.\n\n- Upgrade Testcontainers from 1.18.0 to 1.21.4 to fix Docker 29.x\n  detection failure on ubuntu-24.04 runners (docker-java 3.3.x in\n  1.18.0 cannot negotiate with Docker Engine 29.1.5 API).\n\nmodified:   drivers/jdbc/lib/build.gradle.kts\nmodified:   drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/BaseDockerizedTest.java"
    },
    {
      "commit": "5fe2121a06f692efc9a8c3d77b57c67b874e1b69",
      "tree": "53fa0a0da6fd59635306507e95f5ccdb62adf7f4",
      "parents": [
        "858747c7e2a414651b4f8e76558fdce1bbdc0af8"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Tue Feb 10 10:32:29 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Feb 10 23:32:29 2026 +0500"
      },
      "message": "Add pg_upgrade support functions for PostgreSQL (#2326)\n\nAdd pg_upgrade support functions for PostgreSQL for major version\nupgrades\n\nNOTE: This PR was created with AI tools and a human.\n\nThe ag_graph.namespace column uses the regnamespace type, which pg_upgrade\ncannot handle in user tables. This commit adds four SQL functions to enable\nseamless PostgreSQL major version upgrades while preserving all graph data.\n\nNew functions in ag_catalog:\n- age_prepare_pg_upgrade(): Converts namespace from regnamespace to oid,\n  creates backup table with graph-to-namespace mappings (stores nspname\n  directly to avoid quoting issues)\n- age_finish_pg_upgrade(): Remaps stale OIDs after upgrade, restores\n  regnamespace type, invalidates AGE caches while preserving schema ownership\n- age_revert_pg_upgrade_changes(): Cancels preparation if upgrade is aborted\n- age_pg_upgrade_status(): Returns current upgrade readiness status\n\nUsage:\n  1. Before pg_upgrade: SELECT age_prepare_pg_upgrade();\n  2. Run pg_upgrade as normal\n  3. After pg_upgrade:  SELECT age_finish_pg_upgrade();\n\nKey implementation details:\n- Uses transaction-level advisory locks (pg_advisory_xact_lock) for safety\n- Preserves original schema ownership during cache invalidation\n- Validates all backup rows are mapped before proceeding\n- Handles zero-graph edge case gracefully\n- Handles insufficient privileges gracefully with informative notices\n- Backup table deleted only after all steps succeed\n\nFiles changed:\n- sql/age_pg_upgrade.sql: New file with function implementations\n- sql/sql_files: Added age_pg_upgrade entry\n- age--1.7.0--y.y.y.sql: Added functions for extension upgrades\n\nAll regression tests pass."
    },
    {
      "commit": "858747c7e2a414651b4f8e76558fdce1bbdc0af8",
      "tree": "052e8a143a5a4187910077ed16fa1b3c8ed32a00",
      "parents": [
        "6287af85f291e10d048c37ffd22b902950779af7"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Thu Jan 22 09:58:14 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jan 22 22:58:14 2026 +0500"
      },
      "message": "Advance master branch to Apache AGE version 1.7.0 (#2316)\n\nUpdated the following files to advance the Apache AGE version\nto 1.7.0\n\nmodified:   Makefile\nmodified:   README.md\nmodified:   RELEASE\nrenamed:    age--1.6.0--y.y.y.sql -\u003e age--1.6.0--1.7.0.sql\nnew file:   age--1.7.0--y.y.y.sql\nmodified:   age.control\nmodified:   docker/Dockerfile\ndeleted:    age--1.5.0--1.6.0.sql"
    },
    {
      "commit": "6287af85f291e10d048c37ffd22b902950779af7",
      "tree": "0ec19095337cada5e92ec849f6ab30a26fbd0585",
      "parents": [
        "b3219fd4228cc261df06070e37512c76b8c07678"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Thu Jan 22 21:50:23 2026 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jan 22 08:50:23 2026 -0800"
      },
      "message": "Fix upgrade script for 1.6.0 to 1.7.0 (#2320)\n\n- Added index creation for existing labels\n\nAssisted-by AI"
    },
    {
      "commit": "b3219fd4228cc261df06070e37512c76b8c07678",
      "tree": "762be11d32cc6df8c097b057ed08e50914624178",
      "parents": [
        "1702ae075de6897e9555fa527060f622304a1d93"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Wed Jan 21 10:01:57 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jan 21 23:01:57 2026 +0500"
      },
      "message": "Master to PostgreSQL version 18 (#2315)\n\n* Updated CI, Labeler, Docker, and branch security files for PG18 (#2246)\n\nUpdated the CI and Docker files for the PG18\nUpdated the labeler and branch security files for PG18.\n\nSome of these only apply to the master branch but are updated\nfor consistency.\n\nmodified:   .asf.yaml\nmodified:   .github/labeler.yml\nmodified:   .github/workflows/go-driver.yml\nmodified:   .github/workflows/installcheck.yaml\nmodified:   .github/workflows/jdbc-driver.yaml\nmodified:   .github/workflows/nodejs-driver.yaml\nmodified:   .github/workflows/python-driver.yaml\nmodified:   docker/Dockerfile\nmodified:   docker/Dockerfile.dev\nmodified:   drivers/docker-compose.yml\n\n* PG18 port for AGE (#2251)\n\n* [PG18 port][Set1] Fix header dependencies and use TupleDescAttr macro\n\n- Include executor/executor.h for PG18 header reorganization and use \n  TupleDescAttr() accessor macro instead of direct attrs[] access.\n\n* [PG18 port][Set2] Adapt to expandRTE signature change and pg_noreturn\n\n- Add VarReturningType parameter to expandRTE() calls using VAR_RETURNING_DEFAULT.\n- Replace pg_attribute_noreturn() with pg_noreturn prefix specifier.\n\n* [PG18 port][Set3] Fix double ExecOpenIndices call for PG18 compatibility\n\n- PG18 enforces stricter assertions in ExecOpenIndices, requiring\n  ri_IndexRelationDescs to be NULL when called.\n\n- In update_entity_tuple(), indices may already be opened by the\n  caller (create_entity_result_rel_info), causing assertion failures.\n\n- Add a check to only open indices if not already open, and track\n  ownership with a boolean flag to ensure we only close what we opened.\n\n- Found when regression tests failed with assertions, which this change\n  resolves.\n\n* [PG18 port][Set4] Update regression test expected output for ordering\n\nPG18\u0027s implementation changes result in different row ordering for\nqueries without explicit ORDER BY clauses. Update expected output\nfiles to reflect the new ordering while maintaining identical\nresult content.\n\n* [PG18 port][Set5] Address review comments - coding standard fix\n\nNote: Assisted by GitHub Copilot Agent mode.\n\n* Fix DockerHub build warning messages (#2252)\n\nPR fixes build warning messages on DockerHub and on my local build.\n\nNo regression tests needed.\n\nmodified:   src/include/nodes/ag_nodes.h\nmodified:   src/include/optimizer/cypher_createplan.h\nmodified:   src/include/optimizer/cypher_pathnode.h\nmodified:   tools/gen_keywordlist.pl\n\n---------\nCo-authored-by: Krishnakumar R (KK) \u003c65895020+kk-src@users.noreply.github.com\u003e"
    },
    {
      "commit": "1702ae075de6897e9555fa527060f622304a1d93",
      "tree": "071de4a0d3981a3f2bed9bd4239ccc73d8234e20",
      "parents": [
        "b29ca5e7d2f84cfe2619eea70c4ace2cba41aa0b"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Tue Jan 20 23:44:19 2026 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jan 20 10:44:19 2026 -0800"
      },
      "message": "Add RLS support and fix permission checks (#2309)\n\n- Previously, age only set ACL_SELECT and ACL_INSERT in RTEPermissionInfo,\n  bypassing pg\u0027s privilege checking for DELETE and UPDATE operations.\n- Additionally, RLS policies were not enforced because AGE uses CMD_SELECT\n  for all Cypher queries, causing the rewriter to skip RLS policy application.\n\nPermission fixes:\n- Add ACL_DELETE permission flag for DELETE clause operations\n- Add ACL_UPDATE permission flag for SET/REMOVE clause operations\n- Recursively search RTEs including subqueries for permission info\n\nRLS support:\n- Implemented at executor level because age transforms all cypher\n  queries to CMD_SELECT, so pg\u0027s rewriter never adds RLS\n  policies for INSERT/UPDATE/DELETE operations. There isnt an\n  appropriate rewriter hook to modify this behavior, so we do it\n  in executor instead.\n- Add setup_wcos() to apply WITH CHECK policies at execution time\n  for CREATE, SET, and MERGE operations\n- Add setup_security_quals() and check_security_quals() to apply\n  USING policies for UPDATE and DELETE operations\n- USING policies silently filter rows (matching pg behavior)\n- WITH CHECK policies raise errors on violation\n- DETACH DELETE raises error if edge RLS blocks deletion to prevent\n  dangling edges\n- Add permission checks and rls in startnode/endnode functions\n- Add regression tests\n\nAssisted-by AI"
    },
    {
      "commit": "b29ca5e7d2f84cfe2619eea70c4ace2cba41aa0b",
      "tree": "5587482b9187b9556706ae8f0a1de2b2fa415ab5",
      "parents": [
        "56a92d8c1be364e07bac51665a362ca91957194b"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Mon Jan 19 22:21:02 2026 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jan 19 09:21:02 2026 -0800"
      },
      "message": "Replace libcsv with pg COPY for csv loading (#2310)\n\n- Commit also adds permission checks\n- Resolves a critical memory spike issue on loading large file\n- Use pg\u0027s COPY infrastructure (BeginCopyFrom, NextCopyFromRawFields)\n  for 64KB buffered CSV parsing instead of libcsv\n- Add byte based flush threshold (64KB) matching COPY behavior for memory safety\n- Use heap_multi_insert with BulkInsertState for optimized batch inserts\n- Add per batch memory context to prevent memory growth during large loads\n- Remove libcsv dependency (libcsv.c, csv.h)\n- Improves loading performance by 15-20%\n- No previous regression tests were impacted\n- Added regression tests for permissions/rls\nAssisted-by AI"
    },
    {
      "commit": "56a92d8c1be364e07bac51665a362ca91957194b",
      "tree": "68ae7e3af1606efbf51dfa6d60f6a8b2f77466d2",
      "parents": [
        "8bdeec54e898451771f4dc021a13b00781d6d1a0"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Sun Jan 18 10:02:55 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jan 18 23:02:55 2026 +0500"
      },
      "message": "Fix Issue 1884: Ambiguous column reference (#2306)\n\nFix Issue 1884: Ambiguous column reference and invalid AGT header\nerrors.\n\nNote: This PR was created with AI tools and a human, or 2.\n\nThis commit addresses two related bugs that occur when using SET to store\ngraph elements (vertices, edges, paths) as property values:\n\nIssue 1884 - \"column reference is ambiguous\" error:\nWhen a Cypher query uses the same variable in both the SET expression RHS\nand the RETURN clause (e.g., SET n.prop \u003d n RETURN n), PostgreSQL would\nreport \"column reference is ambiguous\" because the variable appeared in\nmultiple subqueries without proper qualification.\n\nSolution: The fix for this issue was already in place through the target\nentry naming scheme that qualifies column references.\n\n\"Invalid AGT header value\" offset error:\nWhen deserializing nested VERTEX, EDGE, or PATH values stored in properties,\nthe system would fail with errors like \"Invalid AGT header value: 0x00000041\".\nThis occurred because ag_serialize_extended_type() did not include alignment\npadding (padlen) in the agtentry length calculation for these types, while\nfill_agtype_value() uses INTALIGN() when reading, causing offset mismatch.\n\nSolution: Modified ag_serialize_extended_type() in agtype_ext.c to include\npadlen in the agtentry length for VERTEX, EDGE, and PATH cases, matching\nthe existing pattern used for INTEGER, FLOAT, and NUMERIC types:\n\n    *agtentry \u003d AGTENTRY_IS_AGTYPE | (padlen + (AGTENTRY_OFFLENMASK \u0026 ...));\n\nThis ensures the serialized length accounts for alignment padding, allowing\ncorrect deserialization of nested graph elements.\n\nAppropriate regression tests were added to verify the fixes.\n\nCo-authored by: Zainab Saad \u003c105385638+Zainab-Saad@users.noreply.github.com\u003e\n\nmodified:   regress/expected/cypher_set.out\nmodified:   regress/sql/cypher_set.sql\nmodified:   src/backend/parser/cypher_clause.c\nmodified:   src/backend/utils/adt/agtype_ext.c"
    },
    {
      "commit": "8bdeec54e898451771f4dc021a13b00781d6d1a0",
      "tree": "9b832ab09abb86f195c566a4aa14e885b4c87951",
      "parents": [
        "b9d0982892306abff0013dd8f336e153684b02e9"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Sat Jan 17 03:18:10 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Jan 17 16:18:10 2026 +0500"
      },
      "message": "Upgrade Jest to v29 for node: protocol compatibility (#2307)\n\nNote: This PR was created with AI tools and a human.\n\nThe pg-connection-string module (dependency of pg) now uses the node:\nprotocol prefix for built-in modules (e.g., require(\u0027node:process\u0027)).\nJest 26 does not support this syntax, causing test failures.\n\nChanges:\n- Upgrade jest from ^26.6.3 to ^29.7.0\n- Upgrade ts-jest from ^26.5.1 to ^29.4.6\n- Upgrade @types/jest from ^26.0.20 to ^29.5.14\n- Update typescript to ^4.9.5\n\nThis also resolves 19 npm audit vulnerabilities (17 moderate, 2 high)\nthat existed in the older Jest 26 dependency tree.\n\nmodified:   drivers/nodejs/package.json"
    },
    {
      "commit": "b9d0982892306abff0013dd8f336e153684b02e9",
      "tree": "bc7bf98751e942cd136bae6bd9aecbdba76798b5",
      "parents": [
        "c979380e9865a624ad73eef01ec84717bb817f2b"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Jan 16 15:12:22 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Jan 17 04:12:22 2026 +0500"
      },
      "message": "Optimize vertex/edge field access with direct array indexing (#2302)\n\nNOTE: This PR was created using AI tools and a human.\n\nLeverage deterministic key ordering from uniqueify_agtype_object() to\naccess vertex/edge fields in O(1) instead of O(log n) binary search.\n\nFields are sorted by key length, giving fixed positions:\n- Vertex: id(0), label(1), properties(2)\n- Edge: id(0), label(1), end_id(2), start_id(3), properties(4)\n\nChanges:\n- Add field index constants and accessor macros to agtype.h\n- Update age_id(), age_start_id(), age_end_id(), age_label(),\n  age_properties() to use direct field access\n- Add fill_agtype_value_no_copy() for read-only scalar extraction\n  without memory allocation\n- Add compare_agtype_scalar_containers() fast path for scalar comparison\n- Update hash_agtype_value(), equals_agtype_scalar_value(), and\n  compare_agtype_scalar_values() to use direct field access macros\n- Add fast path in get_one_agtype_from_variadic_args() bypassing\n  extract_variadic_args() for single argument case\n- Add comprehensive regression test (30 tests)\n\nPerformance impact: Improves ORDER BY, hash joins, aggregations, and\nCypher functions (id, start_id, end_id, label, properties) on vertices\nand edges.\n\nAll previous regression tests were not impacted.\nAdditional regression test added to enhance coverage.\n\nmodified:   Makefile\nnew file:   regress/expected/direct_field_access.out\nnew file:   regress/sql/direct_field_access.sql\nmodified:   src/backend/utils/adt/agtype.c\nmodified:   src/backend/utils/adt/agtype_util.c\nmodified:   src/include/utils/agtype.h"
    },
    {
      "commit": "c979380e9865a624ad73eef01ec84717bb817f2b",
      "tree": "ce149789a3d319fbee5f39feef99288f8f316fc0",
      "parents": [
        "a1f472d6f9344dc4449ac7343bf7d81a31b66f02"
      ],
      "author": {
        "name": "Jean-Paul Abbuehl",
        "email": "jp.abbuehl@gmail.com",
        "time": "Mon Jan 12 21:10:08 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jan 12 12:10:08 2026 -0800"
      },
      "message": "feat: Add 32-bit platform support for graphid type (#2286)\n\n* feat: Add 32-bit platform support for graphid type\n\nThis enables AGE to work on 32-bit platforms including WebAssembly (WASM).\n\nProblem:\n- graphid is int64 (8 bytes) with PASSEDBYVALUE\n- On 32-bit systems, Datum is only 4 bytes\n- PostgreSQL rejects pass-by-value types larger than Datum\n\nSolution:\n- Makefile-only change (no C code modifications)\n- When SIZEOF_DATUM\u003d4 is passed to make, strip PASSEDBYVALUE from the generated SQL\n- If not specified, normal 64-bit behavior is preserved (PASSEDBYVALUE kept)\n\nThis change is backward compatible:\n- 64-bit systems continue using pass-by-value\n- 32-bit systems now work with pass-by-reference\n\nMotivation: PGlite (PostgreSQL compiled to WebAssembly) uses 32-bit\npointers and requires this patch to run AGE.\n\nTested on:\n- 64-bit Linux (regression tests pass)\n- 32-bit WebAssembly via PGlite (all operations work)\n\nCo-authored-by: abbuehlj \u003cjean-paul.abbuehl@roche.com\u003e"
    },
    {
      "commit": "a1f472d6f9344dc4449ac7343bf7d81a31b66f02",
      "tree": "7a8ce4eae52f05d88a0bf4e60ab1923373039ca7",
      "parents": [
        "7beb653303529b05391667de4204ffb4da318eeb"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Sat Jan 10 08:37:54 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Jan 10 21:37:54 2026 +0500"
      },
      "message": "Fix and improve index.sql addendum (#2301)\n\nNOTE: This PR was created with the help of AI tools and a human.\n\nAdded additional requested regression tests -\n\n *EXPLAIN for pattern with WHERE clause\n\n *EXPLAIN for pattern with filters on both country and city\n\nmodified:   regress/expected/index.out\nmodified:   regress/sql/index.sql"
    },
    {
      "commit": "7beb653303529b05391667de4204ffb4da318eeb",
      "tree": "b32ad7b1b69f4ff7dd40bcb3e085d265928963a0",
      "parents": [
        "2e8f7ab992fc5db6cedf88cbdffc15df3a3cf932"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Jan 09 12:55:36 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Jan 10 01:55:36 2026 +0500"
      },
      "message": "Fix and improve index.sql regression test coverage (#2300)\n\nNOTE: This PR was created with AI tools and a human.\n\n- Remove unused copy command (leftover from deleted agload_test_graph test)\n- Replace broken Section 4 that referenced non-existent graph with\n  comprehensive WHERE clause tests covering string, int, bool, and float\n  properties with AND/OR/NOT operators\n- Add EXPLAIN tests to verify index usage:\n  - Section 3: Validate GIN indices (load_city_gin_idx, load_country_gin_idx)\n    show Bitmap Index Scan for property matching\n  - Section 4: Validate all expression indices (city_country_code_idx,\n    city_id_idx, city_west_coast_idx, country_life_exp_idx) show Index Scan\n    for WHERE clause filtering\n\nAll indices now have EXPLAIN verification confirming they are used as expected.\n\nmodified:   regress/expected/index.out\nmodified:   regress/sql/index.sql"
    },
    {
      "commit": "2e8f7ab992fc5db6cedf88cbdffc15df3a3cf932",
      "tree": "c2e9d3d1245cab4b7fce973f3b99f957bdc08ec4",
      "parents": [
        "4eeceab256f4795e6e8fb1d802935bdf403e5e93"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Jan 09 12:27:47 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Jan 10 01:27:47 2026 +0500"
      },
      "message": "Fix Issue 2289: handle empty list in IN expression (#2294)\n\nNOTE: This PR was created with AI tools and a human.\n\nWhen evaluating \u0027x IN []\u0027 with an empty list, the transform_AEXPR_IN\nfunction would return NULL because no expressions were processed.\nThis caused a \u0027cache lookup failed for type 0\u0027 error downstream.\n\nThis fix adds an early check for the empty list case:\n- \u0027x IN []\u0027 returns false (nothing can be in an empty list)\n\nAdditional NOTE: Cypher does not have \u0027NOT IN\u0027 syntax. To check if\na value is NOT in a list, use \u0027NOT (x IN list)\u0027. The NOT operator\nwill invert the false from an empty list to true as expected.\n\nThe fix returns a boolean constant directly, avoiding the NULL result\nthat caused the type lookup failure.\n\nAdded regression tests.\n\nmodified:   regress/expected/expr.out\nmodified:   regress/sql/expr.sql\nmodified:   src/backend/parser/cypher_expr.c"
    },
    {
      "commit": "4eeceab256f4795e6e8fb1d802935bdf403e5e93",
      "tree": "c7836d4e9c790a2705e1ed2c2f13874998073bf4",
      "parents": [
        "dd6deb70e34e2ab75836dc6d037fde5a360d7075"
      ],
      "author": {
        "name": "M15terHyde",
        "email": "59905806+M15terHyde@users.noreply.github.com",
        "time": "Tue Jan 06 12:52:33 2026 -0600"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jan 06 10:52:33 2026 -0800"
      },
      "message": "Revise README for Python driver updates (#2298)\n\nUpdated README to from psycopg2 to psycopg3 (psycopg)"
    },
    {
      "commit": "dd6deb70e34e2ab75836dc6d037fde5a360d7075",
      "tree": "01d792af46e87ae59c472f070ad73db28a01b991",
      "parents": [
        "48fca83d90f29cf68fd9748db3b2d628f4978749"
      ],
      "author": {
        "name": "Arthur Nascimento",
        "email": "tureba@gmail.com",
        "time": "Tue Jan 06 13:36:49 2026 -0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jan 06 08:36:49 2026 -0800"
      },
      "message": "Makefile: fix race condition on cypher_gram_def.h (#2273)\n\nThe file cypher_gram.c generates cypher_gram_def.h, which is directly\nnecessary for cypher_parser.o and cypher_keywords.o and their respective\n.bc files.\n\nBut that direct dependency is not reflected in the Makefile, which only\nhad the indirect dependency of .o on .c. So on high parallel builds, the\n.h may not have been generated by bison yet.\n\nAdditionally, the .bc files should have the same dependencies as the .o\nfiles, but those are lacking.\n\nHere is an example output where the .bc file fails to build, as it was\nrunning concurrently with the bison instance that was about to finalize\ncypher_gram_def.h:\n\n  In file included from src/backend/parser/cypher_parser.c:24:\n  clang-17 -Wno-ignored-attributes -fno-strict-aliasing -fwrapv -fexcess-precision\u003dstandard -Wno-unused-command-line-argument -Wno-compound-token-split-by-macro -O2  -I.//src/include -I.//src/include/parser -I. -I./ -I/usr/pgsql-17/include/server -I/usr/pgsql-17/include/internal -D_GNU_SOURCE  -I/usr/include -I/usr/include/libxml2  -flto\u003dthin -emit-llvm -c -o src/backend/parser/cypher_parser.bc src/backend/parser/cypher_parser.c\n  .//src/include/parser/cypher_gram.h:65:10: fatal error: \u0027parser/cypher_gram_def.h\u0027 file not found\n     65 | #include \"parser/cypher_gram_def.h\"\n        |          ^~~~~~~~~~~~~~~~~~~~~~~~~~\n  1 error generated.\n  make: *** [/usr/pgsql-17/lib/pgxs/src/makefiles/../../src/Makefile.global:1085: src/backend/parser/cypher_parser.bc] Error 1\n  make: *** Waiting for unfinished jobs....\n  gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror\u003dvla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough\u003d3 -Wshadow\u003dcompatible-local -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision\u003dstandard -Wno-format-truncation -O2 -g -fmessage-length\u003d0 -D_FORTIFY_SOURCE\u003d2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -fPIC -fvisibility\u003dhidden -I.//src/include -I.//src/include/parser -I. -I./ -I/usr/pgsql-17/include/server -I/usr/pgsql-17/include/internal -D_GNU_SOURCE  -I/usr/include -I/usr/include/libxml2   -c -o src/backend/catalog/ag_label.o src/backend/catalog/ag_label.c\n  /usr/bin/bison -Wno-deprecated  --defines\u003dsrc/include/parser/cypher_gram_def.h -o src/backend/parser/cypher_gram.c src/backend/parser/cypher_gram.y\n\nPreviously, cypher_parser.o was missing the dependency, so it could\nstart before cypher_gram_def.h was available:\n\n Considering target file \u0027src/backend/parser/cypher_parser.o\u0027.\n  File \u0027src/backend/parser/cypher_parser.o\u0027 does not exist.\n  Considering target file \u0027src/backend/parser/cypher_parser.c\u0027.\n  File \u0027src/backend/parser/cypher_parser.c\u0027 was considered already.\n  Considering target file \u0027src/backend/parser/cypher_gram.c\u0027.\n  File \u0027src/backend/parser/cypher_gram.c\u0027 was considered already.\n Finished prerequisites of target file \u0027src/backend/parser/cypher_parser.o\u0027.\n Must remake target \u0027src/backend/parser/cypher_parser.o\u0027.\n\nAs well as cypher_parser.bc, missing the dependency on\ncypher_gram_def.h:\n\n Considering target file \u0027src/backend/parser/cypher_parser.bc\u0027.\n  File \u0027src/backend/parser/cypher_parser.bc\u0027 does not exist.\n  Considering target file \u0027src/backend/parser/cypher_parser.c\u0027.\n  File \u0027src/backend/parser/cypher_parser.c\u0027 was considered already.\n Finished prerequisites of target file \u0027src/backend/parser/cypher_parser.bc\u0027.\n Must remake target \u0027src/backend/parser/cypher_parser.bc\u0027.\n\nNow cypher_parser.o correctly depends on cypher_gram_def.h:\n\n Considering target file \u0027src/backend/parser/cypher_parser.o\u0027.\n  File \u0027src/backend/parser/cypher_parser.o\u0027 does not exist.\n  Considering target file \u0027src/backend/parser/cypher_parser.c\u0027.\n  File \u0027src/backend/parser/cypher_parser.c\u0027 was considered already.\n  Considering target file \u0027src/backend/parser/cypher_gram.c\u0027.\n  File \u0027src/backend/parser/cypher_gram.c\u0027 was considered already.\n  Considering target file \u0027src/include/parser/cypher_gram_def.h\u0027.\n  File \u0027src/include/parser/cypher_gram_def.h\u0027 was considered already.\n Finished prerequisites of target file \u0027src/backend/parser/cypher_parser.o\u0027.\n Must remake target \u0027src/backend/parser/cypher_parser.o\u0027.\n\nAnd cypher_parser.bc correctly depends on cypher_gram_def.h as well:\n\n Considering target file \u0027src/backend/parser/cypher_parser.bc\u0027.\n  File \u0027src/backend/parser/cypher_parser.bc\u0027 does not exist.\n  Considering target file \u0027src/backend/parser/cypher_parser.c\u0027.\n  File \u0027src/backend/parser/cypher_parser.c\u0027 was considered already.\n  Considering target file \u0027src/backend/parser/cypher_gram.c\u0027.\n  File \u0027src/backend/parser/cypher_gram.c\u0027 was considered already.\n  Considering target file \u0027src/include/parser/cypher_gram_def.h\u0027.\n  File \u0027src/include/parser/cypher_gram_def.h\u0027 was considered already.\n Finished prerequisites of target file \u0027src/backend/parser/cypher_parser.bc\u0027.\n Must remake target \u0027src/backend/parser/cypher_parser.bc\u0027."
    },
    {
      "commit": "48fca83d90f29cf68fd9748db3b2d628f4978749",
      "tree": "12f2e80989b270d97c25f235a0f0b423b998bd69",
      "parents": [
        "838926cc35ae64d3514c656959b749719e904b09"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Tue Dec 16 08:33:28 2025 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Dec 16 21:33:28 2025 +0500"
      },
      "message": "Restrict age_load commands (#2274)\n\nThis PR applies restrictions to the following age_load commands -\n\n    load_labels_from_file()\n    load_edges_from_file()\n\nThey are now tied to a specific root directory and are required to have a\nspecific file extension to eliminate any attempts to force them to access\nany other files.\n\nNothing else has changed with the actual command formats or parameters,\nonly that they work out of the /tmp/age directory and only access files\nwith an extension of .csv.\n\nAdded regression tests and updated the location of the csv files for\nthose regression tests.\n\nmodified:   regress/expected/age_load.out\nmodified:   regress/sql/age_load.sql\nmodified:   src/backend/utils/load/age_load.c"
    },
    {
      "commit": "838926cc35ae64d3514c656959b749719e904b09",
      "tree": "d5f2d2be2f856dff7fa28b46cdcebf01ad48694c",
      "parents": [
        "1bb95bf616abe733dd6d5c02aa32c7f419827646"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Thu Dec 11 21:51:12 2025 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Dec 11 08:51:12 2025 -0800"
      },
      "message": "Migrate python driver configuration to pyproject.toml (#2272)\n\n- Add pyproject.toml with package configuration\n- Simplify setup.py to minimal backward-compatible wrapper.\n- Updated CI workflow and .gitignore.\n- Resolves warning about using setup.py directly."
    },
    {
      "commit": "1bb95bf616abe733dd6d5c02aa32c7f419827646",
      "tree": "a48f4367309c0a1d1d54b057ae2bc2326dc582da",
      "parents": [
        "91de779aee0ee241bf8022b4822a9745c8bc9fa3"
      ],
      "author": {
        "name": "jsell-rh",
        "email": "jsell@redhat.com",
        "time": "Thu Dec 11 10:56:37 2025 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Dec 11 20:56:37 2025 +0500"
      },
      "message": "Convert string to raw string to remove invalid escape sequence warning (#2267)\n\n- Changed \u0027\\s\u0027 to r\u0027\\s\u0027"
    },
    {
      "commit": "91de779aee0ee241bf8022b4822a9745c8bc9fa3",
      "tree": "f5af4ddc4d7ba716923362006450356fbdca9f1e",
      "parents": [
        "898481a8c79381cc9f49ca71a9756a6216b61353"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Thu Dec 11 07:32:13 2025 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Dec 11 20:32:13 2025 +0500"
      },
      "message": "Update grammar file for maintainability (#2270)\n\nConsolidated duplicate code, added helper functions, and reviewed\nthe grammar file for issues.\n\nNOTE: I used an AI tool to review and cleanup the grammar file. I\n      have reviewed all of the work it did.\n\nImprovements:\n\n1. Added KEYWORD_STRDUP macro to eliminate hardcoded string lengths\n2. Consolidated EXPLAIN statement handling into make_explain_stmt helper\n3. Extracted WITH clause validation into validate_return_item_aliases helper\n4. Created make_default_return_node helper for subquery return-less logic\n\nBenefits:\n\n- Reduced code duplication by ~150 lines\n- Improved maintainability with helper functions\n- Eliminated manual string length calculations (error-prone)\n\nAll 29 existing regression tests pass\n\nmodified:   src/backend/parser/cypher_gram.y"
    },
    {
      "commit": "898481a8c79381cc9f49ca71a9756a6216b61353",
      "tree": "c1a1fa1a9b854a45eeb04f58a403895328ab765d",
      "parents": [
        "0ea94644f25bdc08c803ff6521bfd412a6690401"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Wed Dec 10 09:08:36 2025 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Dec 10 22:08:36 2025 +0500"
      },
      "message": "Fix ORDER BY alias resolution with AS in Cypher queries (#2269)\n\nNOTE: This PR was partially created with AI tools and reviewed by a human.\n\nORDER BY clauses failed when referencing column aliases from RETURN:\n\n    MATCH (p:Person) RETURN p.age AS age ORDER BY age DESC\n    ERROR: could not find rte for age\n\nAdded SQL-99 compliant alias matching to find_target_list_entry() that\nchecks if ORDER BY identifier matches a target list alias before\nattempting expression transformation. This enables standard SQL behavior\nfor sorting by aliased columns with DESC/DESCENDING/ASC/ASCENDING.\n\nUpdated regression tests.\n\nAdded regression tests.\nmodified:   regress/expected/cypher_match.out\nmodified:   regress/expected/expr.out\nmodified:   regress/sql/expr.sql\nmodified:   src/backend/parser/cypher_clause.c"
    },
    {
      "commit": "0ea94644f25bdc08c803ff6521bfd412a6690401",
      "tree": "1d3af94bfb09b730cdb78378a6367aef9b8cc5e7",
      "parents": [
        "fa9973aab32adb0b611b09ee26ae127b92059af2"
      ],
      "author": {
        "name": "Aleksey Konovkin",
        "email": "alkon2000@mail.ru",
        "time": "Tue Dec 09 22:49:05 2025 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Dec 10 00:49:05 2025 +0500"
      },
      "message": "Fix possible memory and file descriptors leaks (#2258)\n\n- Used postgres memory allocation functions instead of standard ones.\n- Wrapped main loop of csv loader in PG_TRY block for better error handling."
    },
    {
      "commit": "fa9973aab32adb0b611b09ee26ae127b92059af2",
      "tree": "723640921e4e41af463ff761109dd32df6de318d",
      "parents": [
        "26f748c42b01b7d6ff5c5ec7c4eb84d97066103a"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Tue Dec 09 11:15:26 2025 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Dec 10 00:15:26 2025 +0500"
      },
      "message": "Adjust \u0027could not find rte for\u0027 ERROR message (#2266)\n\nAdjusted the following type of error message. It was mentioned in\nissue 2263 as being incorrect, which it isn\u0027t. However, it did need\nsome clarification added -\n\n    ERROR:  could not find rte for \u003ccolumn name\u003e\n\nAdded a HINT for additional clarity -\n\n    HINT:  variable \u003ccolumn name\u003e does not exist within scope of usage\n\nFor example:\n\n    CREATE p0\u003d(n0), (n1{k:EXISTS{WITH p0}}) RETURN 1\n\n    ERROR:  could not find rte for p0\n    LINE 3:     CREATE p0\u003d(n0), (n1{k:EXISTS{WITH p0}})\n                                              ^\n    HINT:  variable p0 does not exist within scope of usage\n\nAdditionally, added pstate-\u003ep_expr_kind \u003d\u003d EXPR_KIND_INSERT_TARGET to\ntransform_cypher_clause_as_subquery.\n\nUpdated existing regression tests.\nAdded regression tests from issue.\n\nmodified:   regress/expected/cypher_call.out\nmodified:   regress/expected/cypher_subquery.out\nmodified:   regress/expected/cypher_union.out\nmodified:   regress/expected/cypher_with.out\nmodified:   regress/expected/expr.out\nmodified:   regress/expected/list_comprehension.out\nmodified:   regress/expected/scan.out\nmodified:   src/backend/parser/cypher_clause.c\nmodified:   src/backend/parser/cypher_expr.c"
    },
    {
      "commit": "26f748c42b01b7d6ff5c5ec7c4eb84d97066103a",
      "tree": "549c06563dcccb5e99c500ad4f41a61a2641160f",
      "parents": [
        "5aed9ecc5b492a47dc0e449421cf344de58b5edd"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Tue Dec 09 10:51:01 2025 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Dec 09 23:51:01 2025 +0500"
      },
      "message": "Fix Issue 2256: segmentation fault when calling coalesce function (#2259)\n\nFixed issue 2256: A segmentation fault occurs when calling the coalesce\nfunction in PostgreSQL version 17. This likely predates 17 and includes\nother similar types of \"functions\".\n\nSee issues 1124 (PR 1125) and 1303 (PR 1317) for more details.\n\nThis issue is due to coalesce() being processed differently from other\nfunctions. Additionally, greatest() was found to exhibit the same\nbehavior. They were added to the list of types to ignore during the\ncypher analyze phase.\n\nA few others were added: CaseExpr, XmlExpr, ArrayExpr, \u0026 RowExpr.\nAlthough, I wasn\u0027t able to find cases where these caused crashes.\n\nAdded regression tests.\n\nmodified:   regress/expected/cypher.out\nmodified:   regress/sql/cypher.sql\nmodified:   src/backend/parser/cypher_analyze.c"
    },
    {
      "commit": "5aed9ecc5b492a47dc0e449421cf344de58b5edd",
      "tree": "c547d2cbd098a033ad4e7c13ac86e2ede2d1483c",
      "parents": [
        "571c1982437fc3a9a0bebca293418c22ba64e270"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Wed Dec 03 22:17:09 2025 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Dec 03 09:17:09 2025 -0800"
      },
      "message": "Add index on id columns (#2117)\n\n- Whenever a label will be created, indices on id columns will be\n  created by default. In case of vertex, a unique index on id column\n  will be created, which will also serve as a unique constraint.\n  In case of edge, a non-unique index on start_id and end_id columns\n  will be created.\n\n- This change is expected to improve the performance of queries that\n  involve joins. From some performance tests, it was observed that\n  the performance of queries improved alot.\n\n- Loader was updated to insert tuples in indices as well. This has\n  caused to slow the loader down a bit, but it was necessary.\n\n- A bug related to command ids in cypher_delete executor was also fixed."
    },
    {
      "commit": "571c1982437fc3a9a0bebca293418c22ba64e270",
      "tree": "371c0882c6d9000fd56d6b4a9988664c4ca3dbb5",
      "parents": [
        "190354c415851b9eba893b314fea55623518b3a6"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Wed Nov 19 13:45:44 2025 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 20 02:45:44 2025 +0500"
      },
      "message": "Fix issue 2245 - Creating more than 41 vlabels causes crash in drop_graph (#2248)\n\nFixed issue 2245 - Creating more than 41 vlabels causes drop_grapth to fail\nwith \"label (relation) cache corrupted\" and crashing out on the following\ncommand.\n\nThis was due to corruption of the label_relation_cache during the HASH_DELETE\nprocess.\n\nAs the issue was with a cache flush routine, it was necessary to fix them\nall. Here is the list of the flush functions that were fixed -\n\n    static void flush_graph_name_cache(void)\n    static void flush_graph_namespace_cache(void)\n    static void flush_label_name_graph_cache(void)\n    static void flush_label_graph_oid_cache(void)\n    static void flush_label_relation_cache(void)\n    static void flush_label_seq_name_graph_cache(void)\n\nAdded regression tests.\n\nmodified:   regress/expected/catalog.out\nmodified:   regress/sql/catalog.sql\nmodified:   src/backend/utils/cache/ag_cache.c"
    },
    {
      "commit": "190354c415851b9eba893b314fea55623518b3a6",
      "tree": "c2bd250633745a4e17edd00264bccfb5e0006f92",
      "parents": [
        "ec457345e25601c947aff73bfcc7354d97e238a6"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Thu Nov 13 07:57:41 2025 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Nov 13 20:57:41 2025 +0500"
      },
      "message": "Fix issue 2243 - Regression in string concatenation (#2244)\n\nFixed issue 2243 - Regression in string concatenation using the + operator.\nThe issue was in versions 1.5.0 and 1.6.0, at least. It was due to using\nInt8GetDatum instead of Int64GetDatum for the agtype integer field in the\nfollowing functions -\n\n    get_numeric_datum_from_agtype_value\n    get_string_from_agtype_value\n\nThis impacted more than what the original issue covered, but those additional\ncases were resolved too.\n\nAdded regression tests.\n\nmodified:   regress/expected/agtype.out\nmodified:   regress/sql/agtype.sql\nmodified:   src/backend/utils/adt/agtype_ops.c"
    },
    {
      "commit": "ec457345e25601c947aff73bfcc7354d97e238a6",
      "tree": "2d1b0ae09d7cdba467e374577e67a7eb58059f02",
      "parents": [
        "9c370f421ff0d138c14080a069524aff14682e19"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Oct 03 18:32:42 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Oct 04 06:32:42 2025 +0500"
      },
      "message": "Add fast functions for checking edge uniqueness (#2227)\n\nAdded fast functions for checking edge uniqueness. This will help\nimprove performance for MATCH queries with paths longer than 3 but\nless than 11. The normal edge uniqueness function will deal with\nany path 11 and over.\n\n    modified:   age--1.6.0--y.y.y.sql\n    modified:   sql/agtype_graphid.sql\n    modified:   src/backend/parser/cypher_clause.c\n    modified:   src/backend/utils/adt/age_vle.c"
    },
    {
      "commit": "9c370f421ff0d138c14080a069524aff14682e19",
      "tree": "5be43c30884c83039d141b1ccef63bc29dc66fa8",
      "parents": [
        "652aa06f26276a240ec419361143819e6f61dbd5"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Sep 24 10:50:25 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Sep 24 10:50:25 2025 -0700"
      },
      "message": "Bump gopkg.in/yaml.v3 from 3.0.0 to 3.0.1 in /drivers/golang (#2212)\n\nBumps gopkg.in/yaml.v3 from 3.0.0 to 3.0.1.\n\n---\nupdated-dependencies:\n- dependency-name: gopkg.in/yaml.v3\n  dependency-version: 3.0.1\n  dependency-type: indirect\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "652aa06f26276a240ec419361143819e6f61dbd5",
      "tree": "831e93dde140a84db09f619ad74e1e381436ab05",
      "parents": [
        "94770a69751c8ecdbf21887ee83a98ed6ae979c8"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Sep 15 08:55:24 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Sep 15 20:55:24 2025 +0500"
      },
      "message": "Update branch security (#2219)\n\nUpdate branch security for PG17\n\n    modified:   .asf.yaml"
    },
    {
      "commit": "94770a69751c8ecdbf21887ee83a98ed6ae979c8",
      "tree": "7a9f12cd9770a888bf880dfd5b6eef4f0d4dd6a3",
      "parents": [
        "1afd9fb322c182aef9c79969417582590fb4dd8d"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Sep 15 06:35:52 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Sep 15 18:35:52 2025 +0500"
      },
      "message": "Fix issue with CALL/YIELD for user defined and qualified functions. (#2217)\n\nFixed 2 issues with CALL/YIELD -\n\n   1) If a user defined function was in search_path, the transform_FuncCall\n      logic would only find it, if it were part of an extension.\n\n   2) If a function were qualified, the transform_cypher_call_subquery\n      logic would mistakenly extract the schema name instead of the\n      function name.\n\nNOTE: transform_FuncCall should be reviewed for possible refactor.\n\nAdded regression tests.\n\n    modified:   src/backend/parser/cypher_clause.c\n    modified:   src/backend/parser/cypher_expr.c\n    modified:   regress/expected/cypher_call.out\n    modified:   regress/sql/cypher_call.sql"
    },
    {
      "commit": "1afd9fb322c182aef9c79969417582590fb4dd8d",
      "tree": "cdfdef0070f67c5568b620e9e2a35e675bda996d",
      "parents": [
        "4cc02ad2525172b7417ee4d9afa5ee8015b467cb"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Tue Sep 02 22:05:33 2025 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Sep 02 10:05:33 2025 -0700"
      },
      "message": "Fix upgrade script for 1.5.0 to 1.6.0 (#2210)\n\n- Since there are modifications to agtype gin operators, users\n  will have to drop the gin indexes before running this script\n  and recreate them."
    },
    {
      "commit": "4cc02ad2525172b7417ee4d9afa5ee8015b467cb",
      "tree": "8b042511dc9d4441b76849c98aabcaebfbba7568",
      "parents": [
        "7aa2d885d74cb59985bf51648e25e95506e2f612"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Thu Aug 21 12:37:18 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 22 00:37:18 2025 +0500"
      },
      "message": "Advance master branch to Apache AGE version 1.6.0 (#2196)\n\nUpdated the following files to advance the Apache AGE version\nto 1.6.0\n\n       modified:   Makefile\n       modified:   README.md\n       modified:   RELEASE\n       modified:   age.control\n       modified:   docker/Dockerfile\n       renamed:    age--1.5.0--y.y.y.sql -\u003e age--1.5.0--1.6.0.sql\n       modified:   age--1.5.0--y.y.y.sql\n       new file:    age--1.6.0--y.y.y.sql"
    },
    {
      "commit": "7aa2d885d74cb59985bf51648e25e95506e2f612",
      "tree": "24c49a7850b2dc4bee1e4a69971ecc341a888853",
      "parents": [
        "2803ffa7801f37a0d02a22044150e04b2845d43a"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Aug 18 07:50:17 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 18 19:50:17 2025 +0500"
      },
      "message": "fix issue 2205: left doesn\u0027t catch overflow (#2207)\n\nFixed issue 2205 where large int values aren\u0027t detected. The\nfollowing functions were fixed -\n\nleft, right, and substring\n\n    modified:   regress/expected/expr.out\n    modified:   regress/sql/expr.sql\n    modified:   src/backend/utils/adt/agtype.c\n\nAdded regression tests."
    },
    {
      "commit": "2803ffa7801f37a0d02a22044150e04b2845d43a",
      "tree": "2af612a710cb71189e5012deebf9aec48bf4297a",
      "parents": [
        "c21f0658ab92727dd689b1ed1c84636274933e7b"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Wed Aug 13 08:13:29 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 13 20:13:29 2025 +0500"
      },
      "message": "Fix issue 2201: unexpected empty string behavior (#2203)\n\nThis PR fixes the issue of some string functions returning NULL\ninstead of the empty string.\n\nThe string functions affected and corrected are -\n\nreverse, toupper, tolower, rtrim, ltrim, trim, right, left, substring,\nand replace.\n\nAdded additional regression tests. Corrected previous tests.\n\n    modified:   regress/expected/expr.out\n    modified:   regress/sql/expr.sql\n    modified:   src/backend/utils/adt/agtype.c"
    },
    {
      "commit": "c21f0658ab92727dd689b1ed1c84636274933e7b",
      "tree": "e8da0fd27c655844b407a0a97863d2ee533ed565",
      "parents": [
        "d336d6dfeaea5ab8d5c6f429e32922998f02f117"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Wed Aug 13 19:51:08 2025 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 13 07:51:08 2025 -0700"
      },
      "message": "Add missing dependency in jdbc driver (#2206)\n\n- This was also failing CI. The issue is described here https://github.com/gradle/gradle/issues/33950"
    },
    {
      "commit": "d336d6dfeaea5ab8d5c6f429e32922998f02f117",
      "tree": "4de97e139d07d7ff1c5466f7b3225fdfbee35a20",
      "parents": [
        "1277b8240ae003a2fb1ec9b930edcbda6a0cc3ac"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Aug 01 09:32:57 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 01 21:32:57 2025 +0500"
      },
      "message": "Master to PostgreSQL version 17 (#2197)\n\nConflicts:\n\t.asf.yaml\n\t.github/workflows/installcheck.yaml\n\nChanges to be committed:\n\n\tmodified:   .asf.yaml\n\tmodified:   .github/labeler.yml\n\tmodified:   .github/workflows/installcheck.yaml\n\tmodified:   README.md\n\tmodified:   docker/Dockerfile\n\tmodified:   docker/Dockerfile.dev\n\tmodified:   drivers/docker-compose.yml\n\tmodified:   src/backend/nodes/ag_nodes.c\n\tmodified:   src/backend/parser/cypher_analyze.c\n\n* Add support for PG17 (#2130)\n\n- A new node type is introduced for JSON support, that is\n  JsonConstructorExpr - wrapper over FuncExpr/Aggref/WindowFunc for\n  SQL/JSON constructors.\n- Added additional checks for JsonConstructorExpr expression node for\n  which the walker would crash.\n- Removed palloc0fast function call (which is not available in PG17)\n\n* Update CI, README and repo settings for PG17 (#2156)\n\n- Currently, all workflows are targeting the `PG17_prepare` branch,\n  which will be changed to `PG17` once the branch is renamed.\n- Updated all the github workflows\n- Updated the README\n- Updated repo settings\n- Updated the Dockerfiles\n\n* Remove stale bot and update .asf.yaml settings (#2171)\n\n- Removed stale bot.\n  (https://lists.apache.org/thread/qh4h2z6hsjy2v7wg8mwfnl6cbjp28y08)\n- Decrease required PR approvals by one.\n  (https://lists.apache.org/thread/kmz155t6k0h3b26fjpz36924zthqjlpm)\n- Fixed a warning reported by apache infra i.e.\n  \"An error occurred while processing the github\n   feature in .asf.yaml: GitHub discussions can\n   only be enabled if a mailing list target exists\n   for it.\"\n\n* Update labeler.yml\n\nAdjust workflow/labeler.yml to add permissions.\n\n* Adjust CI for PG17 after rename from PG17_prepare (#2182)\n\nAdjusted the following CI files (workflows) for PG17, they\noriginally pointed to PG17_prepare -\n\nmodified:   .github/workflows/go-driver.yml\nmodified:   .github/workflows/installcheck.yaml\nmodified:   .github/workflows/jdbc-driver.yaml\nmodified:   .github/workflows/nodejs-driver.yaml\nmodified:   .github/workflows/python-driver.yaml\nmodified:   drivers/docker-compose.yml\nmodified:   .github/labeler.yml\n\n* Reimplement list comprehension (#2169) (#2188)\n\n* Revert \"Fix issue 1955 - List comprehension in WHERE clause (#2094)\"\n\nThis reverts commit 0f0d9be9ba02fb90272d2053986f2b5aa4a0c25c.\n\n* Revert \"Fix error using list comprehension with WITH * (#1838)\"\n\nThis reverts commit 5e08a2f58693adca55085da8d56eb1831d963d20.\n\n* Revert \"Fix shift/reduce conflict in grammar (#1719)\"\n\nThis reverts commit fab3119a109280fd63237ce17c6d4dd60b7dfc03.\n\n* Revert \"Implement list comprehension (#1610)\"\n\nThis reverts commit 3b2b394eb669c4f80fc893ad224cf5ea4e10c5a9.\n\n* Reimplement list comprehension\n\n- Reimplement list comprehension to use ARRAY sublinks.\n- Some test results were not correct. All the test results that are\n  modified are correct and are verified with neo4j.\n- Also resolves the issue of using list comprehension in MERGE clause (1611)\n  and issue 1850\n\n* Add expression tree walkers for cypher nodes\n\n- Added cypher_raw_expr_tree_walker and cypher_expr_tree_walker, based\n  on Postgres\u0027s raw_expression_tree_walker and expression_tree_walker.\n  These follow the same walker API as Postgres and add support for\n  Cypher-specific nodes.\n- Also added the agtype[] to agtype func and typecast to 1.5.0-y.y.y.sql\n- Simplifies logic for cypher_subquery handling in where clause.\n- Fixes a crash when list comprehension in the WHERE clause references a\n  variable from the preceding MATCH clause.\n\n* Add support for operators in cypher query (#2172)\n\n- Fixed some operator signatures in .sql\n- Added support for PG operators in cypher. Some\n  hardcoded operators are removed, since they are\n  now covered by the general operator handling.\n- Added full typecast syntax that allows for type\n  modifiers.\n- These changes also improve interoperability with\n  other extensions, as reflected in the regression\n  tests.\n- Added a new function to check if graph_oid exists.\n\n* Prevent object access hook from accesing not installed namespace (#2161)\n\nCurrently we cannot install Age to shared_preload_libraries if\npg_cron is installed.\n\nTo prevent following error we must bail out early.\npostgres\u003d# set backtrace_functions to \u0027get_namespace_oid\u0027;\nSET\npostgres\u003d# create extension pg_cron ;\n2025-04-15 16:59:49.867 +05 [30402] ERROR:  schema \"ag_catalog\" does not exist\n2025-04-15 16:59:49.867 +05 [30402] BACKTRACE:\n\t2   postgres                            0x0000000102401ab0 get_namespace_oid + 204\n\t3   age.so                              0x0000000103285cd0 ag_catalog_namespace_id + 28\n\t4   age.so                              0x00000001032846fc ag_relation_id + 32\n\t5   age.so                              0x00000001032efe9c search_label_relation_cache_miss + 84\n\t6   age.so                              0x00000001032efe30 search_label_relation_cache + 100\n\t7   age.so                              0x00000001032842f4 object_access + 384\n\t8   postgres                            0x000000010240a7a0 RunObjectDropHook + 136\n\t9   postgres                            0x00000001023ee85c deleteOneObject + 108\n\t10  postgres                            0x00000001023eb860 deleteObjectsInList + 476\n\t11  postgres                            0x00000001023eba14 performMultipleDeletions + 316\n\t12  postgres                            0x0000000102560244 ATPostAlterTypeCleanup + 2144\n\t13  postgres                            0x0000000102559fb4 ATRewriteCatalogs + 516\n\t14  postgres                            0x00000001025543a8 ATController + 284\n\t15  postgres                            0x00000001025541bc AlterTable + 96\n\t16  postgres                            0x00000001028b8240 ProcessUtilitySlow + 1812\n\t17  postgres                            0x00000001028b600c standard_ProcessUtility + 3684\n\t18  age.so                              0x00000001032844f8 ag_ProcessUtility_hook + 200\n\t19  postgres                            0x00000001028b516c ProcessUtility + 392\n\t20  postgres                            0x000000010250e5b4 execute_sql_string + 812\n\t21  postgres                            0x000000010250d438 execute_extension_script + 2264\n\t22  postgres                            0x000000010250b330 ApplyExtensionUpdates + 1320\n\t23  postgres                            0x0000000102507954 CreateExtensionInternal + 1896\n\t24  postgres                            0x0000000102506ea4 CreateExtension + 1152\n\t25  postgres                            0x00000001028b8ed4 ProcessUtilitySlow + 5032\n\t26  postgres                            0x00000001028b600c standard_ProcessUtility + 3684\n\t27  age.so                              0x00000001032844f8 ag_ProcessUtility_hook + 200\n\t28  postgres                            0x00000001028b516c ProcessUtility + 392\n\t29  postgres                            0x00000001028b4768 PortalRunUtility + 232\n\t30  postgres                            0x00000001028b3660 PortalRunMulti + 756\n\t31  postgres                            0x00000001028b2abc PortalRun + 1008\n\t32  postgres                            0x00000001028ad870 exec_simple_query + 1436\n\t33  postgres                            0x00000001028ac990 PostgresMain + 2472\n\t34  postgres                            0x00000001027a49ac report_fork_failure_to_client + 0\n\t35  postgres                            0x00000001027a3e54 BackendStartup + 520\n\t36  postgres                            0x00000001027a29f0 ServerLoop + 812\n\t37  postgres                            0x000000010279fe0c PostmasterMain + 6484\n\t38  postgres                            0x000000010266acd0 startup_hacks + 0\n\t39  dyld                                0x000000018a3ab154 start + 2476\n\n* Fix CSV import for edge with one property (#2175)\n\n- start_index marks where property fields begin. For edges, it\u0027s 4.\n  If start_index \u003e\u003d total_fields, create empty properties; otherwise,\n  parse the properties.\n\n---------\n\nCo-authored-by: Umar Hayat \u003cm.umarkiani@gmail.com\u003e\nCo-authored-by: Muhammad Taha Naveed \u003cmtaha@apache.org\u003e\nCo-authored-by: Andrey Borodin \u003camborodin@acm.org\u003e\nCo-authored-by: Moontasir Mahmood \u003c53787290+Munmud@users.noreply.github.com\u003e"
    },
    {
      "commit": "1277b8240ae003a2fb1ec9b930edcbda6a0cc3ac",
      "tree": "67b5a4c5ba65cbf8bf21f6573f35bba18a751174",
      "parents": [
        "1251096556b99b07fd80010eb9245f52cb5e36dd"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Wed Jul 09 20:17:51 2025 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 09 08:17:51 2025 -0700"
      },
      "message": "Add support for operators in cypher query (#2172)\n\n- Fixed some operator signatures in .sql\n- Added support for PG operators in cypher. Some\n  hardcoded operators are removed, since they are\n  now covered by the general operator handling.\n- Added full typecast syntax that allows for type\n  modifiers.\n- These changes also improve interoperability with\n  other extensions, as reflected in the regression\n  tests.\n- Added a new function to check if graph_oid exists."
    },
    {
      "commit": "1251096556b99b07fd80010eb9245f52cb5e36dd",
      "tree": "3b403413296151076126c625746864235bfd80a4",
      "parents": [
        "3f873e3fe1094f770d0936983065deb3089e9785"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Tue Jun 10 23:07:49 2025 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 10 11:07:49 2025 -0700"
      },
      "message": "Reimplement list comprehension (#2169)\n\n* Revert \"Fix issue 1955 - List comprehension in WHERE clause (#2094)\"\n\nThis reverts commit 0f0d9be9ba02fb90272d2053986f2b5aa4a0c25c.\n\n* Revert \"Fix error using list comprehension with WITH * (#1838)\"\n\nThis reverts commit 5e08a2f58693adca55085da8d56eb1831d963d20.\n\n* Revert \"Fix shift/reduce conflict in grammar (#1719)\"\n\nThis reverts commit fab3119a109280fd63237ce17c6d4dd60b7dfc03.\n\n* Revert \"Implement list comprehension (#1610)\"\n\nThis reverts commit 3b2b394eb669c4f80fc893ad224cf5ea4e10c5a9.\n\n* Reimplement list comprehension\n\n- Reimplement list comprehension to use ARRAY sublinks.\n- Some test results were not correct. All the test results that are\n  modified are correct and are verified with neo4j.\n- Also resolves the issue of using list comprehension in MERGE clause (1611)\n  and issue 1850\n\n* Add expression tree walkers for cypher nodes\n\n- Added cypher_raw_expr_tree_walker and cypher_expr_tree_walker, based\n  on Postgres\u0027s raw_expression_tree_walker and expression_tree_walker.\n  These follow the same walker API as Postgres and add support for\n  Cypher-specific nodes.\n- Also added the agtype[] to agtype func and typecast to 1.5.0-y.y.y.sql\n- Simplifies logic for cypher_subquery handling in where clause.\n- Fixes a crash when list comprehension in the WHERE clause references a\n  variable from the preceding MATCH clause."
    },
    {
      "commit": "3f873e3fe1094f770d0936983065deb3089e9785",
      "tree": "8daff83cd19e9f436ca849718f02b10fcf7c974b",
      "parents": [
        "7ae3081f479e655ff1f5592694ad18d68c11434b"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Jun 09 11:06:36 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 09 23:06:36 2025 +0500"
      },
      "message": "Update labeler.yml (#2186)\n\nAdjust workflow/labeler.yml to add permissions."
    },
    {
      "commit": "7ae3081f479e655ff1f5592694ad18d68c11434b",
      "tree": "ec22e49da3d3f96e6917388724563a3c30321647",
      "parents": [
        "04bde30c07600381e5aec4468bdaf4e73a7e948f"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Jun 02 12:23:11 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 03 00:23:11 2025 +0500"
      },
      "message": "Fix issue with the CI build and labeler (#2183)\n\nThe build now needs to use https:, instead of git:\nThe labeler now requires a new step before running.\n\nModified the following files -\n\n    .github/workflows/installcheck.yaml\n    .github/workflows/labeler.yml"
    },
    {
      "commit": "04bde30c07600381e5aec4468bdaf4e73a7e948f",
      "tree": "0d3803b51a0843efb5a6b7c28ffb99e87b300353",
      "parents": [
        "23f3e879e7c6d1ed849f476d99abbb65e57f4207"
      ],
      "author": {
        "name": "Moontasir Mahmood",
        "email": "53787290+Munmud@users.noreply.github.com",
        "time": "Sun May 18 15:56:39 2025 +0600"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun May 18 14:56:39 2025 +0500"
      },
      "message": "Fix CSV import for edge with one property (#2175)\n\n- start_index marks where property fields begin. For edges, it\u0027s 4.\n  If start_index \u003e\u003d total_fields, create empty properties; otherwise,\n  parse the properties."
    },
    {
      "commit": "23f3e879e7c6d1ed849f476d99abbb65e57f4207",
      "tree": "70eccf7aa39cfc914f32bfc07a7ff5053229411a",
      "parents": [
        "50422cbd810f35d9ca2111f2be50b4bbc45c175b"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Mon May 12 19:40:33 2025 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon May 12 07:40:33 2025 -0700"
      },
      "message": "Remove stale bot and update .asf.yaml settings (#2171)\n\n- Removed stale bot.\n  (https://lists.apache.org/thread/qh4h2z6hsjy2v7wg8mwfnl6cbjp28y08)\n- Decrease required PR approvals by one.\n  (https://lists.apache.org/thread/kmz155t6k0h3b26fjpz36924zthqjlpm)\n- Fixed a warning reported by apache infra i.e.\n  \"An error occurred while processing the github\n   feature in .asf.yaml: GitHub discussions can\n   only be enabled if a mailing list target exists\n   for it.\""
    },
    {
      "commit": "50422cbd810f35d9ca2111f2be50b4bbc45c175b",
      "tree": "9a1ae71f4272264e94deae403e806fff592fc1c2",
      "parents": [
        "2d2b8547054a96ef96d89fa84be52486ee7626b3"
      ],
      "author": {
        "name": "Andrey Borodin",
        "email": "amborodin@acm.org",
        "time": "Thu May 08 21:31:28 2025 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 08 21:31:28 2025 +0500"
      },
      "message": "Prevent object access hook from accesing not installed namespace (#2161)\n\nCurrently we cannot install Age to shared_preload_libraries if\npg_cron is installed.\n\nTo prevent following error we must bail out early.\npostgres\u003d# set backtrace_functions to \u0027get_namespace_oid\u0027;\nSET\npostgres\u003d# create extension pg_cron ;\n2025-04-15 16:59:49.867 +05 [30402] ERROR:  schema \"ag_catalog\" does not exist\n2025-04-15 16:59:49.867 +05 [30402] BACKTRACE:\n\t2   postgres                            0x0000000102401ab0 get_namespace_oid + 204\n\t3   age.so                              0x0000000103285cd0 ag_catalog_namespace_id + 28\n\t4   age.so                              0x00000001032846fc ag_relation_id + 32\n\t5   age.so                              0x00000001032efe9c search_label_relation_cache_miss + 84\n\t6   age.so                              0x00000001032efe30 search_label_relation_cache + 100\n\t7   age.so                              0x00000001032842f4 object_access + 384\n\t8   postgres                            0x000000010240a7a0 RunObjectDropHook + 136\n\t9   postgres                            0x00000001023ee85c deleteOneObject + 108\n\t10  postgres                            0x00000001023eb860 deleteObjectsInList + 476\n\t11  postgres                            0x00000001023eba14 performMultipleDeletions + 316\n\t12  postgres                            0x0000000102560244 ATPostAlterTypeCleanup + 2144\n\t13  postgres                            0x0000000102559fb4 ATRewriteCatalogs + 516\n\t14  postgres                            0x00000001025543a8 ATController + 284\n\t15  postgres                            0x00000001025541bc AlterTable + 96\n\t16  postgres                            0x00000001028b8240 ProcessUtilitySlow + 1812\n\t17  postgres                            0x00000001028b600c standard_ProcessUtility + 3684\n\t18  age.so                              0x00000001032844f8 ag_ProcessUtility_hook + 200\n\t19  postgres                            0x00000001028b516c ProcessUtility + 392\n\t20  postgres                            0x000000010250e5b4 execute_sql_string + 812\n\t21  postgres                            0x000000010250d438 execute_extension_script + 2264\n\t22  postgres                            0x000000010250b330 ApplyExtensionUpdates + 1320\n\t23  postgres                            0x0000000102507954 CreateExtensionInternal + 1896\n\t24  postgres                            0x0000000102506ea4 CreateExtension + 1152\n\t25  postgres                            0x00000001028b8ed4 ProcessUtilitySlow + 5032\n\t26  postgres                            0x00000001028b600c standard_ProcessUtility + 3684\n\t27  age.so                              0x00000001032844f8 ag_ProcessUtility_hook + 200\n\t28  postgres                            0x00000001028b516c ProcessUtility + 392\n\t29  postgres                            0x00000001028b4768 PortalRunUtility + 232\n\t30  postgres                            0x00000001028b3660 PortalRunMulti + 756\n\t31  postgres                            0x00000001028b2abc PortalRun + 1008\n\t32  postgres                            0x00000001028ad870 exec_simple_query + 1436\n\t33  postgres                            0x00000001028ac990 PostgresMain + 2472\n\t34  postgres                            0x00000001027a49ac report_fork_failure_to_client + 0\n\t35  postgres                            0x00000001027a3e54 BackendStartup + 520\n\t36  postgres                            0x00000001027a29f0 ServerLoop + 812\n\t37  postgres                            0x000000010279fe0c PostmasterMain + 6484\n\t38  postgres                            0x000000010266acd0 startup_hacks + 0\n\t39  dyld                                0x000000018a3ab154 start + 2476"
    },
    {
      "commit": "2d2b8547054a96ef96d89fa84be52486ee7626b3",
      "tree": "0fe1a0bba9c78cd107d1cdbb6c145315eb107b60",
      "parents": [
        "c75d9e477e2f83016a7a144291d2c15038cf229f"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Thu May 08 20:32:58 2025 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 08 08:32:58 2025 -0700"
      },
      "message": "Fix CI build errors caused by missing dependencies (#2163)\n\nAdds extra step in CI workflow to install missing necessary\ndependencies."
    },
    {
      "commit": "c75d9e477e2f83016a7a144291d2c15038cf229f",
      "tree": "fee8d2b64ccfdba4912b42754a0a922b6aa8df7a",
      "parents": [
        "3247728a610fc9e385707a975e0dcd72a00f1773"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Fri Sep 27 10:51:11 2024 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Sep 27 19:51:11 2024 +0200"
      },
      "message": "Adjust repository settings (#2119)\n\nAdjusted the repository settings to adhere more strictly to Apache\r\npolicies."
    },
    {
      "commit": "3247728a610fc9e385707a975e0dcd72a00f1773",
      "tree": "240255fd45483882518230e56898b85b77a25f62",
      "parents": [
        "bd7dd4c2b57c037c4d4fdbf6df19fcee10c1f827"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Wed Sep 11 21:45:58 2024 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Sep 11 09:45:58 2024 -0700"
      },
      "message": "Add support for external extensions (#2088)\n\n- This commit allows the functions from external extensions to be\r\n  called from the Cypher queries, provided that typecast is available\r\n  for args and return type of that function. The extension should be\r\n  installed and the function should be in the search path.\r\n\r\n- Added cypher typecast for pgvector datatypes, its not a direct cast.\r\n  It casts agtype to text and then text to vector.\r\n\r\n- Added regression tests for pg_trgm, fuzzystrmatch and pgvector\r\n  extensions. pg_trgm is another extension that is used for fuzzy\r\n  string matching. These regression test are extra tests that need to\r\n  be explicitly added to the regression suite.\r\n  Following command can be used to do so:\r\n  make installcheck EXTRA_TESTS\u003d\"pg_trgm pgvector fuzzystrmatch\"\r\n\r\n- Updated CI to run the extra tests for the extensions."
    },
    {
      "commit": "bd7dd4c2b57c037c4d4fdbf6df19fcee10c1f827",
      "tree": "311694877e46549927503f3cfa84352a8b9419a4",
      "parents": [
        "0f0d9be9ba02fb90272d2053986f2b5aa4a0c25c"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Tue Sep 10 11:08:01 2024 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Sep 10 23:08:01 2024 +0500"
      },
      "message": "Fix issue 2093: pfree() called with a NULL pointer (#2095)\n\nFixed issue 2093 where pfree() was called with a NULL pointer.\r\n\r\nThe issue is due to some confusion with pfree(). There are 2\r\ndefinitions for it, one that checks for a passed NULL and the\r\nother which does not.\r\n\r\nCreated a function, pfree_if_not_null(), to check for NULL and\r\ncall pfree() if a NULL wasn\u0027t passed.\r\n\r\nModified the pfree references in the following files -\r\n\r\n   src/backend/commands/label_commands.c\r\n   src/backend/executor/cypher_merge.c\r\n   src/backend/executor/cypher_set.c\r\n   src/backend/parser/ag_scanner.l\r\n   src/backend/parser/cypher_analyze.c\r\n   src/backend/parser/cypher_expr.c\r\n   src/backend/parser/cypher_gram.y\r\n   src/backend/parser/cypher_parse_agg.c\r\n   src/backend/utils/adt/age_global_graph.c\r\n   src/backend/utils/adt/age_graphid_ds.c\r\n   src/backend/utils/adt/age_session_info.c\r\n   src/backend/utils/adt/age_vle.c\r\n   src/backend/utils/adt/agtype.c\r\n   src/backend/utils/adt/agtype_gin.c\r\n   src/backend/utils/adt/agtype_raw.c\r\n   src/backend/utils/adt/agtype_util.c\r\n   src/backend/utils/load/ag_load_edges.c\r\n   src/backend/utils/load/ag_load_labels.c\r\n   src/include/utils/age_graphid_ds.h\r\n   src/include/utils/age_session_info.h\r\n   src/include/utils/agtype.h\r\n\r\nAdded regression tests for the original issue."
    },
    {
      "commit": "0f0d9be9ba02fb90272d2053986f2b5aa4a0c25c",
      "tree": "12c875081c9d33956c4e2057e637dcc8124e860f",
      "parents": [
        "c3d658d3af479f5b7f9e1c606e4c7a402006ad0b"
      ],
      "author": {
        "name": "Muhammad Taha Naveed",
        "email": "mtaha@apache.org",
        "time": "Mon Sep 09 22:31:26 2024 +0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Sep 09 10:31:26 2024 -0700"
      },
      "message": "Fix issue 1955 - List comprehension in WHERE clause (#2094)\n\n- Fixed issue when MATCH clause variable is referenced inside list\r\n  comprehension in WHERE clause or in property constraint.\r\n- Added regression tests."
    },
    {
      "commit": "c3d658d3af479f5b7f9e1c606e4c7a402006ad0b",
      "tree": "1a3c92e1c6240bd2bd00c5e82160dce3ce979825",
      "parents": [
        "9c0960efbe5e03c470f3d9b2b1048faa5c81222e"
      ],
      "author": {
        "name": "John Gemignani",
        "email": "jrgemignani@gmail.com",
        "time": "Mon Sep 02 03:04:10 2024 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Sep 02 15:04:10 2024 +0500"
      },
      "message": "Add support for fuzzystrmatch and other external extensions (#2083)\n\nAdded support for the fuzzystrmatch extension and for other\r\nexternal extensions.\r\n\r\nAdded support for typecasting `::pg_text` to TEXTOID.\r\n\r\nAdded regression tests.\r\n\r\n   modified:   sql/agtype_coercions.sql\r\n   modified:   src/backend/parser/cypher_expr.c\r\n   modified:   src/backend/utils/adt/agtype.c\r\n   modified:   regress/expected/expr.out\r\n   modified:   regress/sql/expr.sql"
    }
  ],
  "next": "9c0960efbe5e03c470f3d9b2b1048faa5c81222e"
}
