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

ngageoint / geopackage-js / 4078143969

pending completion
4078143969

push

github

Christopher Caldwell
bump version

3593 of 8015 branches covered (44.83%)

Branch coverage included in aggregate %.

15102 of 20471 relevant lines covered (73.77%)

1564.55 hits per line

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

13.55
/lib/db/sqljsAdapter.ts
1
import { DBAdapter } from './dbAdapter';
2
import { DBValue } from './dbValue';
3
import initSqlJs from 'rtree-sql.js';
1✔
4
import { GeoPackageException } from '../geoPackageException';
1✔
5
import { ResultSet } from './resultSet';
1✔
6

7
/**
8
 * This adapter uses sql.js to execute queries against the GeoPackage database
9
 * @see {@link http://kripken.github.io/sql.js/documentation/|sqljs}
10
 */
11
export class SqljsAdapter implements DBAdapter {
1✔
12
  static SQL: { Database: any };
13
  db: any;
14
  filePath: string | Buffer | Uint8Array;
15
  static sqljsWasmLocateFile: (filename: string) => string = (filename) => filename;
1✔
16

17
  static setSqljsWasmLocateFile(locateFile: (filename: string) => string): void {
1✔
18
    SqljsAdapter.sqljsWasmLocateFile = locateFile;
×
19
  }
20

21
  /**
22
   * Returns a Promise which, when resolved, returns a DBAdapter which has connected to the GeoPackage database file
23
   */
24
  initialize(): Promise<this> {
1✔
25
    return new Promise<this>((resolve, reject) => {
×
26
      new Promise((resolve) => {
×
27
        if (SqljsAdapter.SQL == null) {
×
28
          initSqlJs({
×
29
            locateFile: SqljsAdapter.sqljsWasmLocateFile,
30
          })
31
            .then((SQL: { Database: any }) => {
32
              SqljsAdapter.SQL = SQL;
×
33
              resolve(SQL);
×
34
            })
35
            .catch((e) => {
36
              reject(e);
×
37
            });
38
        } else {
39
          resolve(SqljsAdapter.SQL);
×
40
        }
41
      })
42
        .then((SQL: { Database: any }) => {
43
          if (this.filePath && typeof this.filePath === 'string') {
×
44
            if (typeof process !== 'undefined' && process.version) {
×
45
              // eslint-disable-next-line @typescript-eslint/no-var-requires
46
              const fs = require('fs');
×
47
              if (this.filePath.indexOf('http') === 0) {
×
48
                // eslint-disable-next-line @typescript-eslint/no-var-requires
49
                const http = require('http');
×
50
                http
×
51
                  .get(this.filePath, (response: any) => {
52
                    if (response.statusCode !== 200) {
×
53
                      return reject(new Error('Unable to reach url: ' + this.filePath));
×
54
                    }
55
                    const body: any = [];
×
56
                    response.on('data', (chunk: any) => body.push(chunk));
×
57
                    response.on('end', () => {
×
58
                      const t = new Uint8Array(Buffer.concat(body));
×
59
                      this.db = new SQL.Database(t);
×
60
                      resolve(this);
×
61
                    });
62
                  })
63
                  .on('error', (e: any) => {
64
                    return reject(e);
×
65
                  });
66
              } else {
67
                try {
×
68
                  fs.statSync(this.filePath);
×
69
                } catch (e) {
70
                  this.db = new SQL.Database();
×
71
                  // var adapter = new SqljsAdapter(db);
72
                  return resolve(this);
×
73
                }
74
                const filebuffer = fs.readFileSync(this.filePath);
×
75
                const t = new Uint8Array(filebuffer);
×
76
                this.db = new SQL.Database(t);
×
77
                return resolve(this);
×
78
              }
79
            } else {
80
              // eslint-disable-next-line no-undef
81
              const xhr = new XMLHttpRequest();
×
82
              xhr.open('GET', this.filePath, true);
×
83
              xhr.responseType = 'arraybuffer';
×
84
              xhr.onload = (): void => {
×
85
                if (xhr.status !== 200) {
×
86
                  return reject(new Error('Unable to reach url: ' + this.filePath));
×
87
                }
88
                const uInt8Array = new Uint8Array(xhr.response);
×
89
                this.db = new SQL.Database(uInt8Array);
×
90
                return resolve(this);
×
91
              };
92
              xhr.onerror = (): void => {
×
93
                return reject(new Error('Error reaching url: ' + this.filePath));
×
94
              };
95
              xhr.send();
×
96
            }
97
          } else if (this.filePath) {
×
98
            const byteArray = this.filePath;
×
99
            this.db = new SQL.Database(byteArray);
×
100
            return resolve(this);
×
101
          } else {
102
            this.db = new SQL.Database();
×
103
            return resolve(this);
×
104
          }
105
        })
106
        .catch((e) => {
107
          reject(e);
×
108
        });
109
    });
110
  }
111

112
  /**
113
   * @param  {string|Buffer|Uint8Array} [filePath] string path to an existing file or a path to where a new file will be created or a url from which to download a GeoPackage or a Uint8Array containing the contents of the file, if undefined, an in memory database is created
114
   */
115
  constructor(filePath?: string | Buffer | Uint8Array) {
116
    this.filePath = filePath;
×
117
  }
118

119
  size(): number {
1✔
120
    throw new GeoPackageException('Method not implemented.');
×
121
  }
122

123
  readableSize(): string {
1✔
124
    throw new GeoPackageException('Method not implemented.');
×
125
  }
126

127
  /**
128
   * Closes the connection to the GeoPackage
129
   */
130
  close(): void {
1✔
131
    this.db.close();
×
132
  }
133
  /**
134
   * Get the connection to the database file
135
   * @return {any}
136
   */
137
  getDBConnection(): any {
1✔
138
    return this.db;
×
139
  }
140
  /**
141
   * Returns a Uint8Array containing the contents of the database as a file
142
   */
143
  async export(): Promise<Uint8Array> {
1✔
144
    return this.db.export();
×
145
  }
146
  /**
147
   * Registers the given function so that it can be used by SQL statements
148
   * @see {@link http://kripken.github.io/sql.js/documentation/#http://kripken.github.io/sql.js/documentation/class/Database.html#create_function-dynamic|sqljs create_function}
149
   * @param  {string} name               name of function to register
150
   * @param  {Function} functionDefinition function to register
151
   * @return {Adapter} this
152
   */
153
  registerFunction(name: string, functionDefinition: Function): this {
1✔
154
    this.db.create_function(name, functionDefinition);
×
155
    return this;
×
156
  }
157
  /**
158
   * Gets one row of results from the statement
159
   * @see {@link http://kripken.github.io/sql.js/documentation/#http://kripken.github.io/sql.js/documentation/class/Statement.html#get-dynamic|sqljs get}
160
   * @see {@link http://kripken.github.io/sql.js/documentation/#http://kripken.github.io/sql.js/documentation/class/Statement.html#getAsObject-dynamic|sqljs getAsObject}
161
   * @param  {String} sql    statement to run
162
   * @param  {Array|Object} [params] substitution parameters
163
   * @return {Object}
164
   */
165
  get(sql: string, params?: [] | Record<string, DBValue>): Record<string, DBValue> {
1✔
166
    params = params || [];
×
167
    const statement = this.db.prepare(sql);
×
168
    statement.bind(params);
×
169
    const hasResult = statement.step();
×
170
    let row;
171
    if (hasResult) {
×
172
      row = statement.getAsObject();
×
173
    }
174
    statement.free();
×
175
    return row;
×
176
  }
177
  /**
178
   * Determines if a tableName exists in the database
179
   * @param {String} tableName
180
   * @returns {Boolean}
181
   */
182
  isTableExists(tableName: string): boolean {
1✔
183
    const statement = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=:name");
×
184
    statement.bind([tableName]);
×
185
    const hasResult = statement.step();
×
186
    let row;
187
    if (hasResult) {
×
188
      row = statement.getAsObject();
×
189
    }
190
    statement.free();
×
191
    return !!row;
×
192
  }
193
  /**
194
   * Gets all results from the statement in an array
195
   * @param  {String} sql    statement to run
196
   * @param  {Array|Object} [params] bind parameters
197
   * @return {Object[]}
198
   */
199
  all(sql: string, params?: [] | Record<string, DBValue>): Record<string, DBValue>[] {
1✔
200
    const rows = [];
×
201
    const iterator = this.each(sql, params);
×
202
    for (const row of iterator) {
×
203
      rows.push(row);
×
204
    }
205
    return rows;
×
206
  }
207
  /**
208
   * Returns an Iterable with results from the query
209
   * @param  {string} sql    statement to run
210
   * @param  {Object|Array} params bind parameters
211
   * @return {IterableIterator<Object>}
212
   */
213
  each(sql: string, params?: [] | Record<string, DBValue>): IterableIterator<Record<string, DBValue>> {
1✔
214
    const statement = this.db.prepare(sql);
×
215
    statement.bind(params);
×
216
    return {
×
217
      [Symbol.iterator](): IterableIterator<Record<string, DBValue>> {
218
        return this;
×
219
      },
220
      next: function (): { value: Record<string, DBValue>; done: boolean } {
221
        if (statement.step()) {
×
222
          return {
×
223
            value: statement.getAsObject(),
224
            done: false,
225
          };
226
        } else {
227
          statement.free();
×
228
          return {
×
229
            value: undefined,
230
            done: true,
231
          };
232
        }
233
      },
234
    };
235
  }
236
  /**
237
   * Runs the statement specified, returning information about what changed
238
   * @see {@link http://kripken.github.io/sql.js/documentation/#http://kripken.github.io/sql.js/documentation/class/Statement.html#run-dynamic|sqljs run}
239
   * @param  {string} sql    statement to run
240
   * @param  {Object|Array} [params] bind parameters
241
   * @return {Object} object containing a changes property indicating the number of rows changed and a lastInsertROWID indicating the last inserted row
242
   */
243
  run(sql: string, params?: [] | Record<string, DBValue>): { changes: number; lastInsertRowid: number } {
1✔
244
    if (params && !(params instanceof Array)) {
×
245
      for (const key in params) {
×
246
        params['$' + key] = params[key];
×
247
      }
248
    }
249
    this.db.run(sql, params);
×
250
    const lastId = this.db.exec('select last_insert_rowid();');
×
251
    let lastInsertedId;
252
    if (lastId) {
×
253
      lastInsertedId = lastId[0].values[0][0];
×
254
    }
255
    return {
×
256
      lastInsertRowid: lastInsertedId,
257
      changes: this.db.getRowsModified(),
258
    };
259
  }
260
  /**
261
   * Runs the specified insert statement and returns the last inserted id or undefined if no insert happened
262
   * @param  {String} sql    statement to run
263
   * @param  {Object|Array} [params] bind parameters
264
   * @return {Number} last inserted row id
265
   */
266
  insert(sql: string, params?: [] | Record<string, DBValue>): number {
1✔
267
    if (params && !(params instanceof Array)) {
×
268
      for (const key in params) {
×
269
        params['$' + key] = params[key];
×
270
      }
271
    }
272
    const statement = this.db.prepare(sql, params);
×
273
    statement.step();
×
274
    statement.free();
×
275
    const lastId = this.db.exec('select last_insert_rowid();');
×
276
    if (lastId) {
×
277
      return lastId[0].values[0][0];
×
278
    } else {
279
      return;
×
280
    }
281
  }
282
  /**
283
   * Prepares a SQL statement
284
   * @param sql
285
   */
286
  prepareStatement(sql: string): any {
1✔
287
    return this.db.prepare(sql);
×
288
  }
289
  /**
290
   * Runs an insert statement with the parameters provided
291
   * @param  {any} statement  statement to run
292
   * @param  {Object|Array} [params] bind parameters
293
   * @return {Number} last inserted row id
294
   */
295
  bindAndInsert(statement: any, params?: [] | Record<string, DBValue>): number {
1✔
296
    if (params && !(params instanceof Array)) {
×
297
      for (const key in params) {
×
298
        params['$' + key] = params[key];
×
299
      }
300
    }
301
    return statement.run(params).lastInsertRowid;
×
302
  }
303
  /**
304
   * Closes a prepared statement
305
   * @param statement
306
   */
307
  closeStatement(statement: any): void {
1✔
308
    statement.free();
×
309
  }
310
  /**
311
   * Runs the specified delete statement and returns the number of deleted rows
312
   * @param  {String} sql    statement to run
313
   * @param  {Object|Array} [params] bind parameters
314
   * @return {Number} deleted rows
315
   */
316
  delete(sql: string, params?: [] | Record<string, DBValue>): number {
1✔
317
    const statement = this.db.prepare(sql, params);
×
318
    statement.step();
×
319
    const rowsModified = this.db.getRowsModified();
×
320
    statement.free();
×
321
    return rowsModified;
×
322
  }
323
  /**
324
   * Drops the table
325
   * @param  {String} table table name
326
   * @return {Boolean} indicates if the table was dropped
327
   */
328
  dropTable(table: string): boolean {
1✔
329
    const response = this.db.exec('DROP TABLE IF EXISTS "' + table + '"');
×
330
    this.db.exec('VACUUM');
×
331
    return !!response;
×
332
  }
333
  /**
334
   * Counts rows that match the query
335
   * @param  {String} tableName table name from which to count
336
   * @param  {String} [where]     where clause
337
   * @param  {Object|Array} [whereArgs] where args
338
   * @return {Number} count
339
   */
340
  count(tableName: string, where?: string, whereArgs?: [] | Record<string, DBValue>): number {
1✔
341
    let sql = 'SELECT COUNT(*) as count FROM "' + tableName + '"';
×
342
    if (where) {
×
343
      sql += ' where ' + where;
×
344
    }
345
    return this.get(sql, whereArgs).count as number;
×
346
  }
347

348
  transaction(func: Function): void {
1✔
349
    this.db.exec('BEGIN TRANSACTION');
×
350
    try {
×
351
      func();
×
352
      this.db.exec('COMMIT TRANSACTION');
×
353
    } catch (e) {
354
      this.db.exec('ROLLBACK TRANSACTION');
×
355
      throw e;
×
356
    }
357
  }
358

359
  /**
360
   * Enable or disable unsafe mode
361
   * @param enabled
362
   */
363
  // eslint-disable-next-line @typescript-eslint/no-empty-function,@typescript-eslint/no-unused-vars
364
  unsafe(enabled: boolean): void {}
1✔
365

366
  /**
367
   * Returns a result set for the given query
368
   */
369
  query(sql: string, params?: [] | Record<string, DBValue>): ResultSet {
1✔
370
    let statement = this.db.prepare(sql);
×
371
    statement.bind(params);
×
372
    return new ResultSet(
×
373
      {
374
        [Symbol.iterator](): IterableIterator<Record<string, DBValue>> {
375
          return this;
×
376
        },
377
        next: function (): { value: Record<string, DBValue>; done: boolean } {
378
          if (statement.step()) {
×
379
            return {
×
380
              value: statement.getAsObject(),
381
              done: false,
382
            };
383
          } else {
384
            statement.free();
×
385
            return {
×
386
              value: undefined,
387
              done: true,
388
            };
389
          }
390
        },
391
      },
392
      {
393
        close: (): void => {
394
          statement.free();
×
395
          statement = null;
×
396
        },
397
      },
398
      this,
399
    );
400
  }
401
}
1✔
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

© 2025 Coveralls, Inc