Skip to content

Commit

Permalink
Refactor sql.js out of SQLJSPackageDB constructor
Browse files Browse the repository at this point in the history
Instead of requiring the caller to initialize and pass in a sql.js Database, add an async initialize() function to do it within the SQLJSPackageDB implementation.

Callers can also use the new async newSQLJSPackageDB funtion to get a new initialized SQLJSPackageDB (one call instead of two).
  • Loading branch information
cmoesel committed Dec 12, 2024
1 parent 2fb67f4 commit 6e06847
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 73 deletions.
6 changes: 2 additions & 4 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import os from 'os';
import path from 'path';
import { Command, OptionValues } from 'commander';
import fs from 'fs-extra';
import initSqlJs from 'sql.js';
import { DiskBasedPackageCache } from './cache';
import { BuildDotFhirDotOrgClient } from './current';
import { SQLJSPackageDB } from './db';
import { newSQLJSPackageDB } from './db';
import { BasePackageLoader } from './loader';
import { DefaultRegistryClient } from './registry';
import { logger } from './utils';
Expand All @@ -33,8 +32,7 @@ async function install(fhirPackages: string[], options: OptionValues) {
logger.log(level, message);
};

const SQL = await initSqlJs();
const packageDB = new SQLJSPackageDB(new SQL.Database());
const packageDB = await newSQLJSPackageDB();
const fhirCache = options.cachePath ?? path.join(os.homedir(), '.fhir', 'packages');
const packageCache = new DiskBasedPackageCache(fhirCache, { log });
const registryClient = new DefaultRegistryClient({ log });
Expand Down
115 changes: 84 additions & 31 deletions src/db/SQLJSPackageDB.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import util from 'util';
import { Database, Statement } from 'sql.js';
import initSqlJs, { Database, Statement } from 'sql.js';
import { FindResourceInfoOptions, PackageInfo, PackageStats, ResourceInfo } from '../package';
import { PackageDB } from './PackageDB';

Expand Down Expand Up @@ -105,17 +104,23 @@ const INSERT_RESOURCE = `INSERT INTO resource
const SD_FLAVORS = ['Extension', 'Logical', 'Profile', 'Resource', 'Type'];

export class SQLJSPackageDB implements PackageDB {
private db: Database;
private insertPackageStmt: Statement;
private insertResourceStmt: Statement;
private findAllPackagesStmt: Statement;
private findPackagesStmt: Statement;
private findPackageStmt: Statement;
private optimized: boolean;
constructor(
private db: Database,
initialize = true
) {
if (initialize) {
private initialized: boolean;

constructor() {
this.initialized = false;
}

async initialize() {
const SQL = await initSqlJs();
if (!this.initialized) {
this.db = new SQL.Database();
this.db.run(
[
CREATE_PACKAGE_TABLE,
Expand All @@ -124,31 +129,46 @@ export class SQLJSPackageDB implements PackageDB {
CREATE_RESOURCE_TABLE_INDICES
].join(';')
);
this.insertPackageStmt = this.db.prepare(INSERT_PACKAGE);
this.insertResourceStmt = this.db.prepare(INSERT_RESOURCE);
this.findAllPackagesStmt = this.db.prepare(FIND_ALL_PACKAGES);
this.findPackagesStmt = this.db.prepare(FIND_PACKAGES);
this.findPackageStmt = this.db.prepare(FIND_PACKAGE);
this.initialized = true;
this.optimized = false;
}
this.insertPackageStmt = this.db.prepare(INSERT_PACKAGE);
this.insertResourceStmt = this.db.prepare(INSERT_RESOURCE);
this.findAllPackagesStmt = this.db.prepare(FIND_ALL_PACKAGES);
this.findPackagesStmt = this.db.prepare(FIND_PACKAGES);
this.findPackageStmt = this.db.prepare(FIND_PACKAGE);
this.optimized = false;
}

isInitialized() {
return this.initialized;
}

clear() {
this.db.exec('DELETE FROM package');
this.db.exec('DELETE FROM resource');
this.db.exec('VACUUM');
if (this.db) {
this.db.exec('DELETE FROM package');
this.db.exec('DELETE FROM resource');
this.db.exec('VACUUM');
this.optimized = false;
}
}

optimize() {
if (!this.optimized) {
this.db.exec('PRAGMA optimize=0x10002');
this.optimized = true;
} else {
this.db.exec('PRAGMA optimize');
if (this.db) {
if (!this.optimized) {
this.db.exec('PRAGMA optimize=0x10002');
this.optimized = true;
} else {
this.db.exec('PRAGMA optimize');
}
}
}

savePackageInfo(info: PackageInfo): void {
if (!this.db) {
throw new Error(
'SQLJSPackageDB not initialized. Please call the initialize() function before using this class.'
);
}
const binding: any = {
':name': info.name,
':version': info.version
Expand All @@ -163,6 +183,11 @@ export class SQLJSPackageDB implements PackageDB {
}

saveResourceInfo(info: ResourceInfo): void {
if (!this.db) {
throw new Error(
'SQLJSPackageDB not initialized. Please call the initialize() function before using this class.'
);
}
const binding: any = {
':resourceType': info.resourceType
};
Expand Down Expand Up @@ -221,6 +246,11 @@ export class SQLJSPackageDB implements PackageDB {
}

findPackageInfos(name: string): PackageInfo[] {
if (!this.db) {
throw new Error(
'SQLJSPackageDB not initialized. Please call the initialize() function before using this class.'
);
}
const results: PackageInfo[] = [];
const findStmt = name === '*' ? this.findAllPackagesStmt : this.findPackagesStmt;
try {
Expand All @@ -237,6 +267,11 @@ export class SQLJSPackageDB implements PackageDB {
}

findPackageInfo(name: string, version: string): PackageInfo | undefined {
if (!this.db) {
throw new Error(
'SQLJSPackageDB not initialized. Please call the initialize() function before using this class.'
);
}
try {
this.findPackageStmt.bind({ ':name': name, ':version': version });
if (this.findPackageStmt.step()) {
Expand All @@ -248,6 +283,11 @@ export class SQLJSPackageDB implements PackageDB {
}

findResourceInfos(key: string, options: FindResourceInfoOptions = {}): ResourceInfo[] {
if (!this.db) {
throw new Error(
'SQLJSPackageDB not initialized. Please call the initialize() function before using this class.'
);
}
// In case a key wasn't supplied, just use empty string. Later we might have it return ALL.
if (key == null) {
key = '';
Expand Down Expand Up @@ -355,6 +395,11 @@ export class SQLJSPackageDB implements PackageDB {
}

findResourceInfo(key: string, options: FindResourceInfoOptions = {}): ResourceInfo | undefined {
if (!this.db) {
throw new Error(
'SQLJSPackageDB not initialized. Please call the initialize() function before using this class.'
);
}
// TODO: Make this more sophisticated if/when it makes sense
const results = this.findResourceInfos(key, { ...options, limit: 1 });
if (results.length > 0) {
Expand All @@ -363,6 +408,11 @@ export class SQLJSPackageDB implements PackageDB {
}

getPackageStats(name: string, version: string): PackageStats | undefined {
if (!this.db) {
throw new Error(
'SQLJSPackageDB not initialized. Please call the initialize() function before using this class.'
);
}
const pkg = this.findPackageInfo(name, version);
if (pkg == null) {
return;
Expand All @@ -378,18 +428,21 @@ export class SQLJSPackageDB implements PackageDB {
};
}

exportDB(): Promise<{ mimeType: string; data: Buffer }> {
async exportDB(): Promise<{ mimeType: string; data: Buffer }> {
if (!this.db) {
return Promise.reject(
new Error(
'SQLJSPackageDB not initialized. Please call the initialize() function before using this class.'
)
);
}
const data = this.db.export();
return Promise.resolve({ mimeType: 'application/x-sqlite3', data: Buffer.from(data) });
}
}

logPackageTable() {
const res = this.db.exec('SELECT * FROM package');
console.log(util.inspect(res, false, 3, true));
}

logResourceTable() {
const res = this.db.exec('SELECT * FROM resource');
console.log(util.inspect(res, false, 3, true));
}
export async function newSQLJSPackageDB(): Promise<SQLJSPackageDB> {
const packageDB = new SQLJSPackageDB();
await packageDB.initialize();
return packageDB;
}
6 changes: 2 additions & 4 deletions src/loader/DefaultPackageLoader.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import os from 'os';
import path from 'path';
import initSqlJs from 'sql.js';
import { DiskBasedPackageCache } from '../cache/DiskBasedPackageCache';
import { BuildDotFhirDotOrgClient } from '../current';
import { SQLJSPackageDB } from '../db';
import { newSQLJSPackageDB } from '../db';
import { DefaultRegistryClient } from '../registry';
import { BasePackageLoader, BasePackageLoaderOptions } from './BasePackageLoader';

export async function defaultPackageLoader(options: BasePackageLoaderOptions) {
const SQL = await initSqlJs();
const packageDB = new SQLJSPackageDB(new SQL.Database());
const packageDB = await newSQLJSPackageDB();
const fhirCache = path.join(os.homedir(), '.fhir', 'packages');
const packageCache = new DiskBasedPackageCache(fhirCache, {
log: options.log
Expand Down
Loading

0 comments on commit 6e06847

Please sign in to comment.