Skip to content

Commit

Permalink
Merge pull request #83 from dolthub/taylor/postgres-graphql
Browse files Browse the repository at this point in the history
Postgres support for graphql server
  • Loading branch information
tbantle22 authored Dec 19, 2023
2 parents 9da6d45 + e440e89 commit 4aec1cb
Show file tree
Hide file tree
Showing 24 changed files with 446 additions and 115 deletions.
1 change: 1 addition & 0 deletions graphql-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"graphql": "^16.7.1",
"graphql-upload": "13",
"mysql2": "^3.6.5",
"pg": "^8.11.3",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.5.4",
"timeago.js": "^4.0.2",
Expand Down
9 changes: 8 additions & 1 deletion graphql-server/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,18 @@ type DatabaseConnection {
name: String!
hideDoltFeatures: Boolean
useSSL: Boolean
type: DatabaseType
}

enum DatabaseType {
Mysql
Postgres
}

type DoltDatabaseDetails {
isDolt: Boolean!
hideDoltFeatures: Boolean!
type: DatabaseType!
}

type DiffStat {
Expand Down Expand Up @@ -324,7 +331,7 @@ enum DiffRowType {
type Mutation {
createBranch(databaseName: String!, newBranchName: String!, fromRefName: String!): String!
deleteBranch(databaseName: String!, branchName: String!): Boolean!
addDatabaseConnection(connectionUrl: String!, name: String!, hideDoltFeatures: Boolean, useSSL: Boolean): String
addDatabaseConnection(connectionUrl: String!, name: String!, hideDoltFeatures: Boolean, useSSL: Boolean, type: DatabaseType): String
removeDatabaseConnection(name: String!): Boolean!
createDatabase(databaseName: String!): Boolean!
resetDatabase: Boolean!
Expand Down
15 changes: 11 additions & 4 deletions graphql-server/src/connections/connection.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { Resolver } from "@nestjs/graphql";
import * as mysql from "mysql2/promise";
import { DataSource } from "typeorm";
import { DatabaseType } from "../databases/database.enum";
import { QueryFactory } from "../queryFactory";
import { DoltQueryFactory } from "../queryFactory/dolt";
import { MySQLQueryFactory } from "../queryFactory/mysql";
import { PostgresQueryFactory } from "../queryFactory/postgres";

export class WorkbenchConfig {
hideDoltFeatures: boolean;

connectionUrl: string;

useSSL: boolean;

type: DatabaseType;
}

@Resolver()
Expand Down Expand Up @@ -58,8 +62,8 @@ export class ConnectionResolver {
this.workbenchConfig = config;

this.ds = new DataSource({
type: "mysql",
connectorPackage: "mysql2",
type: config.type,
connectorPackage: config.type === "mysql" ? "mysql2" : undefined,
url: config.connectionUrl,
ssl: config.useSSL
? {
Expand All @@ -78,15 +82,18 @@ export class ConnectionResolver {

await this.ds.initialize();

const qf = await this.newQueryFactory();
const qf = await this.newQueryFactory(config.type);
this.qf = qf;
}

getWorkbenchConfig(): WorkbenchConfig | undefined {
return this.workbenchConfig;
}

async newQueryFactory(): Promise<QueryFactory> {
async newQueryFactory(type: DatabaseType): Promise<QueryFactory> {
if (type === DatabaseType.Postgres) {
return new PostgresQueryFactory(this.ds);
}
try {
const res = await this.ds?.query("SELECT dolt_version()");
if (res) {
Expand Down
8 changes: 8 additions & 0 deletions graphql-server/src/databases/database.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { registerEnumType } from "@nestjs/graphql";

export enum DatabaseType {
Mysql = "mysql",
Postgres = "postgres",
}

registerEnumType(DatabaseType, { name: "DatabaseType" });
4 changes: 4 additions & 0 deletions graphql-server/src/databases/database.model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Field, ObjectType } from "@nestjs/graphql";
import { DatabaseType } from "./database.enum";

@ObjectType()
export class DatabaseConnection {
Expand All @@ -13,4 +14,7 @@ export class DatabaseConnection {

@Field({ nullable: true })
useSSL?: boolean;

@Field(_type => DatabaseType, { nullable: true })
type?: DatabaseType;
}
25 changes: 16 additions & 9 deletions graphql-server/src/databases/database.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { ConnectionResolver } from "../connections/connection.resolver";
import { FileStoreService } from "../fileStore/fileStore.service";
import { DBArgs } from "../utils/commonTypes";
import { DatabaseType } from "./database.enum";
import { DatabaseConnection } from "./database.model";

@ArgsType()
Expand All @@ -25,6 +26,9 @@ class AddDatabaseConnectionArgs {

@Field({ nullable: true })
useSSL?: boolean;

@Field(_type => DatabaseType, { nullable: true })
type?: DatabaseType;
}

@ObjectType()
Expand All @@ -34,6 +38,9 @@ class DoltDatabaseDetails {

@Field()
hideDoltFeatures: boolean;

@Field(_type => DatabaseType)
type: DatabaseType;
}

@ArgsType()
Expand Down Expand Up @@ -64,15 +71,13 @@ export class DatabaseResolver {
async databases(): Promise<string[]> {
const conn = this.conn.connection();
const dbs = await conn.databases();
return dbs
.map(db => db.Database)
.filter(
db =>
db !== "information_schema" &&
db !== "mysql" &&
db !== "dolt_cluster" &&
!db.includes("/"),
);
return dbs.filter(
db =>
db !== "information_schema" &&
db !== "mysql" &&
db !== "dolt_cluster" &&
!db.includes("/"),
);
}

@Query(_returns => DoltDatabaseDetails)
Expand All @@ -82,6 +87,7 @@ export class DatabaseResolver {
return {
isDolt: conn.isDolt,
hideDoltFeatures: workbenchConfig?.hideDoltFeatures ?? false,
type: workbenchConfig?.type ?? DatabaseType.Mysql,
};
}

Expand All @@ -93,6 +99,7 @@ export class DatabaseResolver {
connectionUrl: args.connectionUrl,
hideDoltFeatures: !!args.hideDoltFeatures,
useSSL: !!args.useSSL,
type: args.type ?? DatabaseType.Mysql,
};
await this.conn.addConnection(workbenchConfig);

Expand Down
20 changes: 16 additions & 4 deletions graphql-server/src/queryFactory/dolt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as foreignKey from "../../indexes/foreignKey.model";
import * as index from "../../indexes/index.model";
import { convertToStringForQuery } from "../../rowDiffs/rowDiff.enums";
import { SchemaType } from "../../schemas/schema.enums";
import { SchemaItem } from "../../schemas/schema.model";
import {
DoltSystemTable,
systemTableValues,
Expand Down Expand Up @@ -91,7 +92,7 @@ export class DoltQueryFactory
return res.filter(c => c.Key === "PRI").map(c => c.Field);
}

async getSchemas(args: t.RefArgs, type?: SchemaType): t.UPR {
async getSchemas(args: t.RefArgs, type?: SchemaType): Promise<SchemaItem[]> {
return this.queryForBuilder(
async em => {
let sel = em
Expand All @@ -103,21 +104,32 @@ export class DoltQueryFactory
type,
});
}
return handleTableNotFound(async () => sel.getRawMany());
const res = await handleTableNotFound(async () => sel.getRawMany());
if (!res) return [];
return res.map(r => {
return { name: r.name, type: r.type };
});
},
args.databaseName,
args.refName,
);
}

async getProcedures(args: t.RefArgs): t.UPR {
async getProcedures(args: t.RefArgs): Promise<SchemaItem[]> {
return this.queryForBuilder(
async em => {
const sel = em
.createQueryBuilder()
.select("*")
.from(DoltSystemTable.PROCEDURES, "");
return handleTableNotFound(async () => sel.getRawMany());
const res = await handleTableNotFound(async () => sel.getRawMany());
if (!res) return [];
return res.map(r => {
return {
name: r.name,
type: SchemaType.Procedure,
};
});
},
args.databaseName,
args.refName,
Expand Down
7 changes: 4 additions & 3 deletions graphql-server/src/queryFactory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { DataSource, EntityManager, QueryRunner } from "typeorm";
import { SortBranchesBy } from "../branches/branch.enum";
import { CommitDiffType } from "../diffSummaries/diffSummary.enums";
import { SchemaType } from "../schemas/schema.enums";
import { SchemaItem } from "../schemas/schema.model";
import { TableDetails } from "../tables/table.model";
import * as t from "./types";

Expand Down Expand Up @@ -47,7 +48,7 @@ export declare class QueryFactory {

// QUERIES

databases(): t.PR;
databases(): Promise<string[]>;

currentDatabase(): Promise<string | undefined>;

Expand All @@ -68,9 +69,9 @@ export declare class QueryFactory {

getSqlSelect(args: t.RefArgs & { queryString: string }): t.PR;

getSchemas(args: t.RefArgs, type?: SchemaType): t.UPR;
getSchemas(args: t.RefArgs, type?: SchemaType): Promise<SchemaItem[]>;

getProcedures(args: t.RefArgs): t.UPR;
getProcedures(args: t.RefArgs): Promise<SchemaItem[]>;

// DOLT-SPECIFIC QUERIES

Expand Down
31 changes: 22 additions & 9 deletions graphql-server/src/queryFactory/mysql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { EntityManager, QueryRunner } from "typeorm";
import { QueryFactory } from "..";
import { SchemaType } from "../../schemas/schema.enums";
import { SchemaItem } from "../../schemas/schema.model";
import { TableDetails } from "../../tables/table.model";
import { ROW_LIMIT } from "../../utils";
import { BaseQueryFactory } from "../base";
Expand All @@ -16,6 +17,14 @@ export class MySQLQueryFactory
{
isDolt = false;

async checkoutDatabase(
qr: QueryRunner,
dbName: string,
refName?: string,
): Promise<void> {
await qr.query(qh.useDB(dbName, refName, this.isDolt));
}

async query<T>(
q: string,
p: t.Params,
Expand All @@ -24,7 +33,7 @@ export class MySQLQueryFactory
): Promise<T> {
return this.handleAsyncQuery(async qr => {
if (dbName) {
await qr.query(qh.useDB(dbName, refName, this.isDolt));
await this.checkoutDatabase(qr, dbName, refName);
}

const res = await qr.query(q, p);
Expand All @@ -44,7 +53,7 @@ export class MySQLQueryFactory
}

if (dbName) {
await qr.query(qh.useDB(dbName, refName, this.isDolt));
await this.checkoutDatabase(qr, dbName, refName);
}

return executeQuery(query);
Expand All @@ -58,7 +67,7 @@ export class MySQLQueryFactory
): Promise<T> {
return this.handleAsyncQuery(async qr => {
if (dbName) {
await qr.query(qh.useDB(dbName, refName, this.isDolt));
await this.checkoutDatabase(qr, dbName, refName);
}

return executeQuery(qr.manager);
Expand All @@ -72,15 +81,16 @@ export class MySQLQueryFactory
): Promise<T> {
return this.handleAsyncQuery(async qr => {
if (dbName) {
await qr.query(qh.useDB(dbName, refName, this.isDolt));
await this.checkoutDatabase(qr, dbName, refName);
}

return executeQuery(qr);
});
}

async databases(): t.PR {
return this.query(qh.databasesQuery, []);
async databases(): Promise<string[]> {
const res: t.RawRows = await this.query(qh.databasesQuery, []);
return res.map(r => r.Database);
}

async getTableNames(args: t.RefArgs): Promise<string[]> {
Expand Down Expand Up @@ -137,7 +147,7 @@ export class MySQLQueryFactory
return this.query(args.queryString, [], args.databaseName, args.refName);
}

async getSchemas(args: t.DBArgs, type?: SchemaType): t.UPR {
async getSchemas(args: t.DBArgs, type?: SchemaType): Promise<SchemaItem[]> {
return this.queryMultiple(async query => {
const vRes = await query(qh.getViewsQuery, [args.databaseName]);
const views = vRes.map(v => {
Expand All @@ -161,12 +171,15 @@ export class MySQLQueryFactory
}, args.databaseName);
}

async getProcedures(args: t.DBArgs): t.UPR {
return this.query(
async getProcedures(args: t.DBArgs): Promise<SchemaItem[]> {
const res: t.RawRows = await this.query(
qh.proceduresQuery,
[args.databaseName],
args.databaseName,
);
return res.map(r => {
return { name: r.Name, type: SchemaType.Procedure };
});
}

// DOLT QUERIES NOT IMPLEMENTED FOR MYSQL
Expand Down
Loading

0 comments on commit 4aec1cb

Please sign in to comment.