@cereusdb/minimal 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -2
- package/dist/external.d.ts +199 -4
- package/dist/external.js +219 -4
- package/dist/index.d.ts +199 -4
- package/dist/index.js +219 -4
- package/dist/storage.d.ts +64 -0
- package/dist/storage.js +211 -0
- package/dist/wasm/cereusdb-external.d.ts +68 -1
- package/dist/wasm/cereusdb-external.js +359 -3
- package/dist/wasm/cereusdb.d.ts +68 -1
- package/dist/wasm/cereusdb.js +359 -3
- package/dist/wasm/cereusdb_bg.wasm +0 -0
- package/dist/wasm/cereusdb_bg.wasm.d.ts +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,6 +37,30 @@ Not included in this package:
|
|
|
37
37
|
|
|
38
38
|
Browser object stores are not included in `@cereusdb/minimal`. Use `@cereusdb/standard`, `@cereusdb/global`, or `@cereusdb/full` when you need `registerObjectStores()` and `registerParquetTable()`.
|
|
39
39
|
|
|
40
|
+
## Persistent databases (OPFS)
|
|
41
|
+
|
|
42
|
+
Tables normally live in memory. Persistent databases are stored in the browser's Origin Private File System and survive page reloads:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const db = await CereusDB.create({ attach: ['opfs://mydb'] }); // opens or creates mydb
|
|
46
|
+
|
|
47
|
+
await db.sqlJSON(`CREATE TABLE mydb.public.cities AS SELECT 1 AS id, 'Berlin' AS name, ST_Point(13.4, 52.5) AS geom`);
|
|
48
|
+
await db.sqlJSON(`INSERT INTO mydb.public.cities VALUES (2, 'Paris', ST_Point(2.35, 48.86))`);
|
|
49
|
+
await db.sqlJSON(`USE mydb`); // `cities` now resolves to mydb.public.cities
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
SQL: `CREATE DATABASE 'opfs://mydb'`, `ATTACH 'opfs://mydb' [AS name]`, `DETACH name`, `DROP DATABASE name`, `USE name`, `ALTER TABLE ... RENAME TO | ADD COLUMN | DROP COLUMN | RENAME COLUMN`, and `SELECT * FROM cereusdb_databases()`. Databases appear as catalogs in `information_schema` and `db.catalog()`. A table's data is loaded the first time it is used. See the [persistent databases guide](https://github.com/tobilg/cereusdb/blob/main/packages/documentation/guides/persistent-databases.md).
|
|
53
|
+
|
|
54
|
+
## GeoParquet export
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const bytes = await db.exportGeoParquet('mydb.public.cities'); // Uint8Array
|
|
58
|
+
await db.downloadGeoParquet('mydb.public.cities'); // downloads cities.parquet
|
|
59
|
+
await db.sqlJSON(`COPY (SELECT * FROM cities WHERE id > 1) TO 'some.parquet'`);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
See the [GeoParquet export guide](https://github.com/tobilg/cereusdb/blob/main/packages/documentation/guides/geoparquet-export.md).
|
|
63
|
+
|
|
40
64
|
## Loading the WASM module
|
|
41
65
|
|
|
42
66
|
The wasm binary ships as a separate file, `dist/wasm/cereusdb_bg.wasm`. The default entry finds it automatically, and bundlers emit it as an asset.
|
|
@@ -63,10 +87,49 @@ Exports:
|
|
|
63
87
|
- `RegisterParquetTableOptions`
|
|
64
88
|
- `RasterFormat`
|
|
65
89
|
- `QueryResult`
|
|
90
|
+
- `MemoryStorageBackend`, `OPFSStorageBackend`, `StorageBackend`, `StorageOptions`
|
|
91
|
+
- `CreateDatabaseOptions`, `AttachDatabaseOptions`, `DatabaseListing`
|
|
92
|
+
- `CatalogDatabase`, `CatalogSchema`, `CatalogTable`, `CatalogColumn`
|
|
93
|
+
- `CreateTableDefinition`, `CreateTableOptions`, `AlterTableOperation`
|
|
94
|
+
- `GeoParquetExportOptions`, `DownloadGeoParquetOptions`, `ExportHandler`
|
|
95
|
+
- `downloadFile`, `PARQUET_MIME_TYPE`
|
|
66
96
|
|
|
67
97
|
Main types:
|
|
68
98
|
|
|
69
99
|
```ts
|
|
100
|
+
interface StorageOptions {
|
|
101
|
+
opfs?: StorageBackend | false; // default: OPFSStorageBackend when the browser has OPFS
|
|
102
|
+
[scheme: string]: StorageBackend | false | undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface StorageBackend {
|
|
106
|
+
readFile(path: string): Promise<Uint8Array | null>;
|
|
107
|
+
writeFile(path: string, data: Uint8Array): Promise<void>;
|
|
108
|
+
remove(path: string): Promise<void>;
|
|
109
|
+
list(path: string): Promise<string[]>;
|
|
110
|
+
lock?(name: string): Promise<void>;
|
|
111
|
+
unlock?(name: string): Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
type ExportHandler = (filename: string, data: Uint8Array, mimeType: string) => void | Promise<void>;
|
|
115
|
+
|
|
116
|
+
type CreateTableDefinition = { columns: Record<string, string> } | { as: string };
|
|
117
|
+
|
|
118
|
+
type AlterTableOperation =
|
|
119
|
+
| { renameTo: string }
|
|
120
|
+
| { addColumn: { name: string; type: string; notNull?: boolean; default?: string }; ifNotExists?: boolean }
|
|
121
|
+
| { dropColumn: string | string[]; ifExists?: boolean }
|
|
122
|
+
| { renameColumn: { from: string; to: string } };
|
|
123
|
+
|
|
124
|
+
interface GeoParquetExportOptions {
|
|
125
|
+
compression?: 'zstd' | 'snappy' | 'lz4' | 'gzip' | 'brotli' | 'uncompressed';
|
|
126
|
+
rowGroupSize?: number;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface DownloadGeoParquetOptions extends GeoParquetExportOptions {
|
|
130
|
+
filename?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
70
133
|
type RasterFormat = 'geotiff' | 'tiff';
|
|
71
134
|
type ObjectStoreProvider = 'http' | 's3' | 'gcs' | 'azure';
|
|
72
135
|
|
|
@@ -80,6 +143,9 @@ interface CereusDBOptions {
|
|
|
80
143
|
| WebAssembly.Module
|
|
81
144
|
| Promise<Response>;
|
|
82
145
|
objectStores?: ObjectStoreRegistryConfig;
|
|
146
|
+
storage?: StorageOptions;
|
|
147
|
+
attach?: string[];
|
|
148
|
+
onExport?: ExportHandler | false;
|
|
83
149
|
}
|
|
84
150
|
|
|
85
151
|
interface ObjectStoreRegistryConfig {
|
|
@@ -118,8 +184,22 @@ class CereusDB {
|
|
|
118
184
|
registerGeoJSON(name: string, geojson: string | object): void;
|
|
119
185
|
registerRaster(name: string, data: BufferSource, format: RasterFormat): void;
|
|
120
186
|
registerGeoTIFF(name: string, data: BufferSource): void;
|
|
121
|
-
dropTable(name: string): void
|
|
122
|
-
|
|
187
|
+
dropTable(name: string): Promise<void>;
|
|
188
|
+
catalog(): Promise<CatalogDatabase[]>;
|
|
189
|
+
createDatabase(location: string, options?: CreateDatabaseOptions): Promise<string>;
|
|
190
|
+
attachDatabase(location: string, options?: AttachDatabaseOptions): Promise<string>;
|
|
191
|
+
detachDatabase(name: string, options?: { ifExists?: boolean }): Promise<void>;
|
|
192
|
+
dropDatabase(nameOrLocation: string, options?: { ifExists?: boolean }): Promise<void>;
|
|
193
|
+
useDatabase(database: string, schema?: string): Promise<void>;
|
|
194
|
+
listDatabases(): Promise<DatabaseListing[]>;
|
|
195
|
+
createTable(name: string, definition: CreateTableDefinition, options?: CreateTableOptions): Promise<void>;
|
|
196
|
+
alterTable(name: string, operation: AlterTableOperation): Promise<void>;
|
|
197
|
+
insertArrow(table: string, data: BufferSource): Promise<number>;
|
|
198
|
+
compactDatabase(name: string): Promise<void>;
|
|
199
|
+
flush(): Promise<void>;
|
|
200
|
+
exportGeoParquet(queryOrTable: string, options?: GeoParquetExportOptions): Promise<Uint8Array>;
|
|
201
|
+
downloadGeoParquet(queryOrTable: string, options?: DownloadGeoParquetOptions): Promise<string>;
|
|
202
|
+
tables(options?: { qualified?: boolean }): string[];
|
|
123
203
|
version(): string;
|
|
124
204
|
}
|
|
125
205
|
```
|
|
@@ -131,6 +211,8 @@ API notes:
|
|
|
131
211
|
- `registerFile()` supports `.parquet`, `.geoparquet`, `.geojson`, and `.json` in this package.
|
|
132
212
|
- Browser object-store methods are part of the shared wrapper, but object-store registration requires `@cereusdb/standard`, `@cereusdb/global`, or `@cereusdb/full`.
|
|
133
213
|
- `registerRaster()` and `registerGeoTIFF()` are part of the shared wrapper, but raster registration requires `@cereusdb/full`.
|
|
214
|
+
- Persistent databases (`createDatabase()` and related methods), `ALTER TABLE`, `insertArrow()`, `catalog()` and GeoParquet export are available in every package.
|
|
215
|
+
- `downloadGeoParquet()` and `COPY ... TO` pass the file to the `onExport` handler: a browser download by default on the main thread; pass your own handler in Web Workers or Node.js. Exporting a CRS other than OGC:CRS84 needs PROJ, which this package does not include.
|
|
134
216
|
|
|
135
217
|
## Example
|
|
136
218
|
|
package/dist/external.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type StorageBackend } from './storage.js';
|
|
2
|
+
export { MemoryStorageBackend, OPFSStorageBackend, type OPFSStorageBackendOptions, type StorageBackend, } from './storage.js';
|
|
1
3
|
export interface QueryResult {
|
|
2
4
|
/** Raw JSON data parsed from query */
|
|
3
5
|
data: Record<string, unknown>[];
|
|
@@ -15,7 +17,127 @@ export interface CereusDBOptions {
|
|
|
15
17
|
wasmSource?: RequestInfo | URL | Response | BufferSource | WebAssembly.Module | Promise<Response>;
|
|
16
18
|
/** Browser-backed object stores to register at startup. */
|
|
17
19
|
objectStores?: ObjectStoreRegistryConfig;
|
|
20
|
+
/**
|
|
21
|
+
* Storage backends for persistent databases, by URL scheme. `opfs` defaults
|
|
22
|
+
* to an {@link OPFSStorageBackend} when the browser supports OPFS; pass
|
|
23
|
+
* `false` to disable it.
|
|
24
|
+
*/
|
|
25
|
+
storage?: StorageOptions;
|
|
26
|
+
/**
|
|
27
|
+
* Persistent databases to open at startup, for example `['opfs://mydb']`.
|
|
28
|
+
* Each is created if it does not exist yet.
|
|
29
|
+
*/
|
|
30
|
+
attach?: string[];
|
|
31
|
+
/**
|
|
32
|
+
* Receives files written by `COPY ... TO '<file>'` and
|
|
33
|
+
* {@link CereusDB.downloadGeoParquet}. Defaults to {@link downloadFile}
|
|
34
|
+
* (a browser download) when a DOM `document` is available; pass `false` to
|
|
35
|
+
* disable exports or a function to handle them yourself (for example in a
|
|
36
|
+
* Web Worker or in Node.js).
|
|
37
|
+
*/
|
|
38
|
+
onExport?: ExportHandler | false;
|
|
39
|
+
}
|
|
40
|
+
/** Receives an exported file. */
|
|
41
|
+
export type ExportHandler = (filename: string, data: Uint8Array, mimeType: string) => void | Promise<void>;
|
|
42
|
+
/** MIME type of GeoParquet exports. */
|
|
43
|
+
export declare const PARQUET_MIME_TYPE = "application/vnd.apache.parquet";
|
|
44
|
+
/**
|
|
45
|
+
* Save a file with the browser's download mechanism. Needs a DOM `document`
|
|
46
|
+
* (the main thread); it does not work in Web Workers or Node.js.
|
|
47
|
+
*/
|
|
48
|
+
export declare function downloadFile(filename: string, data: Uint8Array, mimeType?: string): void;
|
|
49
|
+
export interface StorageOptions {
|
|
50
|
+
opfs?: StorageBackend | false;
|
|
51
|
+
[scheme: string]: StorageBackend | false | undefined;
|
|
52
|
+
}
|
|
53
|
+
export interface CreateDatabaseOptions {
|
|
54
|
+
/** Database (catalog) name. Defaults to the lowercased name in the location URL. */
|
|
55
|
+
name?: string;
|
|
56
|
+
/** Open the database if it already exists instead of failing. */
|
|
57
|
+
ifNotExists?: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface AttachDatabaseOptions {
|
|
60
|
+
/** Database (catalog) name. Defaults to the lowercased name in the location URL. */
|
|
61
|
+
name?: string;
|
|
62
|
+
/** Succeed if the database is already attached under this name. */
|
|
63
|
+
ifNotExists?: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface DatabaseListing {
|
|
66
|
+
/** Catalog name, or `null` for stored databases that are not attached. */
|
|
67
|
+
name: string | null;
|
|
68
|
+
/** `memory` or the storage scheme, for example `opfs`. */
|
|
69
|
+
storage: string;
|
|
70
|
+
/** Location URL for persistent databases. */
|
|
71
|
+
location: string | null;
|
|
72
|
+
attached: boolean;
|
|
73
|
+
}
|
|
74
|
+
export interface CatalogColumn {
|
|
75
|
+
name: string;
|
|
76
|
+
/** Arrow data type. */
|
|
77
|
+
type: string;
|
|
78
|
+
nullable: boolean;
|
|
79
|
+
/** Arrow extension type, for example `geoarrow.wkb` for geometry columns. */
|
|
80
|
+
extension?: string;
|
|
81
|
+
}
|
|
82
|
+
export interface CatalogTable {
|
|
83
|
+
name: string;
|
|
84
|
+
type: 'BASE TABLE' | 'VIEW' | 'LOCAL TEMPORARY';
|
|
85
|
+
columns: CatalogColumn[];
|
|
86
|
+
}
|
|
87
|
+
export interface CatalogSchema {
|
|
88
|
+
name: string;
|
|
89
|
+
tables: CatalogTable[];
|
|
90
|
+
}
|
|
91
|
+
export interface CatalogDatabase {
|
|
92
|
+
name: string;
|
|
93
|
+
storage: string;
|
|
94
|
+
location: string | null;
|
|
95
|
+
schemas: CatalogSchema[];
|
|
96
|
+
}
|
|
97
|
+
/** Columns as `{ name: 'SQL type' }`, or a query for CREATE TABLE ... AS. */
|
|
98
|
+
export type CreateTableDefinition = {
|
|
99
|
+
columns: Record<string, string>;
|
|
100
|
+
} | {
|
|
101
|
+
as: string;
|
|
102
|
+
};
|
|
103
|
+
export interface GeoParquetExportOptions {
|
|
104
|
+
/** Parquet compression codec. Defaults to `zstd`. */
|
|
105
|
+
compression?: 'zstd' | 'snappy' | 'lz4' | 'gzip' | 'brotli' | 'uncompressed';
|
|
106
|
+
/** Maximum number of rows per row group. */
|
|
107
|
+
rowGroupSize?: number;
|
|
108
|
+
}
|
|
109
|
+
export interface DownloadGeoParquetOptions extends GeoParquetExportOptions {
|
|
110
|
+
/**
|
|
111
|
+
* File name. Defaults to `<table>.parquet` for a table and
|
|
112
|
+
* `export.parquet` for a query.
|
|
113
|
+
*/
|
|
114
|
+
filename?: string;
|
|
18
115
|
}
|
|
116
|
+
export interface CreateTableOptions {
|
|
117
|
+
ifNotExists?: boolean;
|
|
118
|
+
orReplace?: boolean;
|
|
119
|
+
}
|
|
120
|
+
export type AlterTableOperation = {
|
|
121
|
+
renameTo: string;
|
|
122
|
+
} | {
|
|
123
|
+
addColumn: {
|
|
124
|
+
name: string;
|
|
125
|
+
/** SQL type, for example `VARCHAR` or `DOUBLE`. */
|
|
126
|
+
type: string;
|
|
127
|
+
notNull?: boolean;
|
|
128
|
+
/** SQL expression used for existing rows and as column default. */
|
|
129
|
+
default?: string;
|
|
130
|
+
};
|
|
131
|
+
ifNotExists?: boolean;
|
|
132
|
+
} | {
|
|
133
|
+
dropColumn: string | string[];
|
|
134
|
+
ifExists?: boolean;
|
|
135
|
+
} | {
|
|
136
|
+
renameColumn: {
|
|
137
|
+
from: string;
|
|
138
|
+
to: string;
|
|
139
|
+
};
|
|
140
|
+
};
|
|
19
141
|
export type RasterFormat = 'geotiff' | 'tiff';
|
|
20
142
|
export type ObjectStoreProvider = 'http' | 's3' | 'gcs' | 'azure';
|
|
21
143
|
export interface ObjectStoreRegistryConfig {
|
|
@@ -42,6 +164,8 @@ export interface RegisterParquetTableOptions {
|
|
|
42
164
|
}
|
|
43
165
|
export declare class CereusDB {
|
|
44
166
|
private inner;
|
|
167
|
+
private storageBackends;
|
|
168
|
+
private exportHandler?;
|
|
45
169
|
private constructor();
|
|
46
170
|
/**
|
|
47
171
|
* Create and initialize a new CereusDB instance.
|
|
@@ -88,10 +212,81 @@ export declare class CereusDB {
|
|
|
88
212
|
* Requires the full GDAL-enabled package build.
|
|
89
213
|
*/
|
|
90
214
|
registerGeoTIFF(name: string, data: BufferSource): void;
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Drop a table. The table is removed immediately; for tables of persistent
|
|
217
|
+
* databases the returned promise resolves once the change is written.
|
|
218
|
+
*/
|
|
219
|
+
dropTable(name: string): Promise<void>;
|
|
220
|
+
/**
|
|
221
|
+
* Create a persistent database, for example `opfs://mydb`, and attach it
|
|
222
|
+
* as a catalog. Equivalent to `CREATE DATABASE 'opfs://mydb'`.
|
|
223
|
+
* Resolves to the database name.
|
|
224
|
+
*/
|
|
225
|
+
createDatabase(location: string, options?: CreateDatabaseOptions): Promise<string>;
|
|
226
|
+
/**
|
|
227
|
+
* Open an existing persistent database. Equivalent to
|
|
228
|
+
* `ATTACH 'opfs://mydb' [AS name]`. Resolves to the database name.
|
|
229
|
+
*/
|
|
230
|
+
attachDatabase(location: string, options?: AttachDatabaseOptions): Promise<string>;
|
|
231
|
+
/** Write pending changes and close a persistent database. Equivalent to `DETACH name`. */
|
|
232
|
+
detachDatabase(name: string, options?: {
|
|
233
|
+
ifExists?: boolean;
|
|
234
|
+
}): Promise<void>;
|
|
235
|
+
/**
|
|
236
|
+
* Drop a database by name or location. Persistent databases are deleted
|
|
237
|
+
* from storage. Equivalent to `DROP DATABASE`.
|
|
238
|
+
*/
|
|
239
|
+
dropDatabase(nameOrLocation: string, options?: {
|
|
240
|
+
ifExists?: boolean;
|
|
241
|
+
}): Promise<void>;
|
|
242
|
+
/** Set the default database (and schema). Equivalent to `USE database[.schema]`. */
|
|
243
|
+
useDatabase(database: string, schema?: string): Promise<void>;
|
|
244
|
+
/** Attached databases plus stored databases that are not attached. */
|
|
245
|
+
listDatabases(): Promise<DatabaseListing[]>;
|
|
246
|
+
/** Describe all databases, schemas, tables and views. */
|
|
247
|
+
catalog(): Promise<CatalogDatabase[]>;
|
|
248
|
+
/**
|
|
249
|
+
* Create a table from column definitions or a query. `name` may be
|
|
250
|
+
* qualified (`mydb.public.cities`).
|
|
251
|
+
*/
|
|
252
|
+
createTable(name: string, definition: CreateTableDefinition, options?: CreateTableOptions): Promise<void>;
|
|
253
|
+
/** Rename a table or add, drop, or rename a column. */
|
|
254
|
+
alterTable(name: string, operation: AlterTableOperation): Promise<void>;
|
|
255
|
+
/**
|
|
256
|
+
* Insert Arrow IPC data (stream or file format, for example from
|
|
257
|
+
* `tableToIPC()` of apache-arrow) into a table. Columns are matched by
|
|
258
|
+
* name; missing columns are filled with NULL. Resolves to the number of
|
|
259
|
+
* inserted rows.
|
|
260
|
+
*/
|
|
261
|
+
insertArrow(table: string, data: BufferSource): Promise<number>;
|
|
262
|
+
/**
|
|
263
|
+
* Export a table or query result as a GeoParquet (1.1) file. Geometry and
|
|
264
|
+
* geography columns are described in the file's `geo` metadata.
|
|
265
|
+
*
|
|
266
|
+
* @param queryOrTable A query (`SELECT ...`, `WITH ...`) or a table name.
|
|
267
|
+
*/
|
|
268
|
+
exportGeoParquet(queryOrTable: string, options?: GeoParquetExportOptions): Promise<Uint8Array>;
|
|
269
|
+
/**
|
|
270
|
+
* Export a table or query result as GeoParquet and pass it to the export
|
|
271
|
+
* handler: a browser download by default (see `onExport` in
|
|
272
|
+
* {@link CereusDBOptions}). Resolves to the file name.
|
|
273
|
+
*
|
|
274
|
+
* @param queryOrTable A query (`SELECT ...`, `WITH ...`) or a table name.
|
|
275
|
+
*/
|
|
276
|
+
downloadGeoParquet(queryOrTable: string, options?: DownloadGeoParquetOptions): Promise<string>;
|
|
277
|
+
/** Rewrite all tables of a persistent database into one segment each. */
|
|
278
|
+
compactDatabase(name: string): Promise<void>;
|
|
279
|
+
/** Write pending changes of persistent databases (for example after registerGeoJSON()). */
|
|
280
|
+
flush(): Promise<void>;
|
|
281
|
+
/**
|
|
282
|
+
* List the tables and views of all databases. By default only the table
|
|
283
|
+
* names are returned, which is ambiguous when databases contain tables with
|
|
284
|
+
* the same name; pass `{ qualified: true }` for `database.schema.table`
|
|
285
|
+
* names, or use {@link CereusDB.catalog} for full details.
|
|
286
|
+
*/
|
|
287
|
+
tables(options?: {
|
|
288
|
+
qualified?: boolean;
|
|
289
|
+
}): string[];
|
|
95
290
|
/** Version string. */
|
|
96
291
|
version(): string;
|
|
97
292
|
private objectStoreApi;
|
package/dist/external.js
CHANGED
|
@@ -1,4 +1,80 @@
|
|
|
1
1
|
import init, { CereusDB as WasmCereusDB } from './wasm/cereusdb-external.js';
|
|
2
|
+
import { OPFSStorageBackend } from './storage.js';
|
|
3
|
+
export { MemoryStorageBackend, OPFSStorageBackend, } from './storage.js';
|
|
4
|
+
/** MIME type of GeoParquet exports. */
|
|
5
|
+
export const PARQUET_MIME_TYPE = 'application/vnd.apache.parquet';
|
|
6
|
+
/**
|
|
7
|
+
* Save a file with the browser's download mechanism. Needs a DOM `document`
|
|
8
|
+
* (the main thread); it does not work in Web Workers or Node.js.
|
|
9
|
+
*/
|
|
10
|
+
export function downloadFile(filename, data, mimeType = 'application/octet-stream') {
|
|
11
|
+
if (typeof document === 'undefined') {
|
|
12
|
+
throw new Error('downloadFile() needs a browser document; pass CereusDB.create({ onExport }) to handle exports elsewhere');
|
|
13
|
+
}
|
|
14
|
+
const url = URL.createObjectURL(new Blob([data], { type: mimeType }));
|
|
15
|
+
const link = document.createElement('a');
|
|
16
|
+
link.href = url;
|
|
17
|
+
link.download = filename;
|
|
18
|
+
link.style.display = 'none';
|
|
19
|
+
document.body.appendChild(link);
|
|
20
|
+
try {
|
|
21
|
+
link.click();
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
link.remove();
|
|
25
|
+
// Revoking right away can cancel the download in some browsers.
|
|
26
|
+
setTimeout(() => URL.revokeObjectURL(url), 60000);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const QUERY_PATTERN = /^\s*(select|with|values|\()/i;
|
|
30
|
+
function resolveExportHandler(option) {
|
|
31
|
+
if (option === false) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
if (option !== undefined) {
|
|
35
|
+
return option;
|
|
36
|
+
}
|
|
37
|
+
return typeof document !== 'undefined' ? downloadFile : undefined;
|
|
38
|
+
}
|
|
39
|
+
function defaultExportFilename(queryOrTable) {
|
|
40
|
+
if (QUERY_PATTERN.test(queryOrTable)) {
|
|
41
|
+
return 'export.parquet';
|
|
42
|
+
}
|
|
43
|
+
const name = queryOrTable.trim().split('.').pop()?.replace(/^"|"$/g, '');
|
|
44
|
+
return `${name || 'export'}.parquet`;
|
|
45
|
+
}
|
|
46
|
+
function resolveStorageBackends(options) {
|
|
47
|
+
const backends = {};
|
|
48
|
+
for (const [scheme, backend] of Object.entries(options ?? {})) {
|
|
49
|
+
if (backend) {
|
|
50
|
+
backends[scheme.toLowerCase()] = backend;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (options?.opfs === undefined && OPFSStorageBackend.isSupported()) {
|
|
54
|
+
backends.opfs = new OPFSStorageBackend();
|
|
55
|
+
}
|
|
56
|
+
return backends;
|
|
57
|
+
}
|
|
58
|
+
function alterTableClause(operation) {
|
|
59
|
+
if ('renameTo' in operation) {
|
|
60
|
+
return `RENAME TO ${operation.renameTo}`;
|
|
61
|
+
}
|
|
62
|
+
if ('addColumn' in operation) {
|
|
63
|
+
const column = operation.addColumn;
|
|
64
|
+
const notNull = column.notNull ? ' NOT NULL' : '';
|
|
65
|
+
const defaultValue = column.default !== undefined ? ` DEFAULT ${column.default}` : '';
|
|
66
|
+
const ifNotExists = operation.ifNotExists ? 'IF NOT EXISTS ' : '';
|
|
67
|
+
return `ADD COLUMN ${ifNotExists}${column.name} ${column.type}${notNull}${defaultValue}`;
|
|
68
|
+
}
|
|
69
|
+
if ('dropColumn' in operation) {
|
|
70
|
+
const columns = Array.isArray(operation.dropColumn)
|
|
71
|
+
? operation.dropColumn
|
|
72
|
+
: [operation.dropColumn];
|
|
73
|
+
const ifExists = operation.ifExists ? 'IF EXISTS ' : '';
|
|
74
|
+
return columns.map((column) => `DROP COLUMN ${ifExists}${column}`).join(', ');
|
|
75
|
+
}
|
|
76
|
+
return `RENAME COLUMN ${operation.renameColumn.from} TO ${operation.renameColumn.to}`;
|
|
77
|
+
}
|
|
2
78
|
function toUint8Array(data) {
|
|
3
79
|
if (ArrayBuffer.isView(data)) {
|
|
4
80
|
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
@@ -14,6 +90,7 @@ function normalizeRasterFormat(format) {
|
|
|
14
90
|
}
|
|
15
91
|
export class CereusDB {
|
|
16
92
|
constructor(inner) {
|
|
93
|
+
this.storageBackends = {};
|
|
17
94
|
this.inner = inner;
|
|
18
95
|
}
|
|
19
96
|
/**
|
|
@@ -33,6 +110,17 @@ export class CereusDB {
|
|
|
33
110
|
if (options?.objectStores !== undefined) {
|
|
34
111
|
db.registerObjectStores(options.objectStores);
|
|
35
112
|
}
|
|
113
|
+
db.exportHandler = resolveExportHandler(options?.onExport);
|
|
114
|
+
if (db.exportHandler !== undefined) {
|
|
115
|
+
inner.register_export_handler(db.exportHandler);
|
|
116
|
+
}
|
|
117
|
+
db.storageBackends = resolveStorageBackends(options?.storage);
|
|
118
|
+
for (const [scheme, backend] of Object.entries(db.storageBackends)) {
|
|
119
|
+
inner.register_storage_backend(scheme, backend);
|
|
120
|
+
}
|
|
121
|
+
for (const location of options?.attach ?? []) {
|
|
122
|
+
await db.createDatabase(location, { ifNotExists: true });
|
|
123
|
+
}
|
|
36
124
|
return db;
|
|
37
125
|
}
|
|
38
126
|
/**
|
|
@@ -109,13 +197,140 @@ export class CereusDB {
|
|
|
109
197
|
registerGeoTIFF(name, data) {
|
|
110
198
|
this.registerRaster(name, data, 'geotiff');
|
|
111
199
|
}
|
|
112
|
-
/**
|
|
200
|
+
/**
|
|
201
|
+
* Drop a table. The table is removed immediately; for tables of persistent
|
|
202
|
+
* databases the returned promise resolves once the change is written.
|
|
203
|
+
*/
|
|
113
204
|
dropTable(name) {
|
|
114
205
|
this.inner.drop_table(name);
|
|
206
|
+
return this.inner.flush();
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Create a persistent database, for example `opfs://mydb`, and attach it
|
|
210
|
+
* as a catalog. Equivalent to `CREATE DATABASE 'opfs://mydb'`.
|
|
211
|
+
* Resolves to the database name.
|
|
212
|
+
*/
|
|
213
|
+
async createDatabase(location, options = {}) {
|
|
214
|
+
return await this.inner.attach_database(location, options.name, true, options.ifNotExists ?? false);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Open an existing persistent database. Equivalent to
|
|
218
|
+
* `ATTACH 'opfs://mydb' [AS name]`. Resolves to the database name.
|
|
219
|
+
*/
|
|
220
|
+
async attachDatabase(location, options = {}) {
|
|
221
|
+
return await this.inner.attach_database(location, options.name, false, options.ifNotExists ?? false);
|
|
222
|
+
}
|
|
223
|
+
/** Write pending changes and close a persistent database. Equivalent to `DETACH name`. */
|
|
224
|
+
async detachDatabase(name, options = {}) {
|
|
225
|
+
await this.inner.detach_database(name, options.ifExists ?? false);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Drop a database by name or location. Persistent databases are deleted
|
|
229
|
+
* from storage. Equivalent to `DROP DATABASE`.
|
|
230
|
+
*/
|
|
231
|
+
async dropDatabase(nameOrLocation, options = {}) {
|
|
232
|
+
await this.inner.drop_database(nameOrLocation, options.ifExists ?? false);
|
|
233
|
+
}
|
|
234
|
+
/** Set the default database (and schema). Equivalent to `USE database[.schema]`. */
|
|
235
|
+
async useDatabase(database, schema) {
|
|
236
|
+
await this.inner.sql_json(schema === undefined ? `USE ${database}` : `USE ${database}.${schema}`);
|
|
237
|
+
}
|
|
238
|
+
/** Attached databases plus stored databases that are not attached. */
|
|
239
|
+
async listDatabases() {
|
|
240
|
+
const rows = (await this.sqlJSON('SELECT database_name, storage, location FROM cereusdb_databases()'));
|
|
241
|
+
const databases = rows.map((row) => ({
|
|
242
|
+
name: row.database_name,
|
|
243
|
+
storage: row.storage,
|
|
244
|
+
location: row.location ?? null,
|
|
245
|
+
attached: true,
|
|
246
|
+
}));
|
|
247
|
+
for (const scheme of Object.keys(this.storageBackends)) {
|
|
248
|
+
const locations = (await this.inner.list_database_locations(scheme));
|
|
249
|
+
for (const location of locations) {
|
|
250
|
+
if (!databases.some((database) => database.location === location)) {
|
|
251
|
+
databases.push({ name: null, storage: scheme, location, attached: false });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return databases;
|
|
256
|
+
}
|
|
257
|
+
/** Describe all databases, schemas, tables and views. */
|
|
258
|
+
async catalog() {
|
|
259
|
+
return JSON.parse(await this.inner.catalog_json());
|
|
115
260
|
}
|
|
116
|
-
/**
|
|
117
|
-
|
|
118
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Create a table from column definitions or a query. `name` may be
|
|
263
|
+
* qualified (`mydb.public.cities`).
|
|
264
|
+
*/
|
|
265
|
+
async createTable(name, definition, options = {}) {
|
|
266
|
+
const orReplace = options.orReplace ? 'OR REPLACE ' : '';
|
|
267
|
+
const ifNotExists = options.ifNotExists ? 'IF NOT EXISTS ' : '';
|
|
268
|
+
const body = 'as' in definition
|
|
269
|
+
? `AS ${definition.as}`
|
|
270
|
+
: `(${Object.entries(definition.columns)
|
|
271
|
+
.map(([column, type]) => `${column} ${type}`)
|
|
272
|
+
.join(', ')})`;
|
|
273
|
+
await this.inner.sql_json(`CREATE ${orReplace}TABLE ${ifNotExists}${name} ${body}`);
|
|
274
|
+
}
|
|
275
|
+
/** Rename a table or add, drop, or rename a column. */
|
|
276
|
+
async alterTable(name, operation) {
|
|
277
|
+
await this.inner.sql_json(`ALTER TABLE ${name} ${alterTableClause(operation)}`);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Insert Arrow IPC data (stream or file format, for example from
|
|
281
|
+
* `tableToIPC()` of apache-arrow) into a table. Columns are matched by
|
|
282
|
+
* name; missing columns are filled with NULL. Resolves to the number of
|
|
283
|
+
* inserted rows.
|
|
284
|
+
*/
|
|
285
|
+
async insertArrow(table, data) {
|
|
286
|
+
return await this.inner.insert_arrow(table, toUint8Array(data));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Export a table or query result as a GeoParquet (1.1) file. Geometry and
|
|
290
|
+
* geography columns are described in the file's `geo` metadata.
|
|
291
|
+
*
|
|
292
|
+
* @param queryOrTable A query (`SELECT ...`, `WITH ...`) or a table name.
|
|
293
|
+
*/
|
|
294
|
+
async exportGeoParquet(queryOrTable, options = {}) {
|
|
295
|
+
const query = QUERY_PATTERN.test(queryOrTable)
|
|
296
|
+
? queryOrTable
|
|
297
|
+
: `SELECT * FROM ${queryOrTable}`;
|
|
298
|
+
return await this.inner.export_geoparquet(query, options);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Export a table or query result as GeoParquet and pass it to the export
|
|
302
|
+
* handler: a browser download by default (see `onExport` in
|
|
303
|
+
* {@link CereusDBOptions}). Resolves to the file name.
|
|
304
|
+
*
|
|
305
|
+
* @param queryOrTable A query (`SELECT ...`, `WITH ...`) or a table name.
|
|
306
|
+
*/
|
|
307
|
+
async downloadGeoParquet(queryOrTable, options = {}) {
|
|
308
|
+
const { filename, ...exportOptions } = options;
|
|
309
|
+
const handler = this.exportHandler;
|
|
310
|
+
if (handler === undefined) {
|
|
311
|
+
throw new Error('No export handler: downloads need a browser document; pass CereusDB.create({ onExport }) elsewhere');
|
|
312
|
+
}
|
|
313
|
+
const data = await this.exportGeoParquet(queryOrTable, exportOptions);
|
|
314
|
+
const name = filename ?? defaultExportFilename(queryOrTable);
|
|
315
|
+
await handler(name, data, PARQUET_MIME_TYPE);
|
|
316
|
+
return name;
|
|
317
|
+
}
|
|
318
|
+
/** Rewrite all tables of a persistent database into one segment each. */
|
|
319
|
+
async compactDatabase(name) {
|
|
320
|
+
await this.inner.compact_database(name);
|
|
321
|
+
}
|
|
322
|
+
/** Write pending changes of persistent databases (for example after registerGeoJSON()). */
|
|
323
|
+
async flush() {
|
|
324
|
+
await this.inner.flush();
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* List the tables and views of all databases. By default only the table
|
|
328
|
+
* names are returned, which is ambiguous when databases contain tables with
|
|
329
|
+
* the same name; pass `{ qualified: true }` for `database.schema.table`
|
|
330
|
+
* names, or use {@link CereusDB.catalog} for full details.
|
|
331
|
+
*/
|
|
332
|
+
tables(options = {}) {
|
|
333
|
+
return options.qualified ? this.inner.qualified_tables() : this.inner.tables();
|
|
119
334
|
}
|
|
120
335
|
/** Version string. */
|
|
121
336
|
version() {
|