mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 21:07:28 -08:00
✨ Add type casting to postgres queries (#1600)
* 538 add support for casting types in postgres
* Add typing to postgres insert nodes and removed unnecessary field from crate db
* Added placeholder and description for types in postgres
* Adding tests to insert and changes suggested by Ben
* ⚡ Minor improvement
Co-authored-by: mutdmour <mutdmour@gmail.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
3b00c96643
commit
f225bbbb84
|
@ -829,7 +829,7 @@ export class HttpRequest implements INodeType {
|
|||
|
||||
// Add Content Type if any are set
|
||||
if (options.bodyContentCustomMimeType) {
|
||||
if(requestOptions.headers === undefined) {
|
||||
if (requestOptions.headers === undefined) {
|
||||
requestOptions.headers = {};
|
||||
}
|
||||
requestOptions.headers['Content-Type'] = options.bodyContentCustomMimeType;
|
||||
|
@ -929,7 +929,7 @@ export class HttpRequest implements INodeType {
|
|||
if (property === 'body') {
|
||||
continue;
|
||||
}
|
||||
returnItem[property] = response![property];
|
||||
returnItem[property] = response![property];
|
||||
}
|
||||
|
||||
newItem.json = returnItem;
|
||||
|
|
|
@ -68,19 +68,22 @@ export async function pgInsert(
|
|||
const schema = getNodeParam('schema', 0) as string;
|
||||
let returnFields = (getNodeParam('returnFields', 0) as string).split(',') as string[];
|
||||
const columnString = getNodeParam('columns', 0) as string;
|
||||
const columns = columnString.split(',').map(column => column.trim());
|
||||
|
||||
const cs = new pgp.helpers.ColumnSet(columns);
|
||||
const columns = columnString.split(',')
|
||||
.map(column => column.trim().split(':'))
|
||||
.map(([name, cast]) => ({ name, cast }));
|
||||
|
||||
const te = new pgp.helpers.TableName({ table, schema });
|
||||
|
||||
// Prepare the data to insert and copy it to be returned
|
||||
const insertItems = getItemCopy(items, columns);
|
||||
const columnNames = columns.map(column => column.name);
|
||||
const insertItems = getItemCopy(items, columnNames);
|
||||
|
||||
const columnSet = new pgp.helpers.ColumnSet(columns);
|
||||
|
||||
// Generate the multi-row insert query and return the id of new row
|
||||
returnFields = returnFields.map(value => value.trim()).filter(value => !!value);
|
||||
const query =
|
||||
pgp.helpers.insert(insertItems, cs, te) +
|
||||
pgp.helpers.insert(insertItems, columnSet, te) +
|
||||
(returnFields.length ? ` RETURNING ${returnFields.join(',')}` : '');
|
||||
|
||||
// Executing the query to insert the data
|
||||
|
@ -109,21 +112,36 @@ export async function pgUpdate(
|
|||
const updateKey = getNodeParam('updateKey', 0) as string;
|
||||
const columnString = getNodeParam('columns', 0) as string;
|
||||
|
||||
const columns = columnString.split(',').map(column => column.trim());
|
||||
const [updateColumnName, updateColumnCast] = updateKey.split(':');
|
||||
const updateColumn = {
|
||||
name: updateColumnName,
|
||||
cast: updateColumnCast,
|
||||
};
|
||||
|
||||
const columns = columnString.split(',')
|
||||
.map(column => column.trim().split(':'))
|
||||
.map(([name, cast]) => ({ name, cast }));
|
||||
|
||||
const te = new pgp.helpers.TableName({ table, schema });
|
||||
|
||||
// Make sure that the updateKey does also get queried
|
||||
if (!columns.includes(updateKey)) {
|
||||
columns.unshift(updateKey);
|
||||
const targetCol = columns.find((column) => column.name === updateColumn.name);
|
||||
if (!targetCol) {
|
||||
columns.unshift(updateColumn);
|
||||
}
|
||||
else if (!targetCol.cast) {
|
||||
targetCol.cast = updateColumn.cast || targetCol.cast;
|
||||
}
|
||||
|
||||
// Prepare the data to update and copy it to be returned
|
||||
const updateItems = getItemCopy(items, columns);
|
||||
const columnNames = columns.map(column => column.name);
|
||||
const updateItems = getItemCopy(items, columnNames);
|
||||
|
||||
const columnSet = new pgp.helpers.ColumnSet(columns);
|
||||
|
||||
// Generate the multi-row update query
|
||||
const query =
|
||||
pgp.helpers.update(updateItems, columns, te) + ' WHERE v.' + updateKey + ' = t.' + updateKey;
|
||||
pgp.helpers.update(updateItems, columnSet, te) + ' WHERE v.' + updateColumn.name + ' = t.' + updateColumn.name;
|
||||
|
||||
// Executing the query to update the data
|
||||
await db.none(query);
|
||||
|
|
|
@ -116,9 +116,9 @@ export class Postgres implements INodeType {
|
|||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'id,name,description',
|
||||
placeholder: 'id:int,name:text,description',
|
||||
description:
|
||||
'Comma separated list of the properties which should used as columns for the new rows.',
|
||||
'Comma separated list of the properties which should used as columns for the new rows.<br>You can use type casting with colons (:) like id:int.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Fields',
|
||||
|
@ -186,9 +186,9 @@ export class Postgres implements INodeType {
|
|||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'name,description',
|
||||
placeholder: 'name:text,description',
|
||||
description:
|
||||
'Comma separated list of the properties which should used as columns for rows to update.',
|
||||
'Comma separated list of the properties which should used as columns for rows to update.<br>You can use type casting with colons (:) like id:int.',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -107,22 +107,6 @@ export class QuestDb implements INodeType {
|
|||
required: true,
|
||||
description: 'Name of the table in which to insert data to.',
|
||||
},
|
||||
{
|
||||
displayName: 'Columns',
|
||||
name: 'columns',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'insert',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'id,name,description',
|
||||
description:
|
||||
'Comma separated list of the properties which should used as columns for the new rows.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Fields',
|
||||
name: 'returnFields',
|
||||
|
@ -194,7 +178,7 @@ export class QuestDb implements INodeType {
|
|||
}).join(',');
|
||||
|
||||
const query = `INSERT INTO ${tableName} (${columns.join(',')}) VALUES (${values});`;
|
||||
queries.push(query);
|
||||
queries.push(query);
|
||||
});
|
||||
|
||||
await db.any(pgp.helpers.concat(queries));
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
const PostgresFun = require('../../../nodes/Postgres/Postgres.node.functions')
|
||||
const pgPromise = require('pg-promise');
|
||||
|
||||
describe('pgUpdate', () => {
|
||||
it('runs query to update db', async () => {
|
||||
const updateItem = {id: 1234, name: 'test'};
|
||||
const nodeParams = {
|
||||
table: 'mytable',
|
||||
schema: 'myschema',
|
||||
updateKey: 'id',
|
||||
columns: 'id,name'
|
||||
};
|
||||
const getNodeParam = (key) => nodeParams[key];
|
||||
const pgp = pgPromise();
|
||||
const none = jest.fn();
|
||||
const db = {none};
|
||||
|
||||
const items = [
|
||||
{
|
||||
json: updateItem
|
||||
}
|
||||
];
|
||||
|
||||
const results = await PostgresFun.pgUpdate(getNodeParam, pgp, db, items)
|
||||
|
||||
expect(db.none).toHaveBeenCalledWith(`update \"myschema\".\"mytable\" as t set \"id\"=v.\"id\",\"name\"=v.\"name\" from (values(1234,'test')) as v(\"id\",\"name\") WHERE v.id = t.id`);
|
||||
expect(results).toEqual([updateItem]);
|
||||
});
|
||||
|
||||
it('runs query to update db if updateKey is not in columns', async () => {
|
||||
const updateItem = {id: 1234, name: 'test'};
|
||||
const nodeParams = {
|
||||
table: 'mytable',
|
||||
schema: 'myschema',
|
||||
updateKey: 'id',
|
||||
columns: 'name'
|
||||
};
|
||||
const getNodeParam = (key) => nodeParams[key];
|
||||
const pgp = pgPromise();
|
||||
const none = jest.fn();
|
||||
const db = {none};
|
||||
|
||||
const items = [
|
||||
{
|
||||
json: updateItem
|
||||
}
|
||||
];
|
||||
|
||||
const results = await PostgresFun.pgUpdate(getNodeParam, pgp, db, items)
|
||||
|
||||
expect(db.none).toHaveBeenCalledWith(`update \"myschema\".\"mytable\" as t set \"id\"=v.\"id\",\"name\"=v.\"name\" from (values(1234,'test')) as v(\"id\",\"name\") WHERE v.id = t.id`);
|
||||
expect(results).toEqual([updateItem]);
|
||||
});
|
||||
|
||||
it('runs query to update db with cast as updateKey', async () => {
|
||||
const updateItem = {id: '1234', name: 'test'};
|
||||
const nodeParams = {
|
||||
table: 'mytable',
|
||||
schema: 'myschema',
|
||||
updateKey: 'id:uuid',
|
||||
columns: 'name'
|
||||
};
|
||||
const getNodeParam = (key) => nodeParams[key];
|
||||
const pgp = pgPromise();
|
||||
const none = jest.fn();
|
||||
const db = {none};
|
||||
|
||||
const items = [
|
||||
{
|
||||
json: updateItem
|
||||
}
|
||||
];
|
||||
|
||||
const results = await PostgresFun.pgUpdate(getNodeParam, pgp, db, items)
|
||||
|
||||
expect(db.none).toHaveBeenCalledWith(`update \"myschema\".\"mytable\" as t set \"id\"=v.\"id\",\"name\"=v.\"name\" from (values('1234'::uuid,'test')) as v(\"id\",\"name\") WHERE v.id = t.id`);
|
||||
expect(results).toEqual([updateItem]);
|
||||
});
|
||||
|
||||
it('runs query to update db with cast in target columns', async () => {
|
||||
const updateItem = {id: '1234', name: 'test'};
|
||||
const nodeParams = {
|
||||
table: 'mytable',
|
||||
schema: 'myschema',
|
||||
updateKey: 'id',
|
||||
columns: 'id:uuid,name'
|
||||
};
|
||||
const getNodeParam = (key) => nodeParams[key];
|
||||
const pgp = pgPromise();
|
||||
const none = jest.fn();
|
||||
const db = {none};
|
||||
|
||||
const items = [
|
||||
{
|
||||
json: updateItem
|
||||
}
|
||||
];
|
||||
|
||||
const results = await PostgresFun.pgUpdate(getNodeParam, pgp, db, items)
|
||||
|
||||
expect(db.none).toHaveBeenCalledWith(`update \"myschema\".\"mytable\" as t set \"id\"=v.\"id\",\"name\"=v.\"name\" from (values('1234'::uuid,'test')) as v(\"id\",\"name\") WHERE v.id = t.id`);
|
||||
expect(results).toEqual([updateItem]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('pgInsert', () => {
|
||||
it('runs query to insert', async () => {
|
||||
const insertItem = {id: 1234, name: 'test', age: 34};
|
||||
const nodeParams = {
|
||||
table: 'mytable',
|
||||
schema: 'myschema',
|
||||
columns: 'id,name,age',
|
||||
returnFields: '*',
|
||||
};
|
||||
const getNodeParam = (key) => nodeParams[key];
|
||||
const pgp = pgPromise();
|
||||
const manyOrNone = jest.fn();
|
||||
const db = {manyOrNone};
|
||||
|
||||
const items = [
|
||||
{
|
||||
json: insertItem,
|
||||
},
|
||||
];
|
||||
|
||||
const results = await PostgresFun.pgInsert(getNodeParam, pgp, db, items);
|
||||
|
||||
expect(db.manyOrNone).toHaveBeenCalledWith(`insert into \"myschema\".\"mytable\"(\"id\",\"name\",\"age\") values(1234,'test',34) RETURNING *`);
|
||||
expect(results).toEqual([undefined, [insertItem]]);
|
||||
});
|
||||
|
||||
it('runs query to insert with type casting', async () => {
|
||||
const insertItem = {id: 1234, name: 'test', age: 34};
|
||||
const nodeParams = {
|
||||
table: 'mytable',
|
||||
schema: 'myschema',
|
||||
columns: 'id:int,name:text,age',
|
||||
returnFields: '*',
|
||||
};
|
||||
const getNodeParam = (key) => nodeParams[key];
|
||||
const pgp = pgPromise();
|
||||
const manyOrNone = jest.fn();
|
||||
const db = {manyOrNone};
|
||||
|
||||
const items = [
|
||||
{
|
||||
json: insertItem,
|
||||
},
|
||||
];
|
||||
|
||||
const results = await PostgresFun.pgInsert(getNodeParam, pgp, db, items);
|
||||
|
||||
expect(db.manyOrNone).toHaveBeenCalledWith(`insert into \"myschema\".\"mytable\"(\"id\",\"name\",\"age\") values(1234::int,'test'::text,34) RETURNING *`);
|
||||
expect(results).toEqual([undefined, [insertItem]]);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue