diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index 66db502..1adfb00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## 4.0.1 + + - upgrade sharedb + + +## 4.0.0 + + - upgrade `pg` from 7.4.1 to 8.5.1 to prevent silent failure for node v14 or later, reported in [this issue](https://github.com/brianc/node-postgres/issues/2317). + - fix submit ops failed due to version mismatched - https://github.com/share/sharedb-postgres/issues/8 + +## Note about re-versioning + +Original `sharedb-postgres` seems to have been not maintained for a long time since 2018. Thus we made a fork and maintain it as `@plotdb/sharedb-postgre`. + + +# Change log in original repo + ## 3.0.0 Thanks to @billwashere, we upgraded to a more modern version of `pg` (4.5.1 -> diff --git a/README.md b/README.md index b385a69..9ee49de 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,68 @@ -# sharedb-postgres +# @plotdb/sharedb-postgres -PostgreSQL database adapter for [sharedb](https://github.com/share/sharedb). This -driver can be used both as a snapshot store and oplog. +PostgreSQL database adapter for [sharedb](https://github.com/share/sharedb). This driver can be used both as a snapshot store and oplog. Doesn't support queries (yet?). Moderately experimental. (This drives [Synaptograph](https://www.synaptograph.com)'s backend, and [@nornagon](https://github.com/nornagon) hasn't noticed any issues so far.) +## Note about versioning + +This is a fork from the [original `sharedb-postgres`](https://github.com/share/sharedb-postgres) and its relative forks (see [billwashere](https://github.com/billwashere/sharedb-postgres-jsonb), [zbryikt](https://github.com/zbryikt/sharedb-postgres-jsonb). It seems to have been not maintained for a long time since 2018, Thus we decide to fork it and maintain it as `@plotdb/sharedb-postgre`. + + +## Installation + +```cmd +npm i @plotdb/sharedb-postgres +``` + + +## Requirements + +Due to the fix to resolve [high concurency issues](https://github.com/share/sharedb-postgres/issues/1) Postgres 9.5+ is now required. + +## Migrating older versions + +Older versions of this adaptor used the data type json. You will need to alter the data type prior to using if you are upgrading. + +```PLpgSQL +ALTER TABLE ops + ALTER COLUMN operation + SET DATA TYPE jsonb + USING operation::jsonb; + +ALTER TABLE snapshots + ALTER COLUMN data + SET DATA TYPE jsonb + USING data::jsonb; +``` + ## Usage -`sharedb-postgres` wraps native [node-postgres](https://github.com/brianc/node-postgres), and it supports the same configuration options. +`sharedb-postgres-jsonb` wraps native [node-postgres](https://github.com/brianc/node-postgres), and it supports the same configuration options. To instantiate a sharedb-postgres wrapper, invoke the module and pass in your -PostgreSQL configuration as an argument. For example: +PostgreSQL configuration as an argument or use environmental arguments. + +For example using environmental arugments: + +```js +var db = require('@plotdb/sharedb-postgres')(); +var backend = require('sharedb')({db: db}) +``` + +Then executing via the command line + +``` +PGUSER=dbuser PGPASSWORD=secretpassword PGHOST=database.server.com PGDATABASE=mydb PGPORT=5433 npm start +``` + +Example using an object ```js -var db = require('sharedb-postgres')({host: 'localhost', database: 'mydb'}); +var db = require('@plotdb/sharedb-postgres')({host: 'localhost', database: 'mydb'}); var backend = require('sharedb')({db: db}) ``` diff --git a/index.js b/index.js index 442bd84..251efe2 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ function PostgresDB(options) { this.closed = false; + this.pg_config = options; this.pool = new pg.Pool(options); }; module.exports = PostgresDB; @@ -22,11 +23,6 @@ PostgresDB.prototype.close = function(callback) { if (callback) callback(); }; -function rollback(client, done) { - client.query('ROLLBACK', function(err) { - return done(err); - }) -} // Persists an op and snapshot if it is for the next version. Calls back with // callback(err, succeeded) @@ -41,84 +37,78 @@ PostgresDB.prototype.commit = function(collection, id, op, snapshot, options, ca * } * snapshot: PostgresSnapshot */ - this.pool.connect(function(err, client, done) { - if (err) { - done(client); - callback(err); - return; - } - function commit() { - client.query('COMMIT', function(err) { - done(err); - if (err) { - callback(err); - } else { - callback(null, true); - } - }) + this.pool.connect((err, client, done) => { + if (err) { + done(client); + callback(err); + return; + } + /* + * This query uses common table expression to upsert the snapshot table + * (iff the new version is exactly 1 more than the latest table or if + * the document id does not exists) + * + * It will then insert into the ops table if it is exactly 1 more than the + * latest table or it the first operation and iff the previous insert into + * the snapshot table is successful. + * + * This result of this query the version of the newly inserted operation + * If either the ops or the snapshot insert fails then 0 rows are returned + * + * If 0 zeros are return then the callback must return false + * + * Casting is required as postgres thinks that collection and doc_id are + * not varchar + */ + const query = { + name: 'sdb-commit-op-and-snap', + text: `WITH snapshot_id AS ( + INSERT INTO snapshots (collection, doc_id, doc_type, version, data) + SELECT $1::varchar collection, $2::varchar doc_id, $4 doc_type, $3 v, $5 d + WHERE $3 = ( + SELECT version+1 v + FROM snapshots + WHERE collection = $1 AND doc_id = $2 + FOR UPDATE + ) OR NOT EXISTS ( + SELECT 1 + FROM snapshots + WHERE collection = $1 AND doc_id = $2 + FOR UPDATE + ) + ON CONFLICT (collection, doc_id) DO UPDATE SET version = $3, data = $5, doc_type = $4 + RETURNING version +) +INSERT INTO ops (collection, doc_id, version, operation) +SELECT $1::varchar collection, $2::varchar doc_id, $3 v, $6 operation +WHERE ( + $3 = ( + SELECT max(version)+1 + FROM ops + WHERE collection = $1 AND doc_id = $2 + ) OR NOT EXISTS ( + SELECT 1 + FROM ops + WHERE collection = $1 AND doc_id = $2 + ) +) AND EXISTS (SELECT 1 FROM snapshot_id) +RETURNING version`, + values: [collection,id,snapshot.v, snapshot.type, snapshot.data,op] } - client.query( - 'SELECT max(version) AS max_version FROM ops WHERE collection = $1 AND doc_id = $2', - [collection, id], - function(err, res) { - var max_version = res.rows[0].max_version; - if (max_version == null) - max_version = 0; - if (snapshot.v !== max_version + 1) { - return callback(null, false); - } - client.query('BEGIN', function(err) { - client.query( - 'INSERT INTO ops (collection, doc_id, version, operation) VALUES ($1, $2, $3, $4)', - [collection, id, snapshot.v, op], - function(err, res) { - if (err) { - // TODO: if err is "constraint violation", callback(null, false) instead - rollback(client, done); - callback(err); - return; - } - if (snapshot.v === 1) { - client.query( - 'INSERT INTO snapshots (collection, doc_id, doc_type, version, data) VALUES ($1, $2, $3, $4, $5)', - [collection, id, snapshot.type, snapshot.v, snapshot.data], - function(err, res) { - // TODO: - // if the insert was successful and did insert, callback(null, true) - // if the insert was successful and did not insert, callback(null, false) - // if there was an error, rollback and callback(error) - if (err) { - rollback(client, done); - callback(err); - return; - } - commit(); - } - ) - } else { - client.query( - 'UPDATE snapshots SET doc_type = $3, version = $4, data = $5 WHERE collection = $1 AND doc_id = $2 AND version = ($4 - 1)', - [collection, id, snapshot.type, snapshot.v, snapshot.data], - function(err, res) { - // TODO: - // if any rows were updated, success - // if 0 rows were updated, rollback and not success - // if error, rollback and not success - if (err) { - rollback(client, done); - callback(err); - return; - } - commit(); - } - ) - } - } - ) - }) + client.query(query, (err, res) => { + if (err) { + callback(err) + } else if(res.rows.length === 0) { + done(client); + callback(null,false) + } + else { + done(client); + callback(null,true) } - ) - }) + }) + + }) }; // Get the named document from the database. The callback is called with (err, @@ -181,9 +171,12 @@ PostgresDB.prototype.getOps = function(collection, id, from, to, options, callba callback(err); return; } - client.query( - 'SELECT version, operation FROM ops WHERE collection = $1 AND doc_id = $2 AND version >= $3 AND version < $4', - [collection, id, from, to], + + var cmd = 'SELECT version, operation FROM ops WHERE collection = $1 AND doc_id = $2 AND version > $3 '; + var params = [collection, id, from]; + if(to || to == 0) { cmd += ' AND version <= $4'; params.push(to)} + cmd += ' order by version'; + client.query( cmd, params, function(err, res) { done(); if (err) { diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..393dc9f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,418 @@ +{ + "name": "@plotdb/sharedb-postgres", + "version": "4.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@plotdb/sharedb-postgres", + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "pg": "^8.5.1", + "pg-pool": "^3.2.1", + "sharedb": "^1.6.0" + } + }, + "node_modules/arraydiff": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/arraydiff/-/arraydiff-0.1.3.tgz", + "integrity": "sha1-hqVDbXty8b3aX9bXTock5C+Dzk0=" + }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "node_modules/hat": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/hat/-/hat-0.0.3.tgz", + "integrity": "sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo=" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "node_modules/ot-json0": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ot-json0/-/ot-json0-1.1.0.tgz", + "integrity": "sha512-wf5fci7GGpMYRDnbbdIFQymvhsbFACMHtxjivQo5KgvAHlxekyfJ9aPsRr6YfFQthQkk4bmsl5yESrZwC/oMYQ==" + }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "node_modules/pg": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz", + "integrity": "sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.4.0", + "pg-pool": "^3.2.2", + "pg-protocol": "^1.4.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/pg-connection-string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", + "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.1.tgz", + "integrity": "sha512-BQDPWUeKenVrMMDN9opfns/kZo4lxmSWhIqo+cSAF7+lfi9ZclQbr9vfnlNaPr8wYF3UYjm5X0yPAhbcgqNOdA==" + }, + "node_modules/pg-protocol": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz", + "integrity": "sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pg/node_modules/pg-pool": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz", + "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA==" + }, + "node_modules/pgpass": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", + "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", + "dependencies": { + "split2": "^3.1.1" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "node_modules/sharedb": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sharedb/-/sharedb-1.6.0.tgz", + "integrity": "sha512-N6jhmh408OrZQA7V9Eb1R1vfnJe4zoBjB932L/Z2U4jBZ1F8VTHneMntgVmDfjqkAIWN16TVpxMbcycuLxcDpQ==", + "dependencies": { + "arraydiff": "^0.1.1", + "async": "^2.6.3", + "fast-deep-equal": "^2.0.1", + "hat": "0.0.3", + "ot-json0": "^1.0.1" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + } + }, + "dependencies": { + "arraydiff": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/arraydiff/-/arraydiff-0.1.3.tgz", + "integrity": "sha1-hqVDbXty8b3aX9bXTock5C+Dzk0=" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "hat": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/hat/-/hat-0.0.3.tgz", + "integrity": "sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo=" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "ot-json0": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ot-json0/-/ot-json0-1.1.0.tgz", + "integrity": "sha512-wf5fci7GGpMYRDnbbdIFQymvhsbFACMHtxjivQo5KgvAHlxekyfJ9aPsRr6YfFQthQkk4bmsl5yESrZwC/oMYQ==" + }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "pg": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz", + "integrity": "sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.4.0", + "pg-pool": "^3.2.2", + "pg-protocol": "^1.4.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "dependencies": { + "pg-pool": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz", + "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA==" + } + } + }, + "pg-connection-string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", + "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.1.tgz", + "integrity": "sha512-BQDPWUeKenVrMMDN9opfns/kZo4lxmSWhIqo+cSAF7+lfi9ZclQbr9vfnlNaPr8wYF3UYjm5X0yPAhbcgqNOdA==" + }, + "pg-protocol": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz", + "integrity": "sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", + "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", + "requires": { + "split2": "^3.1.1" + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "sharedb": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sharedb/-/sharedb-1.6.0.tgz", + "integrity": "sha512-N6jhmh408OrZQA7V9Eb1R1vfnJe4zoBjB932L/Z2U4jBZ1F8VTHneMntgVmDfjqkAIWN16TVpxMbcycuLxcDpQ==", + "requires": { + "arraydiff": "^0.1.1", + "async": "^2.6.3", + "fast-deep-equal": "^2.0.1", + "hat": "0.0.3", + "ot-json0": "^1.0.1" + } + }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "requires": { + "readable-stream": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/package.json b/package.json index a3d57cb..051bc07 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,26 @@ { - "name": "sharedb-postgres", - "version": "3.0.0", - "description": "PostgreSQL adapter for ShareDB", + "name": "@plotdb/sharedb-postgres", + "version": "4.0.1", + "description": "PostgreSQL adapter for ShareDB. forked from share/sharedb-postgres", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Jeremy Apthorp ", "license": "MIT", - "keywords": ["sharedb", "sharejs", "share", "postgres"], - "repository": "share/sharedb-postgres", + "keywords": [ + "sharedb", + "sharejs", + "share", + "postgres" + ], + "repository": { + "type": "git", + "url": "https://github.com/plotdb/sharedb-postgres" + }, "dependencies": { "pg": "^8.5.1", - "sharedb": "^1.0.0-beta.7" + "pg-pool": "^3.2.1", + "sharedb": "^1.6.0" } } diff --git a/sample-cmd b/sample-cmd new file mode 100644 index 0000000..086def8 --- /dev/null +++ b/sample-cmd @@ -0,0 +1,16 @@ +/*var query = { + name: "sdb-commit-op-and-snap", + text: ` + with snaps as ( + insert into snapshots (collection, doc_id, doc_type, version, data) + values ($1, $2, $3, $4, $5) + on conflict (collection, doc_id, version) do update set doc_type = $3, data = $5 + returning version + ) + insert into ops ( collection, doc_id, version, operation ) + values ($1, $2, $4, $6) on conflict (collection, doc_id, version) do update set operation = $6 + returning version + `, + values: [collection, id, snapshot.type, snapshot.v, snapshot.data, op] +}*/ + diff --git a/structure.sql b/structure.sql index 606b5fc..c071b4e 100644 --- a/structure.sql +++ b/structure.sql @@ -1,16 +1,28 @@ -CREATE TABLE ops ( +CREATE TABLE IF NOT EXISTS ops ( collection character varying(255) not null, doc_id character varying(255) not null, version integer not null, - operation json not null, -- {v:0, create:{...}} or {v:n, op:[...]} + operation jsonb not null, -- {v:0, create:{...}} or {v:n, op:[...]} PRIMARY KEY (collection, doc_id, version) ); -CREATE TABLE snapshots ( +CREATE TABLE IF NOT EXISTS snapshots ( collection character varying(255) not null, doc_id character varying(255) not null, doc_type character varying(255) not null, version integer not null, - data json not null, + data jsonb not null, PRIMARY KEY (collection, doc_id) ); + +CREATE INDEX IF NOT EXISTS snapshots_version ON snapshots (collection, doc_id); + +ALTER TABLE ops + ALTER COLUMN operation + SET DATA TYPE jsonb + USING operation::jsonb; + +ALTER TABLE snapshots + ALTER COLUMN data + SET DATA TYPE jsonb + USING data::jsonb;