Skip to content

Commit

Permalink
[Flight] Add rudimentary PG binding (#20372)
Browse files Browse the repository at this point in the history
* [Flight] Add rudimentary PG binding

* Use nested Maps for parameters

* Inline and fix Flow
  • Loading branch information
gaearon authored Dec 4, 2020
1 parent 88ef957 commit 60e4a76
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 0 deletions.
12 changes: 12 additions & 0 deletions packages/react-pg/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# react-pg

This package is meant to be used alongside yet-to-be-released, experimental React features. It's unlikely to be useful in any other context.

**Do not use in a real application.** We're publishing this early for
demonstration purposes.

**Use it at your own risk.**

# No, Really, It Is Unstable

The API ~~may~~ will change wildly between versions.
12 changes: 12 additions & 0 deletions packages/react-pg/index.browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

throw new Error(
'This entry point is not yet supported in the browser environment',
);
12 changes: 12 additions & 0 deletions packages/react-pg/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

'use strict';

export * from './index.node';
12 changes: 12 additions & 0 deletions packages/react-pg/index.node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

'use strict';

export * from './src/ReactPostgres';
7 changes: 7 additions & 0 deletions packages/react-pg/npm/index.browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react-pg.browser.production.min.js');
} else {
module.exports = require('./cjs/react-pg.browser.development.js');
}
3 changes: 3 additions & 0 deletions packages/react-pg/npm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict';

module.exports = require('./index.node');
7 changes: 7 additions & 0 deletions packages/react-pg/npm/index.node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react-pg.node.production.min.js');
} else {
module.exports = require('./cjs/react-pg.node.development.js');
}
27 changes: 27 additions & 0 deletions packages/react-pg/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"private": true,
"name": "react-pg",
"description": "React bindings for PostgreSQL",
"version": "0.0.0",
"repository": {
"type" : "git",
"url" : "https://github.com/facebook/react.git",
"directory": "packages/react-pg"
},
"files": [
"LICENSE",
"README.md",
"build-info.json",
"index.js",
"index.node.js",
"index.browser.js",
"cjs/"
],
"peerDependencies": {
"react": "^17.0.0",
"pg": "*"
},
"browser": {
"./index.js": "./index.browser.js"
}
}
107 changes: 107 additions & 0 deletions packages/react-pg/src/ReactPostgres.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {Wakeable} from 'shared/ReactTypes';

import {unstable_getCacheForType} from 'react';
import {Pool as PostgresPool} from 'pg';
import {prepareValue} from 'pg/lib/utils';

const Pending = 0;
const Resolved = 1;
const Rejected = 2;

type PendingResult = {|
status: 0,
value: Wakeable,
|};

type ResolvedResult = {|
status: 1,
value: mixed,
|};

type RejectedResult = {|
status: 2,
value: mixed,
|};

type Result = PendingResult | ResolvedResult | RejectedResult;

function toResult(thenable): Result {
const result: Result = {
status: Pending,
value: thenable,
};
thenable.then(
value => {
if (result.status === Pending) {
const resolvedResult = ((result: any): ResolvedResult);
resolvedResult.status = Resolved;
resolvedResult.value = value;
}
},
err => {
if (result.status === Pending) {
const rejectedResult = ((result: any): RejectedResult);
rejectedResult.status = Rejected;
rejectedResult.value = err;
}
},
);
return result;
}

function readResult(result: Result) {
if (result.status === Resolved) {
return result.value;
} else {
throw result.value;
}
}

export function Pool(options: mixed) {
this.pool = new PostgresPool(options);
// Unique function per instance because it's used for cache identity.
this.createResultMap = function() {
return new Map();
};
}

Pool.prototype.query = function(query: string, values?: Array<mixed>) {
const pool = this.pool;
const outerMap = unstable_getCacheForType(this.createResultMap);

let innerMap: Map<any, any> = outerMap;
let key = query;
if (values != null) {
// If we have parameters, each becomes as a nesting layer for Maps.
// We want to find (or create as needed) the innermost Map, and return that.
for (let i = 0; i < values.length; i++) {
let nextMap = innerMap.get(key);
if (nextMap === undefined) {
nextMap = new Map();
innerMap.set(key, nextMap);
}
innerMap = nextMap;
// Postgres bindings convert everything to strings:
// https://node-postgres.com/features/queries#parameterized-query
// We reuse their algorithm instead of reimplementing.
key = prepareValue(values[i]);
}
}

let entry: Result | void = innerMap.get(key);
if (!entry) {
const thenable = pool.query(query, values);
entry = toResult(thenable);
innerMap.set(key, entry);
}
return readResult(entry);
};
14 changes: 14 additions & 0 deletions scripts/flow/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,17 @@ declare module 'EventListener' {

declare function __webpack_chunk_load__(id: string): Promise<mixed>;
declare function __webpack_require__(id: string): any;

declare module 'pg' {
declare var Pool: (
options: mixed,
) => {
query: (query: string, values?: Array<mixed>) => void,
};
}

declare module 'pg/lib/utils' {
declare module.exports: {
prepareValue(val: any): mixed,
};
}
18 changes: 18 additions & 0 deletions scripts/rollup/bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,24 @@ const bundles = [
externals: ['react', 'http', 'https'],
},

/******* React PG Browser (experimental, new) *******/
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: ISOMORPHIC,
entry: 'react-pg/index.browser',
global: 'ReactPostgres',
externals: [],
},

/******* React PG Node (experimental, new) *******/
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: ISOMORPHIC,
entry: 'react-pg/index.node',
global: 'ReactPostgres',
externals: ['react', 'pg'],
},

/******* React DOM *******/
{
bundleTypes: [
Expand Down

0 comments on commit 60e4a76

Please sign in to comment.