Getting Started with Doltgres and Knex.js
Doltgres is the Postgres flavor of Dolt. In case you missed it, Doltgres is now Beta! This means that we expect Doltgres to work with all the tools you use with Postgres, including Knex.js.
Knex.js is a "batteries included" SQL query builder for many SQL databases, including Postgres. It features both traditional node style callbacks as well as a promise interface for cleaner async flow control, a stream interface, full-featured query and schema builders, transaction support (with savepoints), connection pooling and standardized responses between different query clients and dialects.
This blog is inspired by a previous blog about getting Started with Dolt and Knex.js and will also walk through the Getting Started example from the Doltgres README, except in Javascript using Knex.js and Node.
TL;DR
If you don't want to run through this whole tutorial and just want the Javascript
code, it is available
in this GitHub repository on a branch. You can come
with any Doltgres SQL server and specify the connection information in a .env
file in the
repository root.
You must have Git and Node installed to run the code. This example uses a connection to a Hosted Dolt database, but the code will also work if you run a local Doltgres SQL server. If you are using a local Doltgres SQL server instead of Hosted Dolt you must also install Doltgres.
You can find directions for running the code in the repository README. But essentially all you need to do is:
% npm install
% node index.js
This script will reset the database to its original state every time the script runs. The code shows off table creation, Dolt commits, reading Dolt system tables, rollback using Dolt reset, branching, and merging all using Knex.js.
Create a Hosted Dolt deployment
First, go to the Create Deployment page.
Select Doltgres and choose whatever instance you want (we're using our $50 trial instance) and click Create
Deployment
.
Once the deployment has started, you'll see the connectivity information in the Database tab.
We then add this information to a .env
file in the root of the example code repository.
DB_HOST="dolthub-knex-example-dg.dbs.hosted.doltdb.com"
DB_PORT=5432
DB_USER="postgres"
DB_PASSWORD="xxxxxxxxxxxxxxxxxxx"
DB_NAME="postgres"
DB_SSL_PATH="/isrgrootx1.pem"
You will also need to download the Certificate and add it to the root of the repository.
The file name should match the DB_SSL_PATH
env.
Connect to the Hosted database
You can follow along with the Javascript code here. There are some small differences between Dolt and Doltgres that you can see from the Knex.js code diff here. In the following sections we'll walk through this file and explain what each query does and why.
The knex
module itself is a function which takes a configuration object for Knex,
accepting a few parameters. The client
parameter is required and determines which client
adapter will be used with the library. Doltgres is Postgres-compatible so we will use the
"postgres" client.
We use dotenv
to load the environment variables
we set earlier in the .env
file. We will use these to create a connection pool to our
Hosted database:
require("dotenv").config();
const knex = require("knex");
const fs = require("fs");
const poolConfig = { min: 0, max: 7 };
const config = {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
ssl: process.env.DB_SSL_PATH
? { ca: fs.readFileSync(__dirname + process.env.DB_SSL_PATH) }
: false,
};
const db = knex({
client: "postgres",
connection: config,
pool: poolConfig,
});
Once we've created our Knex instance, we can start running queries. We'll start with a
function that wraps the Dolt function
active_branch()
.
printActiveBranch();
async function printActiveBranch() {
const branch = await db.raw(`SELECT ACTIVE_BRANCH()`);
console.log("Active branch:", branch.rows[0].active_branch);
}
This prints:
Active branch: main
Notice that this function uses async/await.
Promises are the preferred way of
managing queries in Knex, as they allow you to return values from a fulfillment
handler, which in turn become the value of the promise. The main benefit of promises are
the ability to catch thrown errors without crashing the Node app, making your code behave
like a .try
/ .catch
/ .finally
in synchronous code.
Create tables
Now let's create some tables. In this example our database will have three tables:
employees
, teams
, and employees_teams
. We can utilize Knex's schema
builder method createTable
,
which creates a new table on the database with a callback function to modify the table's
structure.
setupDatabase();
async function setupDatabase() {
await db.schema.createTable("employees", (table) => {
table.integer("id").primary();
table.string("last_name");
table.string("first_name");
});
await db.schema.createTable("teams", (table) => {
table.integer("id").primary();
table.string("name");
});
await db.schema.createTable("employees_teams", (table) => {
table.integer("employee_id").references("id").inTable("employees");
table.integer("team_id").references("id").inTable("teams");
table.primary(["employee_id", "team_id"]);
});
}
Unlike Dolt which uses a SHOW TABLE
statement to examine our changes,
we check the pg_tables
table in the pg_catalog
schema for Doltgres and print the results.
printTables();
async function printTables() {
const res = await db.raw(
"SELECT * FROM pg_tables WHERE schemaname = 'public'"
);
const tables = res.rows.map((table) => table.tablename).join(", ");
console.log("Tables in database:", tables);
}
This outputs:
Tables in database: employees, employees_teams, teams
The employees
, teams
, and employees_teams
tables have been created.
Make a Dolt commit
Next, we'll Dolt commit our new tables. Both Git and SQL have the concept of commits and since Doltgres is a combination of Git and SQL it must support both. This can be confusing. A Dolt commit makes an entry in the commit log for versioning purposes. A SQL transaction commit is required to persist your database writes to disk so other connections can see them.
In order to make a Dolt commit, we use the
DOLT_COMMIT()
function. Doltgres exposes version control write operations as
functions, unlike Dolt which exposes them as procedures. Custom Dolt procedures still work in Doltgres but in Postgres, procedures cannot return results. For that reason, Dolt version control functionality was migrated to functions for Doltgres. So, in Doltgres you SELECT DOLT_COMMIT('-Am', 'new change')
to create a commit instead of CALL DOLT_COMMIT('-Am', 'new change')
like you would in Dolt.
The naming of these functions follows the Git command line standard. git add
on the Git
command line becomes DOLT_ADD()
as a Doltgres
SQL function or Dolt SQL procedure. Arguments mimic Git as well. If you know Git, you already know how to use
Doltgres.
Knex.js doesn't have a query builder for functions. We have to use
a raw SQL statement using Knex's raw()
method. For this method, you can specify a commit
author and message, which are passed as arguments into the Doltgres function. Like Git, Dolt
has a staging area, so we include a -A
option to add all tables before making a commit.
The resulting code looks like this:
doltCommit("Taylor <taylor@dolthub.com>", "Created tables");
async function doltCommit(author, msg) {
const res = await db.raw(
`SELECT DOLT_COMMIT('--author', ?::text, '-Am', ?::text)`,
[author, msg]
);
console.log("Created commit:", res.rows[0].dolt_commit[0]);
}
And running it results in the output:
Created commit: 80ks3k1ook712vauvavdnnd6t3q86e3d
Examine the commit log
Let's examine the Dolt commit log. Doltgres version control read operations are exposed in SQL
as custom system
tables or
table functions.
The commit log can be read using the
dolt.log
system table, named after the git log
and dolt log
command line equivalents. System tables in Doltgres look slightly different than system tables in Dolt, mostly due to how schemas work in Postgres. You can read about the background of that migration here. In Doltgres, you access commit logs using the log
table in the dolt
schema (i.e. SELECT * FROM dolt.log
), whereas Dolt does not have schemas so all system tables are prefixed with dolt_
(i.e. SELECT * FROM dolt_log
).
We can use Knex's query builder select
method to select and order the log
elements we want to print. The resulting code looks like this:
printCommitLog();
async function printCommitLog() {
const res = await db
.select("commit_hash", "committer", "message")
.from("dolt.log")
.orderBy("date", "desc");
console.log("Commit log:");
res.forEach((log) =>
console.log(` ${log.commit_hash}: ${log.message} by ${log.committer}`)
);
}
And it outputs:
Commit log:
80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account
Insert data
Now we're going to populate the tables with some data. We can use the insert
method and optionally specify
conflict behavior using
onConflict().merge()
or onConflict().ignore()
.
insertData();
async function insertData() {
await db("employees").insert([
{ id: 0, last_name: "Sehn", first_name: "Tim" },
{ id: 1, last_name: "Hendriks", first_name: "Brian" },
{ id: 2, last_name: "Son", first_name: "Aaron" },
{ id: 3, last_name: "Fitzgerald", first_name: "Brian" },
]);
await db("teams").insert([
{ id: 0, name: "Engineering" },
{ id: 1, name: "Sales" },
]);
await db("employees_teams").insert([
{ employee_id: 0, team_id: 0 },
{ employee_id: 1, team_id: 0 },
{ employee_id: 2, team_id: 0 },
{ employee_id: 0, team_id: 1 },
{ employee_id: 3, team_id: 1 },
]);
}
We can make sure our inserts worked by displaying a summary table. Knex.js comes with a query builder that supports many types of complex SQL queries. In this example, we'll construct a three table join. Later in this example we'll change the schema, so we'll account for that in this function.
printSummaryTable();
async function printSummaryTable() {
// Get all employees columns because we change the schema
const colInfo = await db("employees").columnInfo();
const employeeCols = Object.keys(colInfo)
.filter((col) => col !== "id")
.map((col) => `employees.${col}`);
// Dolt supports up to 12 table joins. Here we do a 3 table join.
const res = await db
.select("teams.name", ...employeeCols)
.from("employees")
.join("employees_teams", "employees.id", "employees_teams.employee_id")
.join("teams", "teams.id", "employees_teams.team_id")
.orderBy("teams.name", "asc");
console.log("Summary:");
res.forEach((row) => {
let startDate = "";
if ("start_date" in row) {
if (row.start_date === null) {
startDate = "None";
} else {
const d = new Date(row.start_date);
startDate = d.toDateString();
}
}
console.log(
` ${row.name}: ${row.first_name} ${row.last_name} ${startDate}`
);
});
}
Which results in the following output:
Summary:
Engineering: Tim Sehn
Engineering: Brian Hendriks
Engineering: Aaron Son
Sales: Tim Sehn
Sales: Brian Fitzgerald
Examine the status and diff
You can use the
dolt.status
(dolt_status
for Dolt)
system table to see what tables changed.
printStatus();
async function printStatus() {
const res = await db.select("*").from("dolt.status");
console.log("Status:");
if (res.length === 0) {
console.log(" No tables modified");
} else {
res.forEach((row) => {
console.log(` ${row.table_name}: ${row.status}`);
});
}
}
The resulting output looks like:
Status:
employees_teams: modified
employees: modified
teams: modified
Now that I see which tables changed and how, I want to see what rows changed in a
particular table. Doltgres is built on Dolt, which is built from the ground up to provide fast diffs between table
versions even for very large tables. In Doltgres, there are a few ways to view diffs: a
dolt_diff_<table>
system
table
for each user-defined table and a dolt_diff()
table
function.
We filter the diff table down to WORKING
changes so we only see changes that aren't
staged or committed.
printDiff("employees");
async function printDiff(table) {
const res = await db
.select("*")
.from(`dolt_diff_${table}`)
.where("to_commit", "WORKING");
console.log(`Diff for ${table}:`);
console.log(res);
}
The resulting output looks like:
Diff for employees:
[
{
to_id: 0,
to_last_name: 'Sehn',
to_first_name: 'Tim',
to_commit: 'WORKING',
to_commit_date: null,
from_id: null,
from_last_name: null,
from_first_name: null,
from_commit: '9iuopee4d7qdkbo8m8vn69j5qq9ee871',
from_commit_date: 2023-09-20T05:59:42.672Z,
diff_type: 'added'
},
{
to_id: 1,
to_last_name: 'Hendriks',
to_first_name: 'Brian',
to_commit: 'WORKING',
to_commit_date: null,
from_id: null,
from_last_name: null,
from_first_name: null,
from_commit: '9iuopee4d7qdkbo8m8vn69j5qq9ee871',
from_commit_date: 2023-09-20T05:59:42.672Z,
diff_type: 'added'
},
{
to_id: 2,
to_last_name: 'Son',
to_first_name: 'Aaron',
to_commit: 'WORKING',
to_commit_date: null,
from_id: null,
from_last_name: null,
from_first_name: null,
from_commit: '9iuopee4d7qdkbo8m8vn69j5qq9ee871',
from_commit_date: 2023-09-20T05:59:42.672Z,
diff_type: 'added'
},
{
to_id: 3,
to_last_name: 'Fitzgerald',
to_first_name: 'Brian',
to_commit: 'WORKING',
to_commit_date: null,
from_id: null,
from_last_name: null,
from_first_name: null,
from_commit: '9iuopee4d7qdkbo8m8vn69j5qq9ee871',
from_commit_date: 2023-09-20T05:59:42.672Z,
diff_type: 'added'
}
]
Before we go onto the next section let's Dolt commit our changes.
await doltCommit("Tim <tim@dolthub.com>", "Inserted data into tables");
await printCommitLog();
And you'll see our new commit in the log:
Created commit: flfiptf13lc7chlvbkikqtss3r02k2se
Commit log:
flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account
Rolling back a mistake
Doltgres has powerful rollback
capabilities. Let's
imagine I accidentally drop a table. The foreign keys will prevent me from dropping
employees
or teams
, but employees_teams
is not safe from an accident.
dropTable("employees_teams");
async function dropTable(table) {
await db.schema.dropTable(table);
}
As we can see from status and SHOW TABLES
it is gone.
await printStatus();
await printTables();
Status:
employees_teams: deleted
Tables in database: employees, teams
In a traditional database, this could be disastrous. In Doltgres, we can get it back with a
simple select
dolt_reset('hard')
.
This function takes an optional commit. If no commit is specified it resets to the HEAD
commit.
await doltResetHard();
await printStatus();
await printTables();
async function doltResetHard(commit) {
if (commit) {
await db.raw(`SELECT DOLT_RESET('--hard', ?::text)`, [commit]);
console.log("Resetting to commit:", commit);
} else {
await db.raw(`SELECT DOLT_RESET('--hard')`);
console.log("Resetting to HEAD");
}
}
Resetting to HEAD
Status:
No tables modified
Tables in database: employees, employees_teams, teams
Doltgres makes operating databases less error-prone. You can always back out changes you have in progress or rewind to a known good state.
Change data on a branch
Dolt and Doltgres are the only SQL databases with branches and merges. We will create and switch branches, and then make some changes and commit them to this new branch. Later, we'll merge all the changes together. Think of a branch as a really long SQL transaction.
First, you need to create a branch. Creating a branch is a write so you do it with a
function,
dolt_branch()
.
In the Javascript code, we also consult the
dolt.branches
system table to make sure the branch does not already exist. Then we use dolt_checkout()
to switch branches.
await createBranch("modify_data");
await checkoutBranch("modify_data");
async function getBranch(branch) {
return db.select("name").from("dolt.branches").where("name", branch);
}
async function createBranch(branch) {
const res = await getBranch(branch);
if (res.length > 0) {
console.log("Branch exists:", branch);
} else {
await db.raw(`SELECT DOLT_BRANCH(?::text)`, [branch]);
console.log("Created branch:", branch);
}
}
async function checkoutBranch(branch) {
await db.raw(`SELECT DOLT_CHECKOUT(?::text)`, [branch]);
console.log("Using branch:", branch);
}
Now that we're on a new branch, it's safe to make changes and the main
branch will
remain unchanged. We are going to use a
transaction to insert, update, and delete
using the Knex query builder. All queries within a transaction are executed on the same
database connection, and run the entire set of queries as a single unit of work. Any
failure will mean the database will rollback any queries executed on that connection to
the pre-transaction state.
modifyData();
async function modifyData() {
try {
await db.transaction(async (trx) => {
await trx("employees")
.where("first_name", "Tim")
.update("first_name", "Timothy");
await trx("employees").insert({
id: 4,
last_name: "Bantle",
first_name: "Taylor",
});
await trx("employees_teams").insert({
employee_id: 4,
team_id: 0,
});
await trx("employees_teams")
.where("employee_id", 0)
.where("employee_id", 1)
.del();
});
} catch (err) {
// Rolls back transaction
console.error(err);
}
}
Let's inspect what we've done to make sure it looks right.
await printStatus();
await printDiff("employees");
await printDiff("employees_teams");
await printSummaryTable();
Status:
employees: modified
employees_teams: modified
Diff for employees:
[
{
to_id: 0,
to_last_name: 'Sehn',
to_first_name: 'Timothy',
to_commit: 'WORKING',
to_commit_date: null,
from_id: 0,
from_last_name: 'Sehn',
from_first_name: 'Tim',
from_commit: 'bu68epe1k4el46l9cfsdishuf5665q7m',
from_commit_date: 2023-09-20T06:20:43.351Z,
diff_type: 'modified'
},
{
to_id: 4,
to_last_name: 'Bantle',
to_first_name: 'Taylor',
to_commit: 'WORKING',
to_commit_date: null,
from_id: null,
from_last_name: null,
from_first_name: null,
from_commit: 'bu68epe1k4el46l9cfsdishuf5665q7m',
from_commit_date: 2023-09-20T06:20:43.351Z,
diff_type: 'added'
}
]
Diff for employees_teams:
[
{
to_employee_id: 4,
to_team_id: 0,
to_commit: 'WORKING',
to_commit_date: null,
from_employee_id: null,
from_team_id: null,
from_commit: 'bu68epe1k4el46l9cfsdishuf5665q7m',
from_commit_date: 2023-09-20T06:20:43.351Z,
diff_type: 'added'
}
]
Summary:
Engineering: Brian Hendriks
Engineering: Aaron Son
Engineering: Taylor Bantle
Sales: Timothy Sehn
Sales: Brian Fitzgerald
I am added to the engineering team on the modify_data
branch. Tim is no longer on the Sales team.
Finally, let's commit these changes so we can make different changes on another branch.
await doltCommit("Brian <brian@dolthub.com>", "Modified data on branch");
await printCommitLog();
Created commit: j9taft0oie0kg1rhss4glvvusv0tdao9
Commit log:
j9taft0oie0kg1rhss4glvvusv0tdao9: Modified data on branch by Brian
flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account
Change schema on another branch
We're going to make a schema change on another branch and make some data modifications.
Below, you'll see we check out the main
branch so the new branch has the correct base
branch. Then, we create a new branch called modify_schema
and run the modify_schema()
function, which uses a transaction to add a start date column and populate it. We finally
use status and diff to show off what changed.
await checkoutBranch("main");
await createBranch("modify_schema");
await checkoutBranch("modify_schema");
await printActiveBranch();
await modifySchema();
await printStatus();
await printDiff("employees");
await printSummaryTable();
async function modifySchema() {
try {
await db.transaction(async (trx) => {
await trx.schema.alterTable("employees", (table) => {
table.date("start_date");
});
await trx("employees").where("id", 0).update("start_date", "2018-08-06");
await trx("employees").where("id", 1).update("start_date", "2018-08-06");
await trx("employees").where("id", 2).update("start_date", "2018-08-06");
await trx("employees").where("id", 3).update("start_date", "2021-04-19");
});
} catch (err) {
// Rolls back transaction
console.error(err);
}
}
This outputs the following:
Using branch: main
Created branch: modify_schema
Using branch: modify_schema
Active branch: modify_schema
Status:
employees: modified
Diff for employees:
[
{
to_id: 0,
to_last_name: 'Sehn',
to_first_name: 'Tim',
to_start_date: 2018-08-06T07:00:00.000Z,
to_commit: 'WORKING',
to_commit_date: null,
from_id: 0,
from_last_name: 'Sehn',
from_first_name: 'Tim',
from_start_date: null,
from_commit: '3li6nho3o9891ersocjh4q3l371uj880',
from_commit_date: 2023-09-20T06:28:42.459Z,
diff_type: 'modified'
},
{
to_id: 1,
to_last_name: 'Hendriks',
to_first_name: 'Brian',
to_start_date: 2018-08-06T07:00:00.000Z,
to_commit: 'WORKING',
to_commit_date: null,
from_id: 1,
from_last_name: 'Hendriks',
from_first_name: 'Brian',
from_start_date: null,
from_commit: '3li6nho3o9891ersocjh4q3l371uj880',
from_commit_date: 2023-09-20T06:28:42.459Z,
diff_type: 'modified'
},
{
to_id: 2,
to_last_name: 'Son',
to_first_name: 'Aaron',
to_start_date: 2018-08-06T07:00:00.000Z,
to_commit: 'WORKING',
to_commit_date: null,
from_id: 2,
from_last_name: 'Son',
from_first_name: 'Aaron',
from_start_date: null,
from_commit: '3li6nho3o9891ersocjh4q3l371uj880',
from_commit_date: 2023-09-20T06:28:42.459Z,
diff_type: 'modified'
},
{
to_id: 3,
to_last_name: 'Fitzgerald',
to_first_name: 'Brian',
to_start_date: 2021-04-19T07:00:00.000Z,
to_commit: 'WORKING',
to_commit_date: null,
from_id: 3,
from_last_name: 'Fitzgerald',
from_first_name: 'Brian',
from_start_date: null,
from_commit: '3li6nho3o9891ersocjh4q3l371uj880',
from_commit_date: 2023-09-20T06:28:42.459Z,
diff_type: 'modified'
}
]
Summary:
Engineering: Brian Hendriks Mon Aug 06 2018
Engineering: Aaron Son Mon Aug 06 2018
Sales: Tim Sehn Mon Aug 06 2018
Sales: Brian Fitzgerald Mon Apr 19 2021
As you can see our defensive coding in the Insert data section paid off and employee start dates are displayed. This looks good so we'll commit it.
await doltCommit("Taylor <taylor@dolthub.com>", "Modified schema on branch");
await printCommitLog();
Created commit: 4j10oomkf2vp1309eoo01bq4ha4t6iq2
Commit log:
4j10oomkf2vp1309eoo01bq4ha4t6iq2: Modified schema on branch by Taylor
flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account
Merge it all together
Now we will merge all the branches together and show the resulting summary table. To
merge, use the function
dolt_merge()
.
await checkoutBranch("main");
await printActiveBranch();
await printCommitLog();
await printSummaryTable();
await doltMerge("modify_data");
await printSummaryTable();
await printCommitLog();
await doltMerge("modify_schema");
await printSummaryTable();
await printCommitLog();
async function doltMerge(branch) {
const res = await db.raw(`SELECT DOLT_MERGE(?::text)`, [branch]);
console.log("Merge complete for ", branch);
const mergeRes = res.rows[0].dolt_merge;
console.log(` Commit: ${mergeRes[0]}`);
console.log(` Fast forward: ${mergeRes[1]}`);
console.log(` Conflicts: ${mergeRes[2]}`);
}
This outputs the following. You can see the data and schema evolving as we merge.
Using branch: main
Active branch: main
Commit log:
flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account
Summary:
Engineering: Brian Hendriks
Engineering: Aaron Son
Sales: Tim Sehn
Sales: Brian Fitzgerald
Merge complete for modify_data
Commit: 9he3pav122o3qmh7vlm58muv0ie2600j
Fast forward: 1
Conflicts: 0
Summary:
Engineering: Brian Hendriks
Engineering: Aaron Son
Engineering: Taylor Bantle
Sales: Timothy Sehn
Sales: Brian Fitzgerald
Commit log:
j9taft0oie0kg1rhss4glvvusv0tdao9: Modified data on branch by Brian
flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account
Merge complete for modify_schema
Commit: fjfuj3h9nuap933qtt0aa4fske2h8r6b
Fast forward: 0
Conflicts: 0
Summary:
Engineering: Brian Hendriks Mon Aug 06 2018
Engineering: Aaron Son Mon Aug 06 2018
Engineering: Taylor Bantle None
Sales: Timothy Sehn Mon Aug 06 2018
Sales: Brian Fitzgerald Mon Apr 19 2021
Commit log:
fjfuj3h9nuap933qtt0aa4fske2h8r6b: Merge branch 'modify_schema' into main by 14i5lj63h3u8l4kv
4j10oomkf2vp1309eoo01bq4ha4t6iq2: Modified schema on branch by Taylor
j9taft0oie0kg1rhss4glvvusv0tdao9: Modified data on branch by Brian
flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account
Notice the first merge was a fast-forward merge just like in Git. Doltgres will detect schema and data conflicts if you make them.
Conclusion
Congratulations on making it this far! You can now start building your own Doltgres application using Node and Knex.js. If you have any questions or need more help getting starting, stop by our Discord.