[{"data":1,"prerenderedAt":1519},["ShallowReactive",2],{"page-\u002Fguides\u002Fduckdb":3},{"id":4,"title":5,"body":6,"description":1512,"extension":1513,"meta":1514,"navigation":1104,"path":1515,"seo":1516,"stem":1517,"__hash__":1518},"content\u002F4.guides\u002F13.duckdb.md","DuckDB Source",{"type":7,"value":8,"toc":1481},"minimark",[9,13,18,28,105,109,220,233,236,241,352,356,447,458,462,566,570,576,733,742,764,768,771,775,795,799,819,823,832,836,866,870,874,900,904,907,975,979,982,1013,1017,1020,1058,1062,1069,1177,1180,1184,1187,1213,1217,1220,1266,1270,1273,1307,1310,1314,1328,1335,1344,1348,1355,1363,1367,1439,1443,1477],[10,11,12],"p",{},"This guide covers serving vector tiles generated on-the-fly from SQL queries against embedded DuckDB databases — PostGIS-class spatial queries with zero infrastructure.",[14,15,17],"h2",{"id":16},"why-duckdb","Why DuckDB?",[10,19,20,27],{},[21,22,26],"a",{"href":23,"rel":24},"https:\u002F\u002Fduckdb.org\u002F",[25],"nofollow","DuckDB"," is an embedded analytical database that runs in-process — no server to install, configure, or maintain. Combined with its spatial extension, it provides PostGIS-equivalent spatial operations:",[29,30,31,46],"table",{},[32,33,34],"thead",{},[35,36,37,41,43],"tr",{},[38,39,40],"th",{},"Feature",[38,42,5],{},[38,44,45],{},"PostgreSQL\u002FPostGIS Source",[47,48,49,61,72,83,94],"tbody",{},[35,50,51,55,58],{},[52,53,54],"td",{},"Setup",[52,56,57],{},"Zero — embedded in binary",[52,59,60],{},"Requires running PostgreSQL server",[35,62,63,66,69],{},[52,64,65],{},"Configuration",[52,67,68],{},"SQL query in config.toml",[52,70,71],{},"Connection string + credentials",[35,73,74,77,80],{},[52,75,76],{},"Data formats",[52,78,79],{},"Parquet, CSV, JSON, Shapefile, S3",[52,81,82],{},"PostgreSQL tables only",[35,84,85,88,91],{},[52,86,87],{},"Spatial ops",[52,89,90],{},"ST_Intersects, ST_Simplify, etc.",[52,92,93],{},"Full PostGIS suite",[35,95,96,99,102],{},[52,97,98],{},"Deployment",[52,100,101],{},"Single binary, no deps",[52,103,104],{},"Requires PostgreSQL",[14,106,108],{"id":107},"quick-start","Quick Start",[110,111,116],"pre",{"className":112,"code":113,"language":114,"meta":115,"style":115},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[[sources]]\nid = \"places\"\ntype = \"duckdb\"\npath = \"\u002Fdata\u002Foverture.duckdb\"\nquery = \"\"\"\n  SELECT name, ST_AsWKB(geometry) as geometry\n  FROM places\n  WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n\"\"\"\n","toml","",[117,118,119,135,155,170,185,196,202,208,214],"code",{"__ignoreMap":115},[120,121,124,128,132],"span",{"class":122,"line":123},"line",1,[120,125,127],{"class":126},"sMK4o","[[",[120,129,131],{"class":130},"sBMFI","sources",[120,133,134],{"class":126},"]]\n",[120,136,138,142,145,148,152],{"class":122,"line":137},2,[120,139,141],{"class":140},"sTEyZ","id ",[120,143,144],{"class":126},"=",[120,146,147],{"class":126}," \"",[120,149,151],{"class":150},"sfazB","places",[120,153,154],{"class":126},"\"\n",[120,156,158,161,163,165,168],{"class":122,"line":157},3,[120,159,160],{"class":140},"type ",[120,162,144],{"class":126},[120,164,147],{"class":126},[120,166,167],{"class":150},"duckdb",[120,169,154],{"class":126},[120,171,173,176,178,180,183],{"class":122,"line":172},4,[120,174,175],{"class":140},"path ",[120,177,144],{"class":126},[120,179,147],{"class":126},[120,181,182],{"class":150},"\u002Fdata\u002Foverture.duckdb",[120,184,154],{"class":126},[120,186,188,191,193],{"class":122,"line":187},5,[120,189,190],{"class":140},"query ",[120,192,144],{"class":126},[120,194,195],{"class":126}," \"\"\"\n",[120,197,199],{"class":122,"line":198},6,[120,200,201],{"class":150},"  SELECT name, ST_AsWKB(geometry) as geometry\n",[120,203,205],{"class":122,"line":204},7,[120,206,207],{"class":150},"  FROM places\n",[120,209,211],{"class":122,"line":210},8,[120,212,213],{"class":150},"  WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n",[120,215,217],{"class":122,"line":216},9,[120,218,219],{"class":126},"\"\"\"\n",[221,222,224],"alert",{"type":223},"info",[10,225,226,227,229,230],{},"DuckDB support requires the ",[117,228,167],{}," feature flag: ",[117,231,232],{},"cargo build --features duckdb",[14,234,65],{"id":235},"configuration",[237,238,240],"h3",{"id":239},"query-a-duckdb-database","Query a DuckDB Database",[110,242,244],{"className":112,"code":243,"language":114,"meta":115,"style":115},"[[sources]]\nid = \"places\"\ntype = \"duckdb\"\npath = \"\u002Fdata\u002Foverture.duckdb\"\nquery = \"\"\"\n  SELECT name, category, ST_AsWKB(geometry) as geometry\n  FROM places\n  WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n\"\"\"\nlayer_name = \"places\"\nminzoom = 0\nmaxzoom = 14\n",[117,245,246,254,266,278,290,298,303,307,311,315,329,341],{"__ignoreMap":115},[120,247,248,250,252],{"class":122,"line":123},[120,249,127],{"class":126},[120,251,131],{"class":130},[120,253,134],{"class":126},[120,255,256,258,260,262,264],{"class":122,"line":137},[120,257,141],{"class":140},[120,259,144],{"class":126},[120,261,147],{"class":126},[120,263,151],{"class":150},[120,265,154],{"class":126},[120,267,268,270,272,274,276],{"class":122,"line":157},[120,269,160],{"class":140},[120,271,144],{"class":126},[120,273,147],{"class":126},[120,275,167],{"class":150},[120,277,154],{"class":126},[120,279,280,282,284,286,288],{"class":122,"line":172},[120,281,175],{"class":140},[120,283,144],{"class":126},[120,285,147],{"class":126},[120,287,182],{"class":150},[120,289,154],{"class":126},[120,291,292,294,296],{"class":122,"line":187},[120,293,190],{"class":140},[120,295,144],{"class":126},[120,297,195],{"class":126},[120,299,300],{"class":122,"line":198},[120,301,302],{"class":150},"  SELECT name, category, ST_AsWKB(geometry) as geometry\n",[120,304,305],{"class":122,"line":204},[120,306,207],{"class":150},[120,308,309],{"class":122,"line":210},[120,310,213],{"class":150},[120,312,313],{"class":122,"line":216},[120,314,219],{"class":126},[120,316,318,321,323,325,327],{"class":122,"line":317},10,[120,319,320],{"class":140},"layer_name ",[120,322,144],{"class":126},[120,324,147],{"class":126},[120,326,151],{"class":150},[120,328,154],{"class":126},[120,330,332,335,337],{"class":122,"line":331},11,[120,333,334],{"class":140},"minzoom ",[120,336,144],{"class":126},[120,338,340],{"class":339},"sbssI"," 0\n",[120,342,344,347,349],{"class":122,"line":343},12,[120,345,346],{"class":140},"maxzoom ",[120,348,144],{"class":126},[120,350,351],{"class":339}," 14\n",[237,353,355],{"id":354},"query-geoparquet-directly-no-database-file","Query GeoParquet Directly (No Database File)",[110,357,359],{"className":112,"code":358,"language":114,"meta":115,"style":115},"[[sources]]\nid = \"buildings\"\ntype = \"duckdb\"\npath = \"\"\nquery = \"\"\"\n  SELECT name, height, ST_AsWKB(geometry) as geometry\n  FROM read_parquet('\u002Fdata\u002Foverture-buildings.parquet')\n  WHERE bbox.xmin \u003C= {bbox_xmax} AND bbox.xmax >= {bbox_xmin}\n    AND bbox.ymin \u003C= {bbox_ymax} AND bbox.ymax >= {bbox_ymin}\n\"\"\"\nlayer_name = \"buildings\"\n",[117,360,361,369,382,394,403,411,416,421,426,431,435],{"__ignoreMap":115},[120,362,363,365,367],{"class":122,"line":123},[120,364,127],{"class":126},[120,366,131],{"class":130},[120,368,134],{"class":126},[120,370,371,373,375,377,380],{"class":122,"line":137},[120,372,141],{"class":140},[120,374,144],{"class":126},[120,376,147],{"class":126},[120,378,379],{"class":150},"buildings",[120,381,154],{"class":126},[120,383,384,386,388,390,392],{"class":122,"line":157},[120,385,160],{"class":140},[120,387,144],{"class":126},[120,389,147],{"class":126},[120,391,167],{"class":150},[120,393,154],{"class":126},[120,395,396,398,400],{"class":122,"line":172},[120,397,175],{"class":140},[120,399,144],{"class":126},[120,401,402],{"class":126}," \"\"\n",[120,404,405,407,409],{"class":122,"line":187},[120,406,190],{"class":140},[120,408,144],{"class":126},[120,410,195],{"class":126},[120,412,413],{"class":122,"line":198},[120,414,415],{"class":150},"  SELECT name, height, ST_AsWKB(geometry) as geometry\n",[120,417,418],{"class":122,"line":204},[120,419,420],{"class":150},"  FROM read_parquet('\u002Fdata\u002Foverture-buildings.parquet')\n",[120,422,423],{"class":122,"line":210},[120,424,425],{"class":150},"  WHERE bbox.xmin \u003C= {bbox_xmax} AND bbox.xmax >= {bbox_xmin}\n",[120,427,428],{"class":122,"line":216},[120,429,430],{"class":150},"    AND bbox.ymin \u003C= {bbox_ymax} AND bbox.ymax >= {bbox_ymin}\n",[120,432,433],{"class":122,"line":317},[120,434,219],{"class":126},[120,436,437,439,441,443,445],{"class":122,"line":331},[120,438,320],{"class":140},[120,440,144],{"class":126},[120,442,147],{"class":126},[120,444,379],{"class":150},[120,446,154],{"class":126},[10,448,449,450,453,454,457],{},"Set ",[117,451,452],{},"path"," to an empty string for in-memory DuckDB — no ",[117,455,456],{},".duckdb"," file needed.",[237,459,461],{"id":460},"full-options","Full Options",[29,463,464,477],{},[32,465,466],{},[35,467,468,471,474],{},[38,469,470],{},"Option",[38,472,473],{},"Description",[38,475,476],{},"Default",[47,478,479,506,520,536,551],{},[35,480,481,486,503],{},[52,482,483],{},[117,484,485],{},"query",[52,487,488,489,492,493,492,496,492,499,502],{},"SQL template with ",[117,490,491],{},"{bbox}",", ",[117,494,495],{},"{z}",[117,497,498],{},"{x}",[117,500,501],{},"{y}"," placeholders",[52,504,505],{},"Required",[35,507,508,512,518],{},[52,509,510],{},[117,511,452],{},[52,513,514,515,517],{},"Path to ",[117,516,456],{}," file (empty string = in-memory)",[52,519,505],{},[35,521,522,527,530],{},[52,523,524],{},[117,525,526],{},"layer_name",[52,528,529],{},"MVT layer name in tile output",[52,531,532,533],{},"Source ",[117,534,535],{},"id",[35,537,538,543,546],{},[52,539,540],{},[117,541,542],{},"minzoom",[52,544,545],{},"Minimum zoom level to serve",[52,547,548],{},[117,549,550],{},"0",[35,552,553,558,561],{},[52,554,555],{},[117,556,557],{},"maxzoom",[52,559,560],{},"Maximum zoom level to serve",[52,562,563],{},[117,564,565],{},"22",[14,567,569],{"id":568},"sql-template-variables","SQL Template Variables",[10,571,572,573,575],{},"The ",[117,574,485],{}," field is a SQL template. These placeholders are substituted per tile request:",[29,577,578,593],{},[32,579,580],{},[35,581,582,585,588,590],{},[38,583,584],{},"Variable",[38,586,587],{},"Type",[38,589,473],{},[38,591,592],{},"Example",[47,594,595,615,633,650,667,684,701,717],{},[35,596,597,601,604,610],{},[52,598,599],{},[117,600,491],{},[52,602,603],{},"4 doubles",[52,605,606,607],{},"Tile bounding box as ",[117,608,609],{},"xmin, ymin, xmax, ymax",[52,611,612],{},[117,613,614],{},"-122.5, 37.7, -122.4, 37.8",[35,616,617,622,625,628],{},[52,618,619],{},[117,620,621],{},"{bbox_xmin}",[52,623,624],{},"double",[52,626,627],{},"Tile bbox minimum longitude",[52,629,630],{},[117,631,632],{},"-122.5",[35,634,635,640,642,645],{},[52,636,637],{},[117,638,639],{},"{bbox_ymin}",[52,641,624],{},[52,643,644],{},"Tile bbox minimum latitude",[52,646,647],{},[117,648,649],{},"37.7",[35,651,652,657,659,662],{},[52,653,654],{},[117,655,656],{},"{bbox_xmax}",[52,658,624],{},[52,660,661],{},"Tile bbox maximum longitude",[52,663,664],{},[117,665,666],{},"-122.4",[35,668,669,674,676,679],{},[52,670,671],{},[117,672,673],{},"{bbox_ymax}",[52,675,624],{},[52,677,678],{},"Tile bbox maximum latitude",[52,680,681],{},[117,682,683],{},"37.8",[35,685,686,690,693,696],{},[52,687,688],{},[117,689,495],{},[52,691,692],{},"integer",[52,694,695],{},"Zoom level",[52,697,698],{},[117,699,700],{},"14",[35,702,703,707,709,712],{},[52,704,705],{},[117,706,498],{},[52,708,692],{},[52,710,711],{},"Tile X coordinate",[52,713,714],{},[117,715,716],{},"8192",[35,718,719,723,725,728],{},[52,720,721],{},[117,722,501],{},[52,724,692],{},[52,726,727],{},"Tile Y coordinate",[52,729,730],{},[117,731,732],{},"5461",[10,734,735,737,738,741],{},[117,736,491],{}," expands to four comma-separated values, which is exactly what ",[117,739,740],{},"ST_MakeEnvelope()"," expects:",[110,743,747],{"className":744,"code":745,"language":746,"meta":115,"style":115},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","ST_MakeEnvelope({bbox})\n-- becomes:\nST_MakeEnvelope(-122.5, 37.7, -122.4, 37.8)\n","sql",[117,748,749,754,759],{"__ignoreMap":115},[120,750,751],{"class":122,"line":123},[120,752,753],{},"ST_MakeEnvelope({bbox})\n",[120,755,756],{"class":122,"line":137},[120,757,758],{},"-- becomes:\n",[120,760,761],{"class":122,"line":157},[120,762,763],{},"ST_MakeEnvelope(-122.5, 37.7, -122.4, 37.8)\n",[14,765,767],{"id":766},"spatial-functions","Spatial Functions",[10,769,770],{},"DuckDB's spatial extension provides PostGIS-equivalent functions. The extension is auto-loaded at startup.",[237,772,774],{"id":773},"spatial-predicates","Spatial Predicates",[110,776,778],{"className":744,"code":777,"language":746,"meta":115,"style":115},"WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\nWHERE ST_Within(geometry, ST_MakeEnvelope({bbox}))\nWHERE ST_Contains(boundary, ST_Point(-74.006, 40.7128))\n",[117,779,780,785,790],{"__ignoreMap":115},[120,781,782],{"class":122,"line":123},[120,783,784],{},"WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n",[120,786,787],{"class":122,"line":137},[120,788,789],{},"WHERE ST_Within(geometry, ST_MakeEnvelope({bbox}))\n",[120,791,792],{"class":122,"line":157},[120,793,794],{},"WHERE ST_Contains(boundary, ST_Point(-74.006, 40.7128))\n",[237,796,798],{"id":797},"geometry-construction","Geometry Construction",[110,800,802],{"className":744,"code":801,"language":746,"meta":115,"style":115},"ST_MakeEnvelope(xmin, ymin, xmax, ymax)  -- bbox → polygon\nST_Point(longitude, latitude)             -- coordinates → point\nST_GeomFromWKB(wkb_blob)                 -- WKB → geometry\n",[117,803,804,809,814],{"__ignoreMap":115},[120,805,806],{"class":122,"line":123},[120,807,808],{},"ST_MakeEnvelope(xmin, ymin, xmax, ymax)  -- bbox → polygon\n",[120,810,811],{"class":122,"line":137},[120,812,813],{},"ST_Point(longitude, latitude)             -- coordinates → point\n",[120,815,816],{"class":122,"line":157},[120,817,818],{},"ST_GeomFromWKB(wkb_blob)                 -- WKB → geometry\n",[237,820,822],{"id":821},"geometry-output","Geometry Output",[110,824,826],{"className":744,"code":825,"language":746,"meta":115,"style":115},"ST_AsWKB(geometry)   -- geometry → WKB (required for MVT encoding)\n",[117,827,828],{"__ignoreMap":115},[120,829,830],{"class":122,"line":123},[120,831,825],{},[237,833,835],{"id":834},"geometry-processing","Geometry Processing",[110,837,839],{"className":744,"code":838,"language":746,"meta":115,"style":115},"ST_Simplify(geometry, tolerance)                    -- Douglas-Peucker simplification\nST_SimplifyPreserveTopology(geometry, tolerance)     -- topology-preserving simplification\nST_Buffer(geometry, distance)                       -- buffer around geometry\nST_Centroid(geometry)                               -- centroid point\nST_Area(geometry)                                   -- polygon area\n",[117,840,841,846,851,856,861],{"__ignoreMap":115},[120,842,843],{"class":122,"line":123},[120,844,845],{},"ST_Simplify(geometry, tolerance)                    -- Douglas-Peucker simplification\n",[120,847,848],{"class":122,"line":137},[120,849,850],{},"ST_SimplifyPreserveTopology(geometry, tolerance)     -- topology-preserving simplification\n",[120,852,853],{"class":122,"line":157},[120,854,855],{},"ST_Buffer(geometry, distance)                       -- buffer around geometry\n",[120,857,858],{"class":122,"line":172},[120,859,860],{},"ST_Centroid(geometry)                               -- centroid point\n",[120,862,863],{"class":122,"line":187},[120,864,865],{},"ST_Area(geometry)                                   -- polygon area\n",[14,867,869],{"id":868},"query-patterns","Query Patterns",[237,871,873],{"id":872},"basic-spatial-filter","Basic Spatial Filter",[110,875,877],{"className":744,"code":876,"language":746,"meta":115,"style":115},"query = \"\"\"\n  SELECT name, ST_AsWKB(geometry) as geometry\n  FROM places\n  WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n\"\"\"\n",[117,878,879,884,888,892,896],{"__ignoreMap":115},[120,880,881],{"class":122,"line":123},[120,882,883],{},"query = \"\"\"\n",[120,885,886],{"class":122,"line":137},[120,887,201],{},[120,889,890],{"class":122,"line":157},[120,891,207],{},[120,893,894],{"class":122,"line":172},[120,895,213],{},[120,897,898],{"class":122,"line":187},[120,899,219],{},[237,901,903],{"id":902},"zoom-dependent-simplification","Zoom-Dependent Simplification",[10,905,906],{},"Simplify geometries at low zoom levels for faster rendering:",[110,908,910],{"className":744,"code":909,"language":746,"meta":115,"style":115},"query = \"\"\"\n  SELECT name, type,\n    ST_AsWKB(\n      CASE\n        WHEN {z} >= 14 THEN geometry\n        WHEN {z} >= 10 THEN ST_Simplify(geometry, 0.0001)\n        WHEN {z} >= 6  THEN ST_Simplify(geometry, 0.001)\n        ELSE                ST_Simplify(geometry, 0.01)\n      END\n    ) as geometry\n  FROM polygons\n  WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n\"\"\"\n",[117,911,912,916,921,926,931,936,941,946,951,956,961,966,970],{"__ignoreMap":115},[120,913,914],{"class":122,"line":123},[120,915,883],{},[120,917,918],{"class":122,"line":137},[120,919,920],{},"  SELECT name, type,\n",[120,922,923],{"class":122,"line":157},[120,924,925],{},"    ST_AsWKB(\n",[120,927,928],{"class":122,"line":172},[120,929,930],{},"      CASE\n",[120,932,933],{"class":122,"line":187},[120,934,935],{},"        WHEN {z} >= 14 THEN geometry\n",[120,937,938],{"class":122,"line":198},[120,939,940],{},"        WHEN {z} >= 10 THEN ST_Simplify(geometry, 0.0001)\n",[120,942,943],{"class":122,"line":204},[120,944,945],{},"        WHEN {z} >= 6  THEN ST_Simplify(geometry, 0.001)\n",[120,947,948],{"class":122,"line":210},[120,949,950],{},"        ELSE                ST_Simplify(geometry, 0.01)\n",[120,952,953],{"class":122,"line":216},[120,954,955],{},"      END\n",[120,957,958],{"class":122,"line":317},[120,959,960],{},"    ) as geometry\n",[120,962,963],{"class":122,"line":331},[120,964,965],{},"  FROM polygons\n",[120,967,968],{"class":122,"line":343},[120,969,213],{},[120,971,973],{"class":122,"line":972},13,[120,974,219],{},[237,976,978],{"id":977},"feature-filtering-by-zoom","Feature Filtering by Zoom",[10,980,981],{},"Show fewer features at low zoom levels:",[110,983,985],{"className":744,"code":984,"language":746,"meta":115,"style":115},"query = \"\"\"\n  SELECT name, height, ST_AsWKB(geometry) as geometry\n  FROM buildings\n  WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n    AND height > CASE WHEN {z} \u003C 12 THEN 50 ELSE 0 END\n\"\"\"\n",[117,986,987,991,995,1000,1004,1009],{"__ignoreMap":115},[120,988,989],{"class":122,"line":123},[120,990,883],{},[120,992,993],{"class":122,"line":137},[120,994,415],{},[120,996,997],{"class":122,"line":157},[120,998,999],{},"  FROM buildings\n",[120,1001,1002],{"class":122,"line":172},[120,1003,213],{},[120,1005,1006],{"class":122,"line":187},[120,1007,1008],{},"    AND height > CASE WHEN {z} \u003C 12 THEN 50 ELSE 0 END\n",[120,1010,1011],{"class":122,"line":198},[120,1012,219],{},[237,1014,1016],{"id":1015},"multi-table-joins","Multi-Table Joins",[10,1018,1019],{},"Combine data from multiple tables in a single tile:",[110,1021,1023],{"className":744,"code":1022,"language":746,"meta":115,"style":115},"query = \"\"\"\n  SELECT b.name, b.height, c.population,\n         ST_AsWKB(b.geometry) as geometry\n  FROM buildings b\n  JOIN census c ON ST_Within(b.geometry, c.boundary)\n  WHERE ST_Intersects(b.geometry, ST_MakeEnvelope({bbox}))\n\"\"\"\n",[117,1024,1025,1029,1034,1039,1044,1049,1054],{"__ignoreMap":115},[120,1026,1027],{"class":122,"line":123},[120,1028,883],{},[120,1030,1031],{"class":122,"line":137},[120,1032,1033],{},"  SELECT b.name, b.height, c.population,\n",[120,1035,1036],{"class":122,"line":157},[120,1037,1038],{},"         ST_AsWKB(b.geometry) as geometry\n",[120,1040,1041],{"class":122,"line":172},[120,1042,1043],{},"  FROM buildings b\n",[120,1045,1046],{"class":122,"line":187},[120,1047,1048],{},"  JOIN census c ON ST_Within(b.geometry, c.boundary)\n",[120,1050,1051],{"class":122,"line":198},[120,1052,1053],{},"  WHERE ST_Intersects(b.geometry, ST_MakeEnvelope({bbox}))\n",[120,1055,1056],{"class":122,"line":204},[120,1057,219],{},[237,1059,1061],{"id":1060},"directory-and-glob-support","Directory and Glob Support",[10,1063,1064,1065,1068],{},"Unlike the GeoParquet source (which currently takes a single file), DuckDB's ",[117,1066,1067],{},"read_parquet()"," natively supports globs and directories — no additional code needed:",[110,1070,1072],{"className":744,"code":1071,"language":746,"meta":115,"style":115},"-- All parquet files in a directory\nquery = \"\"\"\n  SELECT name, ST_AsWKB(geometry) as geometry\n  FROM read_parquet('.\u002Fdata\u002Foverture\u002Ftheme=places\u002Ftype=place\u002F*.parquet')\n  WHERE bbox.xmin \u003C= {bbox_xmax} AND bbox.xmax >= {bbox_xmin}\n\"\"\"\n\n-- Recursive glob across partitions\nquery = \"\"\"\n  SELECT name, ST_AsWKB(geometry) as geometry\n  FROM read_parquet('.\u002Fdata\u002Foverture\u002Ftheme=places\u002F**\u002F*.parquet')\n  WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n\"\"\"\n\n-- Hive partitioning (auto-extracts theme\u002Ftype as columns)\nquery = \"\"\"\n  SELECT name, theme, type, ST_AsWKB(geometry) as geometry\n  FROM read_parquet('.\u002Fdata\u002Foverture\u002F**\u002F*.parquet', hive_partitioning=true)\n  WHERE type = 'place'\n    AND ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n\"\"\"\n",[117,1073,1074,1079,1083,1087,1092,1096,1100,1106,1111,1115,1119,1124,1128,1132,1137,1143,1148,1154,1160,1166,1172],{"__ignoreMap":115},[120,1075,1076],{"class":122,"line":123},[120,1077,1078],{},"-- All parquet files in a directory\n",[120,1080,1081],{"class":122,"line":137},[120,1082,883],{},[120,1084,1085],{"class":122,"line":157},[120,1086,201],{},[120,1088,1089],{"class":122,"line":172},[120,1090,1091],{},"  FROM read_parquet('.\u002Fdata\u002Foverture\u002Ftheme=places\u002Ftype=place\u002F*.parquet')\n",[120,1093,1094],{"class":122,"line":187},[120,1095,425],{},[120,1097,1098],{"class":122,"line":198},[120,1099,219],{},[120,1101,1102],{"class":122,"line":204},[120,1103,1105],{"emptyLinePlaceholder":1104},true,"\n",[120,1107,1108],{"class":122,"line":210},[120,1109,1110],{},"-- Recursive glob across partitions\n",[120,1112,1113],{"class":122,"line":216},[120,1114,883],{},[120,1116,1117],{"class":122,"line":317},[120,1118,201],{},[120,1120,1121],{"class":122,"line":331},[120,1122,1123],{},"  FROM read_parquet('.\u002Fdata\u002Foverture\u002Ftheme=places\u002F**\u002F*.parquet')\n",[120,1125,1126],{"class":122,"line":343},[120,1127,213],{},[120,1129,1130],{"class":122,"line":972},[120,1131,219],{},[120,1133,1135],{"class":122,"line":1134},14,[120,1136,1105],{"emptyLinePlaceholder":1104},[120,1138,1140],{"class":122,"line":1139},15,[120,1141,1142],{},"-- Hive partitioning (auto-extracts theme\u002Ftype as columns)\n",[120,1144,1146],{"class":122,"line":1145},16,[120,1147,883],{},[120,1149,1151],{"class":122,"line":1150},17,[120,1152,1153],{},"  SELECT name, theme, type, ST_AsWKB(geometry) as geometry\n",[120,1155,1157],{"class":122,"line":1156},18,[120,1158,1159],{},"  FROM read_parquet('.\u002Fdata\u002Foverture\u002F**\u002F*.parquet', hive_partitioning=true)\n",[120,1161,1163],{"class":122,"line":1162},19,[120,1164,1165],{},"  WHERE type = 'place'\n",[120,1167,1169],{"class":122,"line":1168},20,[120,1170,1171],{},"    AND ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n",[120,1173,1175],{"class":122,"line":1174},21,[120,1176,219],{},[10,1178,1179],{},"This makes DuckDB the simplest way to serve tiles from large partitioned datasets like Overture Maps (483 files, 476 GB) without any preprocessing.",[237,1181,1183],{"id":1182},"query-geoparquet-from-s3http","Query GeoParquet from S3\u002FHTTP",[10,1185,1186],{},"DuckDB reads remote files natively:",[110,1188,1190],{"className":744,"code":1189,"language":746,"meta":115,"style":115},"query = \"\"\"\n  SELECT name, ST_AsWKB(geometry) as geometry\n  FROM read_parquet('s3:\u002F\u002Foverturemaps-us-west-2\u002Frelease\u002F2025-03\u002Ftheme=places\u002F**\u002F*.parquet')\n  WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n\"\"\"\n",[117,1191,1192,1196,1200,1205,1209],{"__ignoreMap":115},[120,1193,1194],{"class":122,"line":123},[120,1195,883],{},[120,1197,1198],{"class":122,"line":137},[120,1199,201],{},[120,1201,1202],{"class":122,"line":157},[120,1203,1204],{},"  FROM read_parquet('s3:\u002F\u002Foverturemaps-us-west-2\u002Frelease\u002F2025-03\u002Ftheme=places\u002F**\u002F*.parquet')\n",[120,1206,1207],{"class":122,"line":172},[120,1208,213],{},[120,1210,1211],{"class":122,"line":187},[120,1212,219],{},[237,1214,1216],{"id":1215},"aggregation-per-tile","Aggregation per Tile",[10,1218,1219],{},"Compute statistics within each tile:",[110,1221,1223],{"className":744,"code":1222,"language":746,"meta":115,"style":115},"query = \"\"\"\n  SELECT type,\n         count(*) as feature_count,\n         avg(height) as avg_height,\n         ST_AsWKB(ST_Centroid(ST_Collect(geometry))) as geometry\n  FROM buildings\n  WHERE ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n  GROUP BY type\n\"\"\"\n",[117,1224,1225,1229,1234,1239,1244,1249,1253,1257,1262],{"__ignoreMap":115},[120,1226,1227],{"class":122,"line":123},[120,1228,883],{},[120,1230,1231],{"class":122,"line":137},[120,1232,1233],{},"  SELECT type,\n",[120,1235,1236],{"class":122,"line":157},[120,1237,1238],{},"         count(*) as feature_count,\n",[120,1240,1241],{"class":122,"line":172},[120,1242,1243],{},"         avg(height) as avg_height,\n",[120,1245,1246],{"class":122,"line":187},[120,1247,1248],{},"         ST_AsWKB(ST_Centroid(ST_Collect(geometry))) as geometry\n",[120,1250,1251],{"class":122,"line":198},[120,1252,999],{},[120,1254,1255],{"class":122,"line":204},[120,1256,213],{},[120,1258,1259],{"class":122,"line":210},[120,1260,1261],{},"  GROUP BY type\n",[120,1263,1264],{"class":122,"line":216},[120,1265,219],{},[237,1267,1269],{"id":1268},"fast-filtering-with-bbox-columns","Fast Filtering with Bbox Columns",[10,1271,1272],{},"For GeoParquet files with bbox struct columns (GeoParquet 1.1), combine column-level filtering with spatial predicates for best performance:",[110,1274,1276],{"className":744,"code":1275,"language":746,"meta":115,"style":115},"query = \"\"\"\n  SELECT name, ST_AsWKB(geometry) as geometry\n  FROM read_parquet('\u002Fdata\u002Fplaces.parquet')\n  WHERE bbox.xmin \u003C= {bbox_xmax} AND bbox.xmax >= {bbox_xmin}\n    AND bbox.ymin \u003C= {bbox_ymax} AND bbox.ymax >= {bbox_ymin}\n    AND ST_Intersects(geometry, ST_MakeEnvelope({bbox}))\n\"\"\"\n",[117,1277,1278,1282,1286,1291,1295,1299,1303],{"__ignoreMap":115},[120,1279,1280],{"class":122,"line":123},[120,1281,883],{},[120,1283,1284],{"class":122,"line":137},[120,1285,201],{},[120,1287,1288],{"class":122,"line":157},[120,1289,1290],{},"  FROM read_parquet('\u002Fdata\u002Fplaces.parquet')\n",[120,1292,1293],{"class":122,"line":172},[120,1294,425],{},[120,1296,1297],{"class":122,"line":187},[120,1298,430],{},[120,1300,1301],{"class":122,"line":198},[120,1302,1171],{},[120,1304,1305],{"class":122,"line":204},[120,1306,219],{},[10,1308,1309],{},"The bbox pre-filter enables Parquet row group pruning before geometry evaluation — essential for performance on large files.",[14,1311,1313],{"id":1312},"geometry-column-convention","Geometry Column Convention",[10,1315,1316,1317,492,1320,1323,1324,1327],{},"The query must return a column named ",[117,1318,1319],{},"geometry",[117,1321,1322],{},"geom",", or ",[117,1325,1326],{},"wkb_geometry"," containing WKB-encoded geometry blobs. tileserver-rs detects these column names and uses them for MVT encoding.",[10,1329,1330,1331,1334],{},"Use ",[117,1332,1333],{},"ST_AsWKB()"," to convert DuckDB geometry objects to WKB:",[110,1336,1338],{"className":744,"code":1337,"language":746,"meta":115,"style":115},"SELECT ST_AsWKB(geometry) as geometry FROM ...\n",[117,1339,1340],{"__ignoreMap":115},[120,1341,1342],{"class":122,"line":123},[120,1343,1337],{},[14,1345,1347],{"id":1346},"mlt-transcoding","MLT Transcoding",[10,1349,1350,1351,1354],{},"DuckDB sources output MVT tiles. If a client requests ",[117,1352,1353],{},".mlt"," format, the existing MLT transcoding layer converts MVT→MLT on-the-fly:",[110,1356,1361],{"className":1357,"code":1359,"language":1360},[1358],"language-text","GET \u002Fdata\u002Fplaces\u002F14\u002F8192\u002F5461.mlt  →  SQL query → MVT → MLT → response\n","text",[117,1362,1359],{"__ignoreMap":115},[14,1364,1366],{"id":1365},"performance","Performance",[29,1368,1369,1382],{},[32,1370,1371],{},[35,1372,1373,1376,1379],{},[38,1374,1375],{},"Scenario",[38,1377,1378],{},"Query Time",[38,1380,1381],{},"Notes",[47,1383,1384,1395,1406,1417,1428],{},[35,1385,1386,1389,1392],{},[52,1387,1388],{},"Simple bbox filter, 10K features",[52,1390,1391],{},"~10–50ms",[52,1393,1394],{},"With spatial extension",[35,1396,1397,1400,1403],{},[52,1398,1399],{},"Aggregate query (count\u002Fsum)",[52,1401,1402],{},"~20–100ms",[52,1404,1405],{},"DuckDB excels at analytics",[35,1407,1408,1411,1414],{},[52,1409,1410],{},"Multi-table join",[52,1412,1413],{},"~30–150ms",[52,1415,1416],{},"Depends on join selectivity",[35,1418,1419,1422,1425],{},[52,1420,1421],{},"Remote Parquet over S3",[52,1423,1424],{},"~200–500ms",[52,1426,1427],{},"Network latency dominant",[35,1429,1430,1433,1436],{},[52,1431,1432],{},"GeoParquet with bbox pre-filter",[52,1434,1435],{},"~50–200ms",[52,1437,1438],{},"Row group pruning",[237,1440,1442],{"id":1441},"tips","Tips",[1444,1445,1446,1452,1459,1464,1470],"ul",{},[1447,1448,1330,1449,1451],"li",{},[117,1450,1333],{}," for the geometry output — DuckDB's native GEOMETRY type isn't WKB by default",[1447,1453,1454,1455,1458],{},"Combine bbox column pre-filter + ",[117,1456,1457],{},"ST_Intersects"," for best performance on GeoParquet",[1447,1460,1330,1461,1463],{},[117,1462,495],{}," for zoom-dependent simplification and feature filtering",[1447,1465,1466,1467,1469],{},"For large datasets, create a ",[117,1468,456],{}," file with spatial indexes rather than querying raw Parquet per request",[1447,1471,1472,1473,1476],{},"DuckDB operations run in ",[117,1474,1475],{},"spawn_blocking"," — they don't block the async runtime",[1478,1479,1480],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":115,"searchDepth":137,"depth":137,"links":1482},[1483,1484,1485,1490,1491,1497,1507,1508,1509],{"id":16,"depth":137,"text":17},{"id":107,"depth":137,"text":108},{"id":235,"depth":137,"text":65,"children":1486},[1487,1488,1489],{"id":239,"depth":157,"text":240},{"id":354,"depth":157,"text":355},{"id":460,"depth":157,"text":461},{"id":568,"depth":137,"text":569},{"id":766,"depth":137,"text":767,"children":1492},[1493,1494,1495,1496],{"id":773,"depth":157,"text":774},{"id":797,"depth":157,"text":798},{"id":821,"depth":157,"text":822},{"id":834,"depth":157,"text":835},{"id":868,"depth":137,"text":869,"children":1498},[1499,1500,1501,1502,1503,1504,1505,1506],{"id":872,"depth":157,"text":873},{"id":902,"depth":157,"text":903},{"id":977,"depth":157,"text":978},{"id":1015,"depth":157,"text":1016},{"id":1060,"depth":157,"text":1061},{"id":1182,"depth":157,"text":1183},{"id":1215,"depth":157,"text":1216},{"id":1268,"depth":157,"text":1269},{"id":1312,"depth":137,"text":1313},{"id":1346,"depth":137,"text":1347},{"id":1365,"depth":137,"text":1366,"children":1510},[1511],{"id":1441,"depth":157,"text":1442},"Generate vector tiles from SQL queries with embedded DuckDB","md",{},"\u002Fguides\u002Fduckdb",{"title":5,"description":1512},"4.guides\u002F13.duckdb","UNuKODp7By1Q4ar2iAlPuIgNqPujPuksa1HrnL-Y7UI",1793056239283]