diff --git a/docs/functions.md b/docs/functions.md
index 7a92fcb8..aa8d728b 100644
--- a/docs/functions.md
+++ b/docs/functions.md
@@ -27,6 +27,25 @@ All of the DuckDB [json functions and aggregates](https://duckdb.org/docs/data/j
| [`union_extract`](#union_extract) | Extracts a value from a union type by tag name. |
| [`union_tag`](#union_tag) | Gets the tag name of the active member in a union type. |
+## MAP Functions
+
+All of the DuckDB [map functions](https://duckdb.org/docs/sql/data_types/map.html#map-functions).
+
+| Name | Description |
+| :--- | :---------- |
+| [`cardinality`](#cardinality) | Return the size of the map |
+| [`element_at`](#element_at) | Return the value for a given key as a list |
+| [`map_concat`](#map_concat) | Merge multiple maps |
+| [`map_contains`](#map_contains) | Check if a map contains a given key |
+| [`map_contains_entry`](#map_contains_entry) | Check if a map contains a given key-value pair |
+| [`map_contains_value`](#map_contains_value) | Check if a map contains a given value |
+| [`map_entries`](#map_entries) | Return a list of struct(k, v) for each key-value pair |
+| [`map_extract`](#map_extract) | Extract a value from a map using a key |
+| [`map_extract_value`](#map_extract_value) | Return the value for a given key or NULL |
+| [`map_from_entries`](#map_from_entries) | Create a map from an array of struct(k, v) |
+| [`map_keys`](#map_keys) | Get all keys from a map as a list |
+| [`map_values`](#map_values) | Get all values from a map as a list |
+
## Aggregates
|Name|Description|
@@ -672,6 +691,277 @@ WHERE epoch(created_at) > 1640995200; -- After 2022-01-01
| :--- | :--- | :---------- |
| timestamp_expr | timestamp | The timestamp to convert to epoch seconds |
+#### `map_extract(map_col duckdb.map, key duckdb.unresolved_type) -> duckdb.unresolved_type`
+
+Extracts a value from a map using the specified key. If the key doesn't exist, returns an empty array.
+
+```sql
+-- Extract value from a map
+SELECT map_extract(r['map_col'], 'a') as value
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: {1}
+
+-- Extract non-existent key
+SELECT map_extract(r['map_col'], 'c') as value
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: {}
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to extract from |
+| key | duckdb.unresolved_type | The key to look up in the map |
+
+#### `map_keys(map_col duckdb.map) -> duckdb.unresolved_type`
+
+Returns all keys from a map as an array.
+
+```sql
+-- Get all keys from a map
+SELECT map_keys(r['map_col']) as keys
+FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+-- Returns: {a,b,c}
+
+-- Empty map
+SELECT map_keys(r['map_col']) as keys
+FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+-- Returns: {}
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to extract keys from |
+
+#### `map_values(map_col duckdb.map) -> duckdb.unresolved_type`
+
+Returns all values from a map as an array.
+
+```sql
+-- Get all values from a map
+SELECT map_values(r['map_col']) as values
+FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+-- Returns: {1,2,3}
+
+-- Empty map
+SELECT map_values(r['map_col']) as values
+FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+-- Returns: {}
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to extract values from |
+
+#### `cardinality(map_col duckdb.map) -> numeric`
+
+Returns the size of the map (number of key-value pairs).
+
+```sql
+-- Get the number of entries in a map
+SELECT cardinality(r['map_col']) as size
+FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+-- Returns: 3
+
+-- Empty map
+SELECT cardinality(r['map_col']) as size
+FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+-- Returns: 0
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to get the size of |
+
+#### `element_at(map_col duckdb.map, key duckdb.unresolved_type) -> duckdb.unresolved_type`
+
+Returns the value for a given key as an array.
+
+```sql
+-- Get value for a specific key
+SELECT element_at(r['map_col'], 'a') as value
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: {1}
+
+-- Non-existent key
+SELECT element_at(r['map_col'], 'c') as value
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: {}
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to extract from |
+| key | duckdb.unresolved_type | The key to look up in the map |
+
+#### `map_concat(map_col duckdb.map, map_col2 duckdb.map) -> duckdb.map`
+
+Merges multiple maps. On key collision, the value is taken from the last map.
+
+```sql
+-- Merge two maps
+SELECT map_concat(r1['map1'], r2['map2']) as merged
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map1 $$) r1,
+ duckdb.query($$ SELECT MAP(['b', 'c'], [3, 4]) as map2 $$) r2;
+-- Returns: {a=1, b=3, c=4}
+
+-- Note: 'b' value from map2 (3) overwrites map1's value (2)
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The first map |
+| map_col2 | duckdb.map | The second map to merge |
+
+#### `map_contains(map_col duckdb.map, key duckdb.unresolved_type) -> boolean`
+
+Checks if a map contains a given key.
+
+```sql
+-- Check if key exists
+SELECT map_contains(r['map_col'], 'a') as has_key
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: t (true)
+
+-- Check for non-existent key
+SELECT map_contains(r['map_col'], 'c') as has_key
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: f (false)
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to check |
+| key | duckdb.unresolved_type | The key to search for |
+
+#### `map_contains_entry(map_col duckdb.map, key duckdb.unresolved_type, value duckdb.unresolved_type) -> boolean`
+
+Checks if a map contains a given key-value pair.
+
+```sql
+-- Check if key-value pair exists
+SELECT map_contains_entry(r['map_col'], 'a', 1) as has_entry
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: t (true)
+
+-- Check with wrong value for existing key
+SELECT map_contains_entry(r['map_col'], 'a', 2) as has_entry
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: f (false)
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to check |
+| key | duckdb.unresolved_type | The key to search for |
+| value | duckdb.unresolved_type | The value to match with the key |
+
+#### `map_contains_value(map_col duckdb.map, value duckdb.unresolved_type) -> boolean`
+
+Checks if a map contains a given value.
+
+```sql
+-- Check if value exists
+SELECT map_contains_value(r['map_col'], 1) as has_value
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: t (true)
+
+-- Check for non-existent value
+SELECT map_contains_value(r['map_col'], 3) as has_value
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: f (false)
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to check |
+| value | duckdb.unresolved_type | The value to search for |
+
+#### `map_entries(map_col duckdb.map) -> duckdb.struct[]`
+
+Returns an array of struct(key, value) for each key-value pair in the map.
+
+```sql
+-- Get all key-value pairs as structs
+SELECT map_entries(r['map_col']) as entries
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: {"(a,1)","(b,2)"}
+
+-- Access individual struct fields
+SELECT unnest(map_entries(r['map_col'])) as entry
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to extract entries from |
+
+#### `map_extract_value(map_col duckdb.map, key duckdb.unresolved_type) -> duckdb.unresolved_type`
+
+Returns the value for a given key or NULL if the key is not contained in the map.
+
+```sql
+-- Extract single value (not as array)
+SELECT map_extract_value(r['map_col'], 'a') as value
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: 1
+
+-- Non-existent key returns NULL
+SELECT map_extract_value(r['map_col'], 'c') as value
+FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map_col $$) r;
+-- Returns: NULL
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| map_col | duckdb.map | The map to extract from |
+| key | duckdb.unresolved_type | The key to look up in the map |
+
+#### `map_from_entries(entries duckdb.struct[]) -> duckdb.map`
+
+Creates a map from an array of struct(k, v).
+
+```sql
+-- Create map from array of structs
+SELECT map_from_entries(r['entries']) as new_map
+FROM duckdb.query($$
+ SELECT [{'k': 'a', 'v': 1}, {'k': 'b', 'v': 2}] as entries
+$$) r;
+-- Returns: {a=1, b=2}
+
+-- This is the inverse operation of map_entries
+SELECT map_from_entries(map_entries(r['map_col'])) as reconstructed
+FROM duckdb.query($$ SELECT MAP(['x', 'y'], [10, 20]) as map_col $$) r;
+-- Returns: {x=10, y=20}
+```
+
+##### Required Arguments
+
+| Name | Type | Description |
+| :--- | :--- | :---------- |
+| entries | duckdb.struct[] | Array of structs with 'k' (key) and 'v' (value) fields |
+
#### `epoch_ms(timestamp_expr)` -> `BIGINT`
Converts timestamps to Unix epoch milliseconds.
diff --git a/sql/pg_duckdb--1.0.0--1.1.0.sql b/sql/pg_duckdb--1.0.0--1.1.0.sql
index e69de29b..5db78578 100644
--- a/sql/pg_duckdb--1.0.0--1.1.0.sql
+++ b/sql/pg_duckdb--1.0.0--1.1.0.sql
@@ -0,0 +1,140 @@
+-- Add MAP functions support
+-- Extract value from map using key
+CREATE FUNCTION @extschema@.map_extract(map_col duckdb.map, key duckdb.unresolved_type)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract(map_col duckdb.unresolved_type, key duckdb.unresolved_type)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract(map_col duckdb.map, key text)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract(map_col duckdb.unresolved_type, key text)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract(map_col duckdb.map, key integer)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract(map_col duckdb.unresolved_type, key integer)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract(map_col duckdb.map, key bigint)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract(map_col duckdb.unresolved_type, key bigint)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Get all keys from map
+CREATE FUNCTION @extschema@.map_keys(map_col duckdb.map)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_keys(map_col duckdb.unresolved_type)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Get all values from map
+CREATE FUNCTION @extschema@.map_values(map_col duckdb.map)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_values(map_col duckdb.unresolved_type)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Get map size
+CREATE FUNCTION @extschema@.cardinality(map_col duckdb.map)
+RETURNS numeric AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.cardinality(map_col duckdb.unresolved_type)
+RETURNS numeric AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Get element at key (alias for map_extract)
+CREATE FUNCTION @extschema@.element_at(map_col duckdb.map, key duckdb.unresolved_type)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.element_at(map_col duckdb.unresolved_type, key duckdb.unresolved_type)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.element_at(map_col duckdb.map, key text)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.element_at(map_col duckdb.unresolved_type, key text)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.element_at(map_col duckdb.map, key integer)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.element_at(map_col duckdb.unresolved_type, key integer)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.element_at(map_col duckdb.map, key bigint)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.element_at(map_col duckdb.unresolved_type, key bigint)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Merge multiple maps
+CREATE FUNCTION @extschema@.map_concat(map_col duckdb.map, map_col2 duckdb.map)
+RETURNS duckdb.map AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_concat(map_col duckdb.unresolved_type, map_col2 duckdb.map)
+RETURNS duckdb.map AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_concat(map_col duckdb.map, map_col2 duckdb.unresolved_type)
+RETURNS duckdb.map AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_concat(map_col duckdb.unresolved_type, map_col2 duckdb.unresolved_type)
+RETURNS duckdb.map AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Check if map contains key
+CREATE FUNCTION @extschema@.map_contains(map_col duckdb.map, key duckdb.unresolved_type)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains(map_col duckdb.unresolved_type, key duckdb.unresolved_type)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains(map_col duckdb.map, key text)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains(map_col duckdb.unresolved_type, key text)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains(map_col duckdb.map, key integer)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains(map_col duckdb.unresolved_type, key integer)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains(map_col duckdb.map, key bigint)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains(map_col duckdb.unresolved_type, key bigint)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Check if map contains key-value pair
+CREATE FUNCTION @extschema@.map_contains_entry(map_col duckdb.map, key duckdb.unresolved_type, value duckdb.unresolved_type)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_entry(map_col duckdb.unresolved_type, key duckdb.unresolved_type, value duckdb.unresolved_type)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_entry(map_col duckdb.map, key text, value text)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_entry(map_col duckdb.unresolved_type, key text, value text)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Check if map contains value
+CREATE FUNCTION @extschema@.map_contains_value(map_col duckdb.map, value duckdb.unresolved_type)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_value(map_col duckdb.unresolved_type, value duckdb.unresolved_type)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_value(map_col duckdb.map, value text)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_value(map_col duckdb.unresolved_type, value text)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_value(map_col duckdb.map, value integer)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_value(map_col duckdb.unresolved_type, value integer)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_value(map_col duckdb.map, value bigint)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_contains_value(map_col duckdb.unresolved_type, value bigint)
+RETURNS boolean AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Get all key-value pairs as structs
+CREATE FUNCTION @extschema@.map_entries(map_col duckdb.map)
+RETURNS duckdb.struct[] AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_entries(map_col duckdb.unresolved_type)
+RETURNS duckdb.struct[] AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Extract single value from map (not as list)
+CREATE FUNCTION @extschema@.map_extract_value(map_col duckdb.map, key duckdb.unresolved_type)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract_value(map_col duckdb.unresolved_type, key duckdb.unresolved_type)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract_value(map_col duckdb.map, key text)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract_value(map_col duckdb.unresolved_type, key text)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract_value(map_col duckdb.map, key integer)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract_value(map_col duckdb.unresolved_type, key integer)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract_value(map_col duckdb.map, key bigint)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_extract_value(map_col duckdb.unresolved_type, key bigint)
+RETURNS duckdb.unresolved_type AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+
+-- Create map from array of struct(k, v)
+CREATE FUNCTION @extschema@.map_from_entries(entries duckdb.struct[])
+RETURNS duckdb.map AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
+CREATE FUNCTION @extschema@.map_from_entries(entries duckdb.unresolved_type)
+RETURNS duckdb.map AS 'MODULE_PATHNAME', 'duckdb_only_function' LANGUAGE C;
diff --git a/src/pgduckdb_metadata_cache.cpp b/src/pgduckdb_metadata_cache.cpp
index 0492b879..b11543da 100644
--- a/src/pgduckdb_metadata_cache.cpp
+++ b/src/pgduckdb_metadata_cache.cpp
@@ -183,7 +183,19 @@ BuildDuckdbOnlyFunctions() {
"make_timestamptz",
"time_bucket",
"union_extract",
- "union_tag"};
+ "union_tag",
+ "cardinality",
+ "element_at",
+ "map_concat",
+ "map_contains",
+ "map_contains_entry",
+ "map_contains_value",
+ "map_entries",
+ "map_extract",
+ "map_extract_value",
+ "map_from_entries",
+ "map_keys",
+ "map_values"};
for (uint32_t i = 0; i < lengthof(function_names); i++) {
CatCList *catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(function_names[i]));
diff --git a/test/regression/expected/map_functions.out b/test/regression/expected/map_functions.out
new file mode 100644
index 00000000..e5f397cd
--- /dev/null
+++ b/test/regression/expected/map_functions.out
@@ -0,0 +1,66 @@
+-- Test MAP functions
+-- These tests verify that the MAP functions are properly exposed and working
+-- Test map_extract function
+SELECT map_extract(r['map_col'], 'a') as extracted_value FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+ extracted_value
+-----------------
+ {1}
+(1 row)
+
+SELECT map_extract(r['map_col'], 'd') as missing_key FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+ missing_key
+-------------
+ {}
+(1 row)
+
+SELECT map_extract(r['map_col'], 'key1') as string_value FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ string_value
+--------------
+ {value1}
+(1 row)
+
+-- Test map_keys function
+SELECT map_keys(r['map_col']) as keys FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+ keys
+---------
+ {a,b,c}
+(1 row)
+
+SELECT map_keys(r['map_col']) as empty_keys FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+ empty_keys
+------------
+ {}
+(1 row)
+
+-- Test map_values function
+SELECT map_values(r['map_col']) as values FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+ values
+---------
+ {1,2,3}
+(1 row)
+
+SELECT map_values(r['map_col']) as empty_values FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+ empty_values
+--------------
+ {}
+(1 row)
+
+-- Test with unresolved_type for flexibility
+SELECT map_extract(r['map_col']::duckdb.unresolved_type, 'x') as flexible_extract FROM duckdb.query($$ SELECT MAP(['x', 'y'], [10, 20]) as map_col $$) r;
+ flexible_extract
+------------------
+ {10}
+(1 row)
+
+SELECT map_keys(r['map_col']::duckdb.unresolved_type) as flexible_keys FROM duckdb.query($$ SELECT MAP(['p', 'q'], [100, 200]) as map_col $$) r;
+ flexible_keys
+---------------
+ {p,q}
+(1 row)
+
+SELECT map_values(r['map_col']::duckdb.unresolved_type) as flexible_values FROM duckdb.query($$ SELECT MAP(['m', 'n'], [1000, 2000]) as map_col $$) r;
+ flexible_values
+-----------------
+ {1000,2000}
+(1 row)
+
diff --git a/test/regression/expected/map_functions_comprehensive.out b/test/regression/expected/map_functions_comprehensive.out
new file mode 100644
index 00000000..9dee1b83
--- /dev/null
+++ b/test/regression/expected/map_functions_comprehensive.out
@@ -0,0 +1,140 @@
+-- Test MAP functions
+SET duckdb.force_execution = false;
+-- Basic functions
+SELECT cardinality(r['map_col']) as cardinality_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2', 'key3'], ['value1', 'value2', 'value3']) as map_col $$) r;
+ cardinality_result
+--------------------
+ 3
+(1 row)
+
+SELECT element_at(r['map_col'], 'key1') as element_at_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ element_at_result
+-------------------
+ {value1}
+(1 row)
+
+SELECT map_concat(r1['map1'], r2['map2']) as map_concat_result FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map1 $$) r1, duckdb.query($$ SELECT MAP(['b', 'c'], [3, 4]) as map2 $$) r2;
+ map_concat_result
+-------------------
+ {a=1, b=3, c=4}
+(1 row)
+
+-- Contains functions
+SELECT map_contains(r['map_col'], 'key1') as contains_key_true FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ contains_key_true
+-------------------
+ t
+(1 row)
+
+SELECT map_contains(r['map_col'], 'key3') as contains_key_false FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ contains_key_false
+--------------------
+ f
+(1 row)
+
+SELECT map_contains_entry(r['map_col'], 'key1', 'value1') as contains_entry_true FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ contains_entry_true
+---------------------
+ t
+(1 row)
+
+SELECT map_contains_entry(r['map_col'], 'key1', 'value2') as contains_entry_false FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ contains_entry_false
+----------------------
+ f
+(1 row)
+
+SELECT map_contains_value(r['map_col'], 'value1') as contains_value_true FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ contains_value_true
+---------------------
+ t
+(1 row)
+
+SELECT map_contains_value(r['map_col'], 'value3') as contains_value_false FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ contains_value_false
+----------------------
+ f
+(1 row)
+
+-- Extract functions
+SELECT map_extract(r['map_col'], 'key1') as map_extract_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ map_extract_result
+--------------------
+ {value1}
+(1 row)
+
+SELECT map_extract_value(r['map_col'], 'key1') as map_extract_value_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ map_extract_value_result
+--------------------------
+ value1
+(1 row)
+
+SELECT map_extract_value(r['map_col'], 'key3') as map_extract_value_null FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+ map_extract_value_null
+------------------------
+
+(1 row)
+
+-- Keys and values
+SELECT map_from_entries(r['entries']) as map_from_entries_result FROM duckdb.query($$ SELECT [{'k': 'a', 'v': 1}, {'k': 'b', 'v': 2}] as entries $$) r;
+ map_from_entries_result
+-------------------------
+ {a=1, b=2}
+(1 row)
+
+SELECT map_keys(r['map_col']) as map_keys_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2', 'key3'], ['value1', 'value2', 'value3']) as map_col $$) r;
+ map_keys_result
+------------------
+ {key1,key2,key3}
+(1 row)
+
+SELECT map_values(r['map_col']) as map_values_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2', 'key3'], ['value1', 'value2', 'value3']) as map_col $$) r;
+ map_values_result
+------------------------
+ {value1,value2,value3}
+(1 row)
+
+-- Different data types
+SELECT cardinality(r['map_col']) as cardinality_numeric_keys FROM duckdb.query($$ SELECT MAP([1, 2, 3], ['a', 'b', 'c']) as map_col $$) r;
+ cardinality_numeric_keys
+--------------------------
+ 3
+(1 row)
+
+SELECT map_contains(r['map_col'], 1) as contains_numeric_key FROM duckdb.query($$ SELECT MAP([1, 2, 3], ['a', 'b', 'c']) as map_col $$) r;
+ contains_numeric_key
+----------------------
+ t
+(1 row)
+
+-- Edge cases
+SELECT cardinality(r['map_col']) as cardinality_empty FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+ cardinality_empty
+-------------------
+ 0
+(1 row)
+
+SELECT map_keys(r['map_col']) as keys_empty FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+ keys_empty
+------------
+ {}
+(1 row)
+
+SELECT map_values(r['map_col']) as values_empty FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+ values_empty
+--------------
+ {}
+(1 row)
+
+SELECT map_extract(r['map_col'], 'non_existent') as extract_non_existent FROM duckdb.query($$ SELECT MAP(['key1'], ['value1']) as map_col $$) r;
+ extract_non_existent
+----------------------
+ {}
+(1 row)
+
+SELECT map_contains(r['map_col'], 'non_existent') as contains_non_existent FROM duckdb.query($$ SELECT MAP(['key1'], ['value1']) as map_col $$) r;
+ contains_non_existent
+-----------------------
+ f
+(1 row)
+
diff --git a/test/regression/schedule b/test/regression/schedule
index 7f646e48..32a120cd 100644
--- a/test/regression/schedule
+++ b/test/regression/schedule
@@ -52,6 +52,8 @@ test: transaction_isolation
test: transactions
test: type_support
test: union_functions
+test: map_functions
+test: map_functions_comprehensive
test: unresolved_type
test: views
test: parallel_postgres_scan
diff --git a/test/regression/sql/map_functions.sql b/test/regression/sql/map_functions.sql
new file mode 100644
index 00000000..f45e3b08
--- /dev/null
+++ b/test/regression/sql/map_functions.sql
@@ -0,0 +1,20 @@
+-- Test MAP functions
+-- These tests verify that the MAP functions are properly exposed and working
+
+-- Test map_extract function
+SELECT map_extract(r['map_col'], 'a') as extracted_value FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+SELECT map_extract(r['map_col'], 'd') as missing_key FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+SELECT map_extract(r['map_col'], 'key1') as string_value FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+
+-- Test map_keys function
+SELECT map_keys(r['map_col']) as keys FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+SELECT map_keys(r['map_col']) as empty_keys FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+
+-- Test map_values function
+SELECT map_values(r['map_col']) as values FROM duckdb.query($$ SELECT MAP(['a', 'b', 'c'], [1, 2, 3]) as map_col $$) r;
+SELECT map_values(r['map_col']) as empty_values FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+
+-- Test with unresolved_type for flexibility
+SELECT map_extract(r['map_col']::duckdb.unresolved_type, 'x') as flexible_extract FROM duckdb.query($$ SELECT MAP(['x', 'y'], [10, 20]) as map_col $$) r;
+SELECT map_keys(r['map_col']::duckdb.unresolved_type) as flexible_keys FROM duckdb.query($$ SELECT MAP(['p', 'q'], [100, 200]) as map_col $$) r;
+SELECT map_values(r['map_col']::duckdb.unresolved_type) as flexible_values FROM duckdb.query($$ SELECT MAP(['m', 'n'], [1000, 2000]) as map_col $$) r;
diff --git a/test/regression/sql/map_functions_comprehensive.sql b/test/regression/sql/map_functions_comprehensive.sql
new file mode 100644
index 00000000..a5a3b130
--- /dev/null
+++ b/test/regression/sql/map_functions_comprehensive.sql
@@ -0,0 +1,36 @@
+-- Test MAP functions
+SET duckdb.force_execution = false;
+
+-- Basic functions
+SELECT cardinality(r['map_col']) as cardinality_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2', 'key3'], ['value1', 'value2', 'value3']) as map_col $$) r;
+SELECT element_at(r['map_col'], 'key1') as element_at_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+SELECT map_concat(r1['map1'], r2['map2']) as map_concat_result FROM duckdb.query($$ SELECT MAP(['a', 'b'], [1, 2]) as map1 $$) r1, duckdb.query($$ SELECT MAP(['b', 'c'], [3, 4]) as map2 $$) r2;
+
+-- Contains functions
+SELECT map_contains(r['map_col'], 'key1') as contains_key_true FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+SELECT map_contains(r['map_col'], 'key3') as contains_key_false FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+SELECT map_contains_entry(r['map_col'], 'key1', 'value1') as contains_entry_true FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+SELECT map_contains_entry(r['map_col'], 'key1', 'value2') as contains_entry_false FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+SELECT map_contains_value(r['map_col'], 'value1') as contains_value_true FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+SELECT map_contains_value(r['map_col'], 'value3') as contains_value_false FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+
+-- Extract functions
+SELECT map_extract(r['map_col'], 'key1') as map_extract_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+SELECT map_extract_value(r['map_col'], 'key1') as map_extract_value_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+SELECT map_extract_value(r['map_col'], 'key3') as map_extract_value_null FROM duckdb.query($$ SELECT MAP(['key1', 'key2'], ['value1', 'value2']) as map_col $$) r;
+
+-- Keys and values
+SELECT map_from_entries(r['entries']) as map_from_entries_result FROM duckdb.query($$ SELECT [{'k': 'a', 'v': 1}, {'k': 'b', 'v': 2}] as entries $$) r;
+SELECT map_keys(r['map_col']) as map_keys_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2', 'key3'], ['value1', 'value2', 'value3']) as map_col $$) r;
+SELECT map_values(r['map_col']) as map_values_result FROM duckdb.query($$ SELECT MAP(['key1', 'key2', 'key3'], ['value1', 'value2', 'value3']) as map_col $$) r;
+
+-- Different data types
+SELECT cardinality(r['map_col']) as cardinality_numeric_keys FROM duckdb.query($$ SELECT MAP([1, 2, 3], ['a', 'b', 'c']) as map_col $$) r;
+SELECT map_contains(r['map_col'], 1) as contains_numeric_key FROM duckdb.query($$ SELECT MAP([1, 2, 3], ['a', 'b', 'c']) as map_col $$) r;
+
+-- Edge cases
+SELECT cardinality(r['map_col']) as cardinality_empty FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+SELECT map_keys(r['map_col']) as keys_empty FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+SELECT map_values(r['map_col']) as values_empty FROM duckdb.query($$ SELECT MAP([], []) as map_col $$) r;
+SELECT map_extract(r['map_col'], 'non_existent') as extract_non_existent FROM duckdb.query($$ SELECT MAP(['key1'], ['value1']) as map_col $$) r;
+SELECT map_contains(r['map_col'], 'non_existent') as contains_non_existent FROM duckdb.query($$ SELECT MAP(['key1'], ['value1']) as map_col $$) r;