• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visgl / loaders.gl / 25798238260

13 May 2026 12:10PM UTC coverage: 60.607% (+0.3%) from 60.27%
25798238260

push

github

web-flow
feat(json) GeoJSON -> geoarrow, schema, logging  (#3399)

13466 of 24516 branches covered (54.93%)

Branch coverage included in aggregate %.

448 of 541 new or added lines in 12 files covered. (82.81%)

1264 existing lines in 117 files now uncovered.

27516 of 43103 relevant lines covered (63.84%)

15056.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.04
/modules/sql/src/sql-source.ts
1
// loaders.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import type {ArrowTable, Schema} from '@loaders.gl/schema';
6
import {DataSource} from '@loaders.gl/loader-utils';
7
import type {CoreAPI, RequiredOptions} from '@loaders.gl/loader-utils';
8

9
import type {
10
  SQLAdapter,
11
  SQLAdapterFactory,
12
  SQLCatalogInfo,
13
  SQLMetadata,
14
  SQLQueryOptions,
15
  SQLSchemaInfo,
16
  SQLSourceOptions,
17
  SQLTableInfo
18
} from './sql-types';
19
import {convertRowsToArrowTable, isNodeRuntime} from './sql-utils';
20

21
const SQL_ADAPTER_FACTORIES = new Map<string, SQLAdapterFactory>();
5✔
22

23
/** Registers a runtime adapter for a SQL source type. */
24
export function registerSQLAdapter(type: string, factory: SQLAdapterFactory): void {
25
  SQL_ADAPTER_FACTORIES.set(type, factory);
2✔
26
}
27

28
/** Returns the registered adapter factory for a SQL source type. */
29
export function getSQLAdapterFactory(type: string): SQLAdapterFactory | undefined {
30
  return SQL_ADAPTER_FACTORIES.get(type);
6✔
31
}
32

33
/** Base options shared by all SQL sources. */
34
export const SQL_SOURCE_DEFAULT_OPTIONS: Omit<RequiredOptions<SQLSourceOptions>, 'core'> = {
5✔
35
  sql: {
36
    resultFormat: 'auto',
37
    adapterOptions: {},
38
    browserAdapterFactory: undefined!,
39
    nodeAdapterFactory: undefined!
40
  },
41
  duckdb: {
42
    accessMode: 'auto',
43
    bundles: undefined!,
44
    databasePath: undefined!,
45
    remoteUrl: undefined!,
46
    workerUrl: undefined!
47
  },
48
  snowflake: {
49
    account: undefined!,
50
    database: undefined!,
51
    schema: undefined!,
52
    token: undefined!,
53
    warehouse: undefined!,
54
    role: undefined!,
55
    authenticator: undefined!
56
  }
57
};
58

59
/** Shared SQL-backed data source implementation. */
60
export class SQLDataSource extends DataSource<string, SQLSourceOptions> {
61
  private readonly sourceType: string;
62
  private readonly adapterFactory?: SQLAdapterFactory;
63
  private adapterPromise: Promise<SQLAdapter> | null = null;
10✔
64
  private metadataPromise: Promise<SQLMetadata> | null = null;
10✔
65

66
  constructor(
67
    data: string,
68
    options: SQLSourceOptions,
69
    sourceType: string,
70
    defaultOptions: Omit<RequiredOptions<SQLSourceOptions>, 'core'> = SQL_SOURCE_DEFAULT_OPTIONS,
10✔
71
    adapterFactory?: SQLAdapterFactory,
72
    coreApi?: CoreAPI
73
  ) {
74
    super(data, options, defaultOptions, coreApi);
10✔
75
    this.sourceType = sourceType;
10✔
76
    this.adapterFactory = adapterFactory || getSQLAdapterFactory(sourceType);
10✔
77
  }
78

79
  /** Returns cached high-level metadata for the SQL source. */
80
  async getMetadata(): Promise<SQLMetadata> {
81
    if (!this.metadataPromise) {
6✔
82
      this.metadataPromise = this.loadMetadata();
4✔
83
    }
84
    return this.metadataPromise;
6✔
85
  }
86

87
  /** Lists available catalogs from the connected database. */
88
  async listCatalogs(): Promise<SQLCatalogInfo[]> {
89
    const adapter = await this.getAdapter();
×
90
    return await adapter.listCatalogs();
×
91
  }
92

93
  /** Lists available schemas from the connected database. */
94
  async listSchemas(catalogName?: string): Promise<SQLSchemaInfo[]> {
95
    const adapter = await this.getAdapter();
×
96
    return await adapter.listSchemas(catalogName);
×
97
  }
98

99
  /** Lists available tables from the connected database. */
100
  async listTables(options?: {catalogName?: string; schemaName?: string}): Promise<SQLTableInfo[]> {
UNCOV
101
    const adapter = await this.getAdapter();
1✔
UNCOV
102
    return await adapter.listTables(options);
1✔
103
  }
104

105
  /** Returns the schema for a specific SQL table. */
106
  async getTableSchema(options: {
107
    catalogName?: string;
108
    schemaName?: string;
109
    tableName: string;
110
  }): Promise<Schema> {
UNCOV
111
    const adapter = await this.getAdapter();
1✔
UNCOV
112
    return await adapter.getTableSchema(options);
1✔
113
  }
114

115
  /** Executes a SQL query and returns object rows. */
116
  async queryRows(
117
    sqlText: string,
118
    options: SQLQueryOptions = {}
6✔
119
  ): Promise<Record<string, unknown>[]> {
120
    const adapter = await this.getAdapter();
6✔
121
    return await adapter.executeRows(sqlText, options);
4✔
122
  }
123

124
  /** Executes a SQL query and returns a loaders.gl Arrow table. */
125
  async queryArrow(sqlText: string, options: SQLQueryOptions = {}): Promise<ArrowTable> {
5✔
126
    const adapter = await this.getAdapter();
5✔
127
    if (adapter.executeArrow) {
5✔
128
      return await adapter.executeArrow(sqlText, options);
3✔
129
    }
130
    const rows = await adapter.executeRows(sqlText, options);
2✔
131
    return convertRowsToArrowTable(rows);
2✔
132
  }
133

134
  /** Closes any underlying adapter connection. */
135
  async close(): Promise<void> {
136
    if (!this.adapterPromise) {
3!
137
      return;
×
138
    }
139
    const adapter = await this.adapterPromise;
3✔
140
    await adapter.close();
3✔
141
    this.adapterPromise = null;
3✔
142
    this.metadataPromise = null;
3✔
143
  }
144

145
  protected async getAdapter(): Promise<SQLAdapter> {
146
    if (!this.adapterPromise) {
17✔
147
      this.adapterPromise = this.loadAdapter();
9✔
148
    }
149
    return await this.adapterPromise;
17✔
150
  }
151

152
  private async loadAdapter(): Promise<SQLAdapter> {
153
    try {
9✔
154
      const runtimeFactory = isNodeRuntime()
9✔
155
        ? this.options.sql?.nodeAdapterFactory
156
        : this.options.sql?.browserAdapterFactory;
157
      const factory = runtimeFactory || this.adapterFactory;
9✔
158
      if (!factory) {
9✔
159
        throw new Error(`No SQL adapter is registered for source type "${this.sourceType}".`);
2✔
160
      }
161

162
      const adapter = await factory({
7✔
163
        coreApi: this.coreApi,
164
        options: this.options,
165
        sourceType: this.sourceType,
166
        url: this.url
167
      });
168
      await adapter.connect();
7✔
169
      return adapter;
7✔
170
    } catch (error) {
171
      throw this.reportError(
2✔
172
        error,
173
        `Failed to create SQL adapter for ${this.sourceType} from ${this.url}`
174
      );
175
    }
176
  }
177

178
  private async loadMetadata(): Promise<SQLMetadata> {
179
    const adapter = await this.getAdapter();
4✔
180
    const [catalogs, schemas, tables] = await Promise.all([
4✔
181
      adapter.listCatalogs(),
182
      adapter.listSchemas(),
183
      adapter.listTables()
184
    ]);
185
    return {
4✔
186
      type: this.sourceType,
187
      capabilities: adapter.capabilities,
188
      catalogs,
189
      schemas,
190
      tables
191
    };
192
  }
193
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc