Query files in Google Drive directly with a gdrive:// filesystem
Installing and Loading
INSTALL gdrive FROM community;
LOAD gdrive;
Example
-- Unattended: a service account. To WRITE you need a Shared Drive --
-- service accounts have no personal Drive storage quota and get
-- 403 storageQuotaExceeded anywhere else. Reading a shared folder is fine.
--
-- DRIVE_ID is a SHARED DRIVE id; ROOT_FOLDER_ID is a FOLDER id. They are
-- not interchangeable: passing a folder id as DRIVE_ID fails with
-- "Shared drive not found". Use whichever matches what was shared.
CREATE SECRET gdrive (
TYPE gdrive,
PROVIDER service_account,
KEY_FILE '/etc/creds/sa.json',
DRIVE_ID '0ABcDeFgHiJkLmNoPQ', -- or ROOT_FOLDER_ID '1a2b3c...'
DRIVE_SCOPE 'https://www.googleapis.com/auth/drive.readonly'
);
-- Query a file by path. Drive has no path addressing, so each segment
-- costs a lookup; results are cached per secret.
SELECT count(*) FROM 'gdrive://Finance/2026/actuals.parquet';
-- Address a file by id instead: zero resolution calls. Prefer this in
-- generated or stored queries.
SELECT * FROM read_csv('gdrive://id:1a2b3cDeFgHiJkLmNoPQrStUvWxYz');
-- Glob a folder.
SELECT * FROM read_parquet('gdrive://exports/parts/*.parquet');
-- A native Google Sheet is queryable with no manual export: it has no
-- byte stream, so it is served through Drive's CSV export.
SELECT * FROM read_csv('gdrive://Finance/Budget');
-- Write a whole file. Writes are sequential; Drive cannot write at a
-- byte offset, so positional writes raise an error rather than corrupt.
COPY (SELECT * FROM t) TO 'gdrive://reports/out.parquet' (FORMAT parquet);
-- Inspect API calls and cache hits for the session.
SELECT * FROM gdrive_stats();
About gdrive
Registers a gdrive:// filesystem, so any DuckDB path expression can
address a file in Google Drive with no download or copy step. Because
DuckDB dispatches file access through its virtual filesystem, everything
layered on it inherits the scheme: read_parquet, read_csv, COPY,
glob, and table formats such as DuckLake — attaching a DuckLake whose
DATA_PATH is on Drive works and is tested live.
ATTACH of a DuckDB database file on gdrive:// is NOT supported.
DuckDB opens database files through a different path that does not consult
the virtual filesystem, so the attempt fails immediately rather than
half-working.
This is Google Drive, not Google Cloud Storage — a different product
with a different API and access model. For gs:// buckets use the gcs
extension.
Reads are ranged, so a Parquet scan fetches footers and column chunks rather than whole files. Path resolution is cached per secret and per drive, because Drive has no path addressing and each segment otherwise costs an API call.
Two behaviours are deliberately strict. Drive permits two files with the same name in one folder, so a path is not a unique identifier: rather than pick one and make results depend on Drive's internal ordering, ambiguity is an error naming both file ids. And Drive cannot write at a byte offset, so positional writes raise an error instead of silently corrupting a file.
Drive is slower than object storage by a wide margin, and enforces per-user API quotas that object storage does not. A ranged read costs ~1.3 s regardless of how few bytes it requests, against ~1 ms for S3/GCS. Use this when Drive is the system of record for data you want to query and you would rather not maintain a second copy — not as a substitute for object storage under a hot workload. On a workstation, Google Drive for desktop plus ordinary local paths is simpler and faster.
Added Functions
| function_name | function_type | description | comment | examples |
|---|---|---|---|---|
| gdrive_reset_stats | table | Zero the process-wide Drive API call counters reported by gdrive_stats(). Use it to measure exactly what ONE operation costs: reset, run the query, then read gdrive_stats(). Affects the whole process, so it will disturb a concurrent measurement in another connection. | NULL | [CALL gdrive_reset_stats()] |
| gdrive_stats | table | Drive API call counters, one row per metric: files_get, files_list, files_media, files_export, files_create, files_update, files_delete (calls by kind), cache_hits/cache_misses (the path-resolution cache that mitigates R-1 amplification), retries (retried HTTP attempts across all kinds), and total (sum of the files_* kind counters), and path_cache_entries (a GAUGE: the live size of the path->id cache, bounded by gdrive_path_cache_entries). Process-wide, not reset between queries – call it before and after an operation and diff the two snapshots to measure that operation's amplification. | NULL | [SELECT * FROM gdrive_stats()] |
| gdrive_version | scalar | NULL | NULL |
Overloaded Functions
This extension does not add any function overloads.
Added Types
This extension does not add any types.
Added Settings
| name | description | input_type | scope | aliases |
|---|---|---|---|---|
| gdrive_adc_file | Path to an Application Default Credentials JSON file, overriding normal discovery for PROVIDER credential_chain. Empty (default) resolves GOOGLE_APPLICATION_CREDENTIALS, then CLOUDSDK_CONFIG, then the well-known gcloud location. | VARCHAR | GLOBAL | [] |
| gdrive_block_cache_bytes | Total memory the shared block cache may hold (default 256 MiB). Least-recently-used blocks are evicted above this. Shared by all files and all threads. | UBIGINT | GLOBAL | [] |
| gdrive_block_size_bytes | Block size for cached reads (default 16 MiB, 0 to disable and read exact ranges). Drive charges roughly the same for a 1 KB and a 16 MB request, so larger blocks trade bandwidth for far fewer round trips. | UBIGINT | GLOBAL | [] |
| gdrive_docs_export_mime | MIME type to export application/vnd.google-apps.document files as: 'text/plain' (default) or 'text/markdown'. Sheets always export to text/csv. | VARCHAR | GLOBAL | [] |
| gdrive_immutable_prefixes | Comma-separated gdrive:// path prefixes whose files are never overwritten in place (e.g. a DuckLake DATA_PATH). Skips the per-open metadata refresh for them. Matching is on whole path segments. Empty (the default) disables it. Declaring a prefix whose files ARE rewritten in place causes stale reads that cannot be detected. | VARCHAR | GLOBAL | [] |
| gdrive_path_cache_entries | Maximum path->file-id mappings to cache (default 4096, 0 for unbounded). Least-recently-used entries are dropped above this; a dropped mapping just costs one files.list per segment to rebuild. | UBIGINT | GLOBAL | [] |
| gdrive_permanent_delete | RemoveFile permanently deletes instead of moving to trash (default: false, trash). | BOOLEAN | GLOBAL | [] |