2023-03-09 09:13:15 -08:00
import type {
IExecuteFunctions ,
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
} from 'n8n-workflow' ;
2023-01-27 03:22:44 -08:00
import { NodeOperationError } from 'n8n-workflow' ;
2020-07-17 07:10:31 -07:00
2022-04-08 14:32:08 -07:00
import pgPromise from 'pg-promise' ;
2020-08-02 23:21:42 -07:00
import {
2021-04-24 13:55:14 -07:00
generateReturning ,
2020-08-02 23:21:42 -07:00
getItemCopy ,
2021-04-24 13:55:14 -07:00
getItemsCopy ,
2020-08-02 23:21:42 -07:00
pgInsert ,
2023-06-22 07:47:28 -07:00
pgQueryV2 ,
2021-04-24 13:55:14 -07:00
pgUpdate ,
2023-04-03 08:18:01 -07:00
} from '../Postgres/v1/genericFunctions' ;
2020-07-17 07:10:31 -07:00
export class CrateDb implements INodeType {
description : INodeTypeDescription = {
displayName : 'CrateDB' ,
name : 'crateDb' ,
2022-06-20 07:54:01 -07:00
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
2020-07-17 07:10:31 -07:00
icon : 'file:cratedb.png' ,
group : [ 'input' ] ,
version : 1 ,
2021-07-03 05:40:16 -07:00
description : 'Add and update data in CrateDB' ,
2020-07-17 07:10:31 -07:00
defaults : {
name : 'CrateDB' ,
} ,
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
credentials : [
{
name : 'crateDb' ,
required : true ,
} ,
] ,
properties : [
{
displayName : 'Operation' ,
name : 'operation' ,
type : 'options' ,
2022-05-20 14:47:24 -07:00
noDataExpression : true ,
2020-07-17 07:10:31 -07:00
options : [
{
name : 'Execute Query' ,
value : 'executeQuery' ,
2020-09-14 00:58:01 -07:00
description : 'Execute an SQL query' ,
2022-07-10 13:50:51 -07:00
action : 'Execute a SQL query' ,
2020-07-17 07:10:31 -07:00
} ,
{
name : 'Insert' ,
value : 'insert' ,
2020-09-14 00:58:01 -07:00
description : 'Insert rows in database' ,
2022-07-10 13:50:51 -07:00
action : 'Insert rows in database' ,
2020-07-17 07:10:31 -07:00
} ,
{
name : 'Update' ,
value : 'update' ,
2020-09-14 00:58:01 -07:00
description : 'Update rows in database' ,
2022-07-10 13:50:51 -07:00
action : 'Update rows in database' ,
2020-07-17 07:10:31 -07:00
} ,
] ,
default : 'insert' ,
} ,
// ----------------------------------
// executeQuery
// ----------------------------------
{
displayName : 'Query' ,
name : 'query' ,
type : 'string' ,
2023-06-22 07:47:28 -07:00
noDataExpression : true ,
2023-04-25 09:18:27 -07:00
typeOptions : {
editor : 'sqlEditor' ,
2023-09-19 03:16:35 -07:00
rows : 5 ,
2023-06-22 07:47:28 -07:00
sqlDialect : 'PostgreSQL' ,
2023-04-25 09:18:27 -07:00
} ,
2020-07-17 07:10:31 -07:00
displayOptions : {
show : {
operation : [ 'executeQuery' ] ,
} ,
} ,
default : '' ,
2021-04-30 15:35:34 -07:00
placeholder : 'SELECT id, name FROM product WHERE quantity > $1 AND price <= $2' ,
2020-07-17 07:10:31 -07:00
required : true ,
2022-08-01 13:47:55 -07:00
description :
'The SQL query to execute. You can use n8n expressions or $1 and $2 in conjunction with query parameters.' ,
2020-07-17 07:10:31 -07:00
} ,
// ----------------------------------
// insert
// ----------------------------------
{
displayName : 'Schema' ,
name : 'schema' ,
type : 'string' ,
displayOptions : {
show : {
operation : [ 'insert' ] ,
} ,
} ,
2020-07-26 05:35:19 -07:00
default : 'doc' ,
2020-07-17 07:10:31 -07:00
required : true ,
description : 'Name of the schema the table belongs to' ,
} ,
{
displayName : 'Table' ,
name : 'table' ,
type : 'string' ,
displayOptions : {
show : {
operation : [ 'insert' ] ,
} ,
} ,
default : '' ,
required : true ,
2022-05-06 14:01:25 -07:00
description : 'Name of the table in which to insert data to' ,
2020-07-17 07:10:31 -07:00
} ,
{
displayName : 'Columns' ,
name : 'columns' ,
type : 'string' ,
displayOptions : {
show : {
operation : [ 'insert' ] ,
} ,
} ,
default : '' ,
placeholder : 'id,name,description' ,
2022-08-01 13:47:55 -07:00
description :
'Comma-separated list of the properties which should used as columns for the new rows' ,
2020-07-17 07:10:31 -07:00
} ,
2021-04-24 13:55:14 -07:00
// ----------------------------------
// update
// ----------------------------------
2020-07-17 07:10:31 -07:00
{
2021-04-24 13:55:14 -07:00
displayName : 'Schema' ,
name : 'schema' ,
2020-07-17 07:10:31 -07:00
type : 'string' ,
displayOptions : {
show : {
2021-04-24 13:55:14 -07:00
operation : [ 'update' ] ,
2020-07-17 07:10:31 -07:00
} ,
} ,
2021-04-24 13:55:14 -07:00
default : 'doc' ,
required : true ,
description : 'Name of the schema the table belongs to' ,
2020-07-17 07:10:31 -07:00
} ,
{
displayName : 'Table' ,
name : 'table' ,
type : 'string' ,
displayOptions : {
show : {
operation : [ 'update' ] ,
} ,
} ,
default : '' ,
required : true ,
description : 'Name of the table in which to update data in' ,
} ,
{
displayName : 'Update Key' ,
name : 'updateKey' ,
type : 'string' ,
displayOptions : {
show : {
operation : [ 'update' ] ,
} ,
} ,
default : 'id' ,
required : true ,
2022-05-06 14:01:25 -07:00
// eslint-disable-next-line n8n-nodes-base/node-param-description-miscased-id
2022-08-01 13:47:55 -07:00
description :
'Comma-separated list of the properties which decides which rows in the database should be updated. Normally that would be "id".' ,
2020-07-17 07:10:31 -07:00
} ,
{
displayName : 'Columns' ,
name : 'columns' ,
type : 'string' ,
displayOptions : {
show : {
operation : [ 'update' ] ,
} ,
} ,
default : '' ,
placeholder : 'name,description' ,
2022-08-01 13:47:55 -07:00
description :
'Comma-separated list of the properties which should used as columns for rows to update' ,
2020-07-17 07:10:31 -07:00
} ,
2021-04-24 13:55:14 -07:00
// ----------------------------------
// insert,update
// ----------------------------------
{
displayName : 'Return Fields' ,
name : 'returnFields' ,
type : 'string' ,
displayOptions : {
show : {
operation : [ 'insert' , 'update' ] ,
} ,
} ,
default : '*' ,
2022-04-22 09:29:51 -07:00
description : 'Comma-separated list of the fields that the operation will return' ,
2021-04-24 13:55:14 -07:00
} ,
// ----------------------------------
// additional fields
// ----------------------------------
{
displayName : 'Additional Fields' ,
name : 'additionalFields' ,
type : 'collection' ,
placeholder : 'Add Field' ,
default : { } ,
options : [
{
displayName : 'Mode' ,
name : 'mode' ,
type : 'options' ,
options : [
{
name : 'Independently' ,
value : 'independently' ,
description : 'Execute each query independently' ,
} ,
{
2022-06-03 10:23:49 -07:00
name : 'Multiple Queries' ,
2021-04-24 13:55:14 -07:00
value : 'multiple' ,
description : '<b>Default</b>. Sends multiple queries at once to database.' ,
} ,
] ,
default : 'multiple' ,
2022-08-01 13:47:55 -07:00
description :
2022-09-29 03:33:16 -07:00
'The way queries should be sent to database. Can be used in conjunction with <b>Continue on Fail</b>. See <a href="https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.cratedb/">the docs</a> for more examples.' ,
2021-04-24 13:55:14 -07:00
} ,
2021-04-30 15:35:34 -07:00
{
displayName : 'Query Parameters' ,
name : 'queryParams' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-01 13:47:55 -07:00
'/operation' : [ 'executeQuery' ] ,
2021-04-30 15:35:34 -07:00
} ,
} ,
default : '' ,
placeholder : 'quantity,price' ,
2022-08-01 13:47:55 -07:00
description :
'Comma-separated list of properties which should be used as query parameters' ,
2021-04-30 15:35:34 -07:00
} ,
2021-04-24 13:55:14 -07:00
] ,
} ,
2020-07-17 07:10:31 -07:00
] ,
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
2021-08-20 09:57:30 -07:00
const credentials = await this . getCredentials ( 'crateDb' ) ;
2020-07-17 07:10:31 -07:00
const pgp = pgPromise ( ) ;
const config = {
host : credentials.host as string ,
port : credentials.port as number ,
database : credentials.database as string ,
user : credentials.user as string ,
password : credentials.password as string ,
ssl : ! [ 'disable' , undefined ] . includes ( credentials . ssl as string | undefined ) ,
sslmode : ( credentials . ssl as string ) || 'disable' ,
} ;
const db = pgp ( config ) ;
2021-04-24 13:55:14 -07:00
let returnItems : INodeExecutionData [ ] = [ ] ;
2020-07-17 07:10:31 -07:00
const items = this . getInputData ( ) ;
2022-12-02 03:53:59 -08:00
const operation = this . getNodeParameter ( 'operation' , 0 ) ;
2020-07-17 07:10:31 -07:00
if ( operation === 'executeQuery' ) {
// ----------------------------------
// executeQuery
// ----------------------------------
2023-06-22 07:47:28 -07:00
const queryResult = await pgQueryV2 . call ( this , pgp , db , items , this . continueOnFail ( ) , {
resolveExpression : true ,
} ) ;
2020-07-17 07:10:31 -07:00
2021-04-24 13:55:14 -07:00
returnItems = this . helpers . returnJsonArray ( queryResult ) ;
2020-07-17 07:10:31 -07:00
} else if ( operation === 'insert' ) {
// ----------------------------------
// insert
// ----------------------------------
2022-08-01 13:47:55 -07:00
const insertData = await pgInsert (
this . getNodeParameter ,
pgp ,
db ,
items ,
this . continueOnFail ( ) ,
) ;
2020-07-17 07:10:31 -07:00
for ( let i = 0 ; i < insertData . length ; i ++ ) {
returnItems . push ( {
2021-04-24 13:55:14 -07:00
json : insertData [ i ] ,
2020-07-17 07:10:31 -07:00
} ) ;
}
} else if ( operation === 'update' ) {
// ----------------------------------
// update
// ----------------------------------
2022-11-18 07:29:44 -08:00
const additionalFields = this . getNodeParameter ( 'additionalFields' , 0 ) ;
2022-08-01 13:47:55 -07:00
const mode = additionalFields . mode ? ? ( 'multiple' as string ) ;
2020-07-31 07:24:43 -07:00
2022-08-01 13:47:55 -07:00
if ( mode === 'independently' ) {
const updateItems = await pgUpdate (
this . getNodeParameter ,
pgp ,
db ,
items ,
this . continueOnFail ( ) ,
) ;
2020-07-31 07:24:43 -07:00
2021-04-24 13:55:14 -07:00
returnItems = this . helpers . returnJsonArray ( updateItems ) ;
2022-08-01 13:47:55 -07:00
} else if ( mode === 'multiple' ) {
2021-04-24 13:55:14 -07:00
// Crate db does not support multiple-update queries
// Therefore we cannot invoke `pgUpdate` using multiple mode
// so we have to call multiple updates manually here
2020-07-31 07:24:43 -07:00
2021-04-24 13:55:14 -07:00
const table = this . getNodeParameter ( 'table' , 0 ) as string ;
const schema = this . getNodeParameter ( 'schema' , 0 ) as string ;
2022-08-01 13:47:55 -07:00
const updateKeys = ( this . getNodeParameter ( 'updateKey' , 0 ) as string )
. split ( ',' )
. map ( ( column ) = > column . trim ( ) ) ;
const columns = ( this . getNodeParameter ( 'columns' , 0 ) as string )
. split ( ',' )
. map ( ( column ) = > column . trim ( ) ) ;
2021-04-24 13:55:14 -07:00
const queryColumns = columns . slice ( ) ;
2020-08-02 12:23:53 -07:00
2022-08-01 13:47:55 -07:00
updateKeys . forEach ( ( updateKey ) = > {
2021-04-24 13:55:14 -07:00
if ( ! queryColumns . includes ( updateKey ) ) {
columns . unshift ( updateKey ) ;
queryColumns . unshift ( '?' + updateKey ) ;
}
} ) ;
2020-07-31 07:24:43 -07:00
2021-04-24 13:55:14 -07:00
const cs = new pgp . helpers . ColumnSet ( queryColumns , { table : { table , schema } } ) ;
2020-08-02 23:21:42 -07:00
2022-08-01 13:47:55 -07:00
const where =
' WHERE ' +
updateKeys
2022-12-29 05:57:20 -08:00
// eslint-disable-next-line n8n-local-rules/no-interpolation-in-regular-string
2022-08-01 13:47:55 -07:00
. map ( ( updateKey ) = > pgp . as . name ( updateKey ) + ' = ${' + updateKey + '}' )
. join ( ' AND ' ) ;
2021-04-24 13:55:14 -07:00
// updateKeyValue = item.json[updateKey] as string | number;
// if (updateKeyValue === undefined) {
// throw new NodeOperationError(this.getNode(), 'No value found for update key!');
// }
2020-08-02 23:21:42 -07:00
2022-08-01 13:47:55 -07:00
const returning = generateReturning (
pgp ,
this . getNodeParameter ( 'returnFields' , 0 ) as string ,
) ;
const queries : string [ ] = [ ] ;
2021-04-24 13:55:14 -07:00
for ( let i = 0 ; i < items . length ; i ++ ) {
const itemCopy = getItemCopy ( items [ i ] , columns ) ;
2022-08-01 13:47:55 -07:00
queries . push (
2023-01-13 09:11:56 -08:00
( pgp . helpers . update ( itemCopy , cs ) as string ) +
pgp . as . format ( where , itemCopy ) +
returning ,
2022-08-01 13:47:55 -07:00
) ;
2021-04-24 13:55:14 -07:00
}
2023-07-12 02:15:38 -07:00
await db . multi ( pgp . helpers . concat ( queries ) ) ;
2022-12-02 12:54:28 -08:00
returnItems = this . helpers . returnJsonArray ( getItemsCopy ( items , columns ) ) ;
2021-04-24 13:55:14 -07:00
}
2020-07-17 07:10:31 -07:00
} else {
2023-10-12 02:10:14 -07:00
await db . $pool . end ( ) ;
2022-08-01 13:47:55 -07:00
throw new NodeOperationError (
this . getNode ( ) ,
` The operation " ${ operation } " is not supported! ` ,
) ;
2020-07-17 07:10:31 -07:00
}
2023-10-12 02:10:14 -07:00
// shuts down the connection pool associated with the db object to allow the process to finish
await db . $pool . end ( ) ;
2020-07-17 07:10:31 -07:00
2023-09-05 03:59:02 -07:00
return [ returnItems ] ;
2020-07-17 07:10:31 -07:00
}
}