{"id":45557332,"url":"https://github.com/marselester/duckdb-maxmind","last_synced_at":"2026-04-01T22:13:23.205Z","repository":{"id":337762540,"uuid":"1154095192","full_name":"marselester/duckdb-maxmind","owner":"marselester","description":"DuckDB MaxMind extension written in Zig.","archived":false,"fork":false,"pushed_at":"2026-03-17T21:53:27.000Z","size":96,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-18T09:57:10.410Z","etag":null,"topics":["duckdb","duckdb-extension","geoip","geolite","maxmind","maxmind-db","zig"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marselester.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-10T02:19:53.000Z","updated_at":"2026-03-17T21:53:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/marselester/duckdb-maxmind","commit_stats":null,"previous_names":["marselester/duckdb-maxmind"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/marselester/duckdb-maxmind","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marselester%2Fduckdb-maxmind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marselester%2Fduckdb-maxmind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marselester%2Fduckdb-maxmind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marselester%2Fduckdb-maxmind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marselester","download_url":"https://codeload.github.com/marselester/duckdb-maxmind/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marselester%2Fduckdb-maxmind/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31292637,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["duckdb","duckdb-extension","geoip","geolite","maxmind","maxmind-db","zig"],"created_at":"2026-02-23T06:04:00.584Z","updated_at":"2026-04-01T22:13:23.193Z","avatar_url":"https://github.com/marselester.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DuckDB MaxMind Extension\n\nThis unofficial extension allows DuckDB to read MaxMind databases.\n\n## Quick start\n\nInstall the extension from the\n[community repository](https://duckdb.org/community_extensions/extensions/maxmind).\n\n```sh\n$ duckdb\nINSTALL maxmind FROM community;\nLOAD maxmind;\n```\n\nQuery MMDB files using the `read_mmdb()` table function or\none of the scalar functions, e.g., `geolite_city()` or `mmdb_record()`.\n\n```sql\n-- Try \".mode line\" if you want an untruncated output.\n.mode box\n\n-- Sequential scan over blocks of IP networks.\n-- Record fields are flattened into top-level columns (e.g., city, country, location).\nSELECT network, city.names.en\nFROM read_mmdb('./GeoLite2-City.mmdb')\nWHERE city.names.en IS NOT NULL\nLIMIT 1;\n\n┌─────────────┬───────────┐\n│   network   │    en     │\n├─────────────┼───────────┤\n│ 1.0.64.0/20 │ Hiroshima │\n└─────────────┴───────────┘\n\n-- Look up a record by an IP address.\n-- Pass an empty string as the third parameter to decode all fields.\nSELECT geolite_city(\n  './GeoLite2-City.mmdb', '1.0.64.0', ''\n).city.names.en AS en;\n┌───────────┐\n│    en     │\n├───────────┤\n│ Hiroshima │\n└───────────┘\n\n-- Look up a record from any MMDB file.\nSELECT mmdb_record(\n  './GeoLite2-City.mmdb', '1.0.64.0', 'city'\n)::json -\u003e 'city' -\u003e 'names' -\u003e\u003e 'en' AS en;\n┌───────────┐\n│    en     │\n├───────────┤\n│ Hiroshima │\n└───────────┘\n```\n\n## Usage\n\nAn unsigned extension can be installed from a custom repository\nor downloaded from a [CI run](https://github.com/marselester/duckdb-maxmind/actions).\nNote, you can find the download links at\nthe [Releases](https://github.com/marselester/duckdb-maxmind/releases) page as well.\n\n```sh\n$ duckdb -unsigned\nINSTALL maxmind FROM 'https://marselester.com/duckdb-maxmind';\nLOAD maxmind;\n```\n\nThe extension should work on `macos`, `linux`, and `windows` running on `aarch64` (ARM64) or `x86_64`.\nHere is how you can build the extension for ARM64 Linux glibc\n(use `musl` if you use Alpine Linux, e.g., `aarch64-linux-musl`).\n\n```sh\n$ zig build -Doptimize=ReleaseFast -Dtarget=aarch64-linux-gnu\n$ duckdb -unsigned\nLOAD './zig-out/lib/maxmind.duckdb_extension';\n```\n\nTable function `read_mmdb(path)` scans all IP network blocks in the database.\nFor known GeoLite/GeoIP databases, record fields are flattened into top-level columns\nsuch as city, country, location.\nFor unknown database types, records are returned as JSON in a `record` VARCHAR column.\nUse the optional `network` parameter to limit the scan,\nfor example, `read_mmdb(path, network='1.0.0.0/8')`.\nEmpty records are excluded by default, use `include_empty=true` to include them.\n\nScalar function `mmdb_record(path, ip, fields)` works with any MMDB file\nand returns the record as JSON string.\nThe `fields` parameter is a comma-separated list of record fields to decode.\nPass an empty string to decode all fields.\n\nFor known GeoLite/GeoIP databases, typed scalar functions return structs\nwith a `network` field containing the matched IP network in CIDR notation:\n\n- `geolite_city(path, ip, fields)`\n- `geolite_country(path, ip, fields)`\n- `geolite_asn(path, ip, fields)`\n- `geoip_city(path, ip, fields)`\n- `geoip_country(path, ip, fields)`\n- `geoip_enterprise(path, ip, fields)`\n- `geoip_isp(path, ip, fields)`\n- `geoip_connection_type(path, ip, fields)`\n- `geoip_anonymous_ip(path, ip, fields)`\n- `geoip_anonymous_plus(path, ip, fields)`\n- `geoip_ip_risk(path, ip, fields)`\n- `geoip_densityincome(path, ip, fields)`\n- `geoip_domain(path, ip, fields)`\n- `geoip_static_ip_score(path, ip, fields)`\n- `geoip_user_count(path, ip, fields)`\n\n## Development\n\nClone [duckdb-maxmind](https://github.com/marselester/duckdb-maxmind.git) repository\nand update [extension-template-c](https://github.com/duckdb/extension-template-c) submodule\nthat provides DuckDB C Extension API.\n\n```sh\n$ git clone https://github.com/marselester/duckdb-maxmind.git\n$ cd ./duckdb-maxmind/\n$ git submodule update --init --recursive\n```\n\nMake sure the extension works by running a DuckDB interactive session.\n\n```sh\n$ brew install duckdb\n$ zig build duckdb -Doptimize=ReleaseFast\nD SELECT mmdb_record('./GeoLite2-City.mmdb', '1.0.64.0', '');\n```\n\nRun the lookup benchmark to catch regressions (1M random IPs against GeoLite2-City).\n\n```sh\n$ zig build benchmark_lookup -Doptimize=ReleaseFast\n```\n\nHere are reference results on Apple M2 Pro (DuckDB calls functions from different threads).\n\n\u003cdetails\u003e\n\n\u003csummary\u003eAll fields vs filtered (struct)\u003c/summary\u003e\n\n```sh\n$ for i in $(seq 1 10); do\n    zig build benchmark_lookup -Doptimize=ReleaseFast -- GeoLite2-City.mmdb 1000000 \\\n      2\u003e\u00261 | grep 'Lookups Per Second'\n  done\n\n  echo '---'\n\n  for i in $(seq 1 10); do\n    zig build benchmark_lookup -Doptimize=ReleaseFast -- GeoLite2-City.mmdb 1000000 city \\\n      2\u003e\u00261 | grep 'Lookups Per Second'\n  done\n\nLookups Per Second: 1181048\nLookups Per Second: 1375543\nLookups Per Second: 1369311\nLookups Per Second: 1369761\nLookups Per Second: 1324712\nLookups Per Second: 1338950\nLookups Per Second: 1365158\nLookups Per Second: 1361891\nLookups Per Second: 1341626\nLookups Per Second: 1346066\n---\nLookups Per Second: 1645663\nLookups Per Second: 1615902\nLookups Per Second: 1644466\nLookups Per Second: 1524310\nLookups Per Second: 1629483\nLookups Per Second: 1640131\nLookups Per Second: 1566966\nLookups Per Second: 1628056\nLookups Per Second: 1636196\nLookups Per Second: 1386362\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003eAll fields vs filtered (JSON)\u003c/summary\u003e\n\n```sh\n$ for i in $(seq 1 10); do\n    zig build benchmark_lookup_json -Doptimize=ReleaseFast -- GeoLite2-City.mmdb 1000000 \\\n      2\u003e\u00261 | grep 'Lookups Per Second'\n  done\n\n  echo '---'\n\n  for i in $(seq 1 10); do\n    zig build benchmark_lookup_json -Doptimize=ReleaseFast -- GeoLite2-City.mmdb 1000000 city \\\n      2\u003e\u00261 | grep 'Lookups Per Second'\n  done\n\nLookups Per Second: 1474290\nLookups Per Second: 1430456\nLookups Per Second: 1495560\nLookups Per Second: 1477165\nLookups Per Second: 1201247\nLookups Per Second: 1470980\nLookups Per Second: 1493345\nLookups Per Second: 1473481\nLookups Per Second: 1491169\nLookups Per Second: 1499607\n---\nLookups Per Second: 1723675\nLookups Per Second: 1753231\nLookups Per Second: 1674606\nLookups Per Second: 1738747\nLookups Per Second: 1751980\nLookups Per Second: 1729041\nLookups Per Second: 1741489\nLookups Per Second: 1699169\nLookups Per Second: 1706883\nLookups Per Second: 1540820\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003eSELECT * FROM read_mmdb('GeoLite2-City.mmdb')\u003c/summary\u003e\n\n```sql\nD SELECT * FROM read_mmdb('GeoLite2-City.mmdb');\n100% ▕██████████████████████████████████████▏ (00:00:09.63 elapsed)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003eSELECT count(*) FROM read_mmdb('GeoLite2-City.mmdb')\u003c/summary\u003e\n\n```sql\nD SELECT count(*) FROM read_mmdb('GeoLite2-City.mmdb');\n100% ▕██████████████████████████████████████▏ (00:00:02.39 elapsed)\n┌────────────────┐\n│  count_star()  │\n│     int64      │\n├────────────────┤\n│    5502351     │\n│ (5.50 million) │\n└────────────────┘\n```\n\n\u003c/details\u003e\n\nYou might need to update [duckdb.zig](./src/duckdb.zig)\nif there are breaking changes in\n[duckdb_extension.h](https://github.com/duckdb/extension-template-c/blob/main/duckdb_capi/duckdb_extension.h).\n\n```sh\n$ zig translate-c ./extension-template-c/duckdb_capi/duckdb_extension.h \u003e src/duckdb.zig\n```\n\nKudos to @habedi for making\n[template-duckdb-extension-zig](https://github.com/habedi/template-duckdb-extension-zig).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarselester%2Fduckdb-maxmind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarselester%2Fduckdb-maxmind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarselester%2Fduckdb-maxmind/lists"}