Skip to content

Commit

Permalink
feat:databaseDriverAdaptations - Postgres|Sqlite
Browse files Browse the repository at this point in the history
  • Loading branch information
tksilicon committed Aug 4, 2020
1 parent 4f3982d commit ae779d5
Show file tree
Hide file tree
Showing 20 changed files with 1,252 additions and 173 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ mysql.log
package-lock.json
.vscode
node_modules
test.db
*.code-workspace
89 changes: 89 additions & 0 deletions PostgresClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {
Client,
PostgresError,
log,
QueryResult,
QueryConfig,
} from "./postgres_deps.ts";

/** Transaction processor */
export interface TransactionProcessor<T> {
(connection: PostgresClient): Promise<T>;
}

export class PostgresClient {
protected client: Client;

constructor(config: object) {
this.client = new Client(config);
}

async connect(): Promise<void> {
return this.client.connect();
}

// TODO: can we use more specific type for args?
async query(text: string): Promise<QueryResult> {
return this.client.query(text);
}

// TODO: can we use more specific type for args?
async execute(text: string): Promise<QueryResult> {
return this.client.query(text);
}

async multiQuery(queries: QueryConfig[]): Promise<QueryResult[]> {
const result: QueryResult[] = [];

for (const query of queries) {
result.push(await this.client.query(query));
}

return result;
}

async end(): Promise<void> {
await this.client.end();
}

/**
* Use a connection for transaction processor
*
* @param fn transation processor
*/
async useConnection<T>(fn: (conn: PostgresClient) => Promise<T>) {
if (!this.client) {
throw new Error("Unconnected");
}
try {
const result = await fn(this);
return result;
} catch (error) {
throw new PostgresError(
{ severity: "high", code: "TA", message: "transactions" },
);
}
}

/**
* Execute a transaction process, and the transaction successfully
* returns the return value of the transaction process
* @param processor transation processor
*/
async transaction<T>(processor: TransactionProcessor<T>): Promise<T> {
return await this.useConnection(async (connection) => {
try {
await connection.query("BEGIN");
const result = await processor(connection);
await connection.query("COMMIT");
return result;
} catch (error) {
log.info(`ROLLBACK: ${error.message}`);
await connection.query("ROLLBACK");
throw new PostgresError(
{ severity: "high", code: "TA", message: "transactions" },
);
}
});
}
}
58 changes: 49 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
![GitHub release](https://img.shields.io/github/release/manyuanrong/dso.svg)
![(Deno)](https://img.shields.io/badge/deno-1.0.0-green.svg)

`dso` is a simple ORM Library based on [deno_mysql](https://github.com/manyuanrong/deno_mysql)
`dso` is a simple ORM Library based on [deno_mysql](https://github.com/manyuanrong/deno_mysql),[deno-postgres](https://deno.land/x/postgres), [Deno SQLite Module](https://deno.land/x/sqlite),

### Example

Expand Down Expand Up @@ -51,16 +51,52 @@ class UserModel extends BaseModel {
}

const userModel = dso.define(UserModel);
/*
Database configuration follows this interface
export interface Config extends Base {
clientConfig: object | ClientConfig //object or MySQL configuration object;
client?: Client | PostgresClient | SqliteClient ;
}
MYSQL Configuration
const config: ClientConfig = {
hostname: "127.0.0.1",
port: 3306,
poolSize: 3,
debug: false,
username: "root",
password: "",
db: "",
};
const mysqlConfig = {
type: "MYSQL",
clientConfig: { ...config, db: "test_orm" },
};
const config2 = {
user: "thankgodukachukwu",
database: "test_orm",
hostname: "127.0.0.1",
password: "",
port: 5432,
};
const postgresConfig = {
type: "POSTGRES",
clientConfig: config2,
};
const sqliteConfig = {
type: "SQLITE",
clientConfig: { database: "test.db" },
};
*/
async function main() {
// The database must be created before linking
await dso.connect({
hostname: "127.0.0.1",
port: 3306,
username: "root",
password: "",
db: "dbname"
});
await dso.connect(mysqlConfig| postgresConfig | sqliteConfig);

/*
When installing or initializing a database,
Expand Down Expand Up @@ -204,6 +240,10 @@ await dso.sync(force);
Create and start a transaction.

New `Model` instances must be obtained through `getModel(Model)`. Otherwise, it will not be controlled by transactions.
Transactions for each driver is indicated by a second argument `driverType:string` which can be
`"MYSQL" | "POSTGRES" | "SQLITE"`

The example below runs invokes transaction on MySQL database

```ts
const result = await dso.transaction<boolean>(async trans => {
Expand All @@ -213,7 +253,7 @@ const result = await dso.transaction<boolean>(async trans => {
userId = await userModel.insert({ nickName: "foo", password: "bar", phoneNumber: "08135539123" });
topicId = await topicModel.insert({ title: "zoo", userId });
return true;
});
}, "MYSQL");
```

### Top Level Types
Expand Down
112 changes: 112 additions & 0 deletions SqliteClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { DB } from "https://deno.land/x/sqlite/mod.ts";
import { log } from "./sqlite_deps.ts";
import SqliteError from "https://deno.land/x/sqlite/src/error.ts";
import { Rows } from "./sqlite_deps.ts";

/** Transaction processor */
export interface TransactionProcessor<T> {
(connection: SqliteClient): Promise<T>;
}

export class SqliteClient {
protected db: DB;
constructor(path: string) {
this.db = new DB(path);
}

query(sql: string): Rows {
return this.db.query(sql);
}

execute(sql: string): Rows {
return this.db.query(sql);
}

/**
* DB.close
*
* Close database handle. This must be called if
* DB is no longer used, to avoid leaking file
* resources.
*
* If force is specified, any on-going transactions
* will be closed.
*/
close(force: boolean = false) {
this.db.close(force);
}

/**
* DB.lastInsertRowId
*
* Get last inserted row id. This corresponds to
* the SQLite function `sqlite3_last_insert_rowid`.
*
* By default, it will return 0 if there is no row
* inserted yet.
*/
get lastInsertRowId(): number {
return this.db.lastInsertRowId;
}

/**
* DB.changes
*
* Return the number of rows modified, inserted or
* deleted by the most recently completed query.
* This corresponds to the SQLite function
* `sqlite3_changes`.
*/
get changes(): number {
return this.db.changes;
}

/**
* DB.totalChanges
*
* Return the number of rows modified, inserted or
* deleted since the database was opened.
* This corresponds to the SQLite function
* `sqlite3_total_changes`.
*/
get totalChanges(): number {
return this.db.totalChanges;
}

/**
* Use a connection for transaction processor
*
* @param fn transation processor
*/
async useConnection<T>(fn: (conn: this) => Promise<T>) {
if (!this.db) {
throw new Error("Unconnected");
}
try {
const result = await fn(this);
return result;
} catch (error) {
throw new SqliteError("connection", 2);
}
}

/**
* Execute a transaction process, and the transaction successfully
* returns the return value of the transaction process
* @param processor transation processor
*/
async transaction<T>(processor: TransactionProcessor<T>): Promise<T> {
return await this.useConnection(async (connection) => {
try {
await connection.query("BEGIN");
const result = await processor(connection);
await connection.query("COMMIT");
return result;
} catch (error) {
log.info(`ROLLBACK: ${error.message}`);
await connection.query("ROLLBACK");
throw new SqliteError("transaction", 1);
}
});
}
}
2 changes: 1 addition & 1 deletion mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ export { dso } from "./src/dso.ts";
export * from "./src/field.ts";
export * from "./src/index.ts";
export * from "./src/model.ts";
export * from "./src/util.ts";
export * from "./src/util.ts";
24 changes: 24 additions & 0 deletions postgres_deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export {
Pool,
PostgresError,
Client,
} from "https://deno.land/x/postgres/mod.ts";

export {
Connection,
} from "https://deno.land/x/postgres/connection.ts";

export {
Query as QueryPostgres,
QueryConfig,
QueryResult,
} from "https://deno.land/x/postgres/query.ts";

export {
ConnectionOptions,
createParams,
} from "https://deno.land/x/postgres/connection_params.ts";

export {
log,
} from "https://deno.land/x/mysql/src/logger.ts";
16 changes: 16 additions & 0 deletions sqlite_deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export {
getStr,
setStr,
setArr,
} from "https://deno.land/x/sqlite/src/wasm.ts";

export { Status, Values } from "https://deno.land/x/sqlite/src/constants.ts";

export {
Rows,
Empty,
} from "https://deno.land/x/sqlite/src/rows.ts";

export {
log,
} from "https://deno.land/x/mysql/src/logger.ts";
3 changes: 3 additions & 0 deletions src/drivers/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface Base {
type: string; // MySQl|Postgres|Sqlite
}
7 changes: 7 additions & 0 deletions src/drivers/mysql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Base } from "./base.ts";
import { ClientConfig, Client } from "../../deps.ts";

export interface MysqlConfig extends Base {
clientConfig: ClientConfig;
client?: Client;
}
7 changes: 7 additions & 0 deletions src/drivers/postgres.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Base } from "./base.ts";
import { PostgresClient } from "../../PostgresClient.ts";

export interface PostgresConfig extends Base {
clientConfig: object;
client?: PostgresClient;
}
7 changes: 7 additions & 0 deletions src/drivers/sqlite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Base } from "./base.ts";
import { SqliteClient } from "../../SqliteClient.ts";

export interface SqliteConfig extends Base {
clientConfig: object;
client?: SqliteClient;
}
Loading

0 comments on commit ae779d5

Please sign in to comment.