Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to insert a record with a uuid array into a pg table using nodejs pg-promise library

i need to have a table in my db which contains a single column which is an array of uuid objects (uuid[] type)

but when i try to insert into it using a nodejs library named pg-promise it fails

i get the following error message telling me i need to rewrite the cast or the expression

{"name":"error","length":206,"severity":"ERROR","code":"42804","hint":"You will need to rewrite or cast the expression.","position":"230","file":"src\\backend\\parse
r\\parse_target.c","line":"510","routine":"transformAssignedExpr"}

this is strange since i have absolutely no issues when i try to enter a single uuid to another column on the same exact table (meaning, i have no issue with representing uuid, btw i create them as a text variables from another lib, but they are plain old text variables)

nor do i have an issue when i try to enter an array of TEXT objects to the same column (in case i change the table to have a TEXT[] column instead of UUID[] column)

here is my code

////////////////

var Promise = require('bluebird');
var pgpLib = require('pg-promise');
var pgp = pgpLib();
var cn = confUtil.pgDbConnectionConfiguration();
var db = pgp(cn);

//////////////////

var newEntity={};
newEntity.hash      = uuid.v4();    
newEntity.location  = {X:2394876,Y:2342342};
newEntity.mother    = uuid.v4();
newEntity.timestamp = Date.now();
newEntity.content   = {content:"blah"};
newEntity.sobList   = [uuid.v4(),uuid.v4(),uuid.v4()];
addEntity (newEntity);

////////////////////

function addEntity(newEntity) {
    var insertEntityQueryPrefix='insert into entities (';
    var insertEntityQueryMiddle=') values (';
    var insertEntityQueryPostfix="";
    var insertEntityQuery="";

    Object.keys(newEntity).forEach(function(key){
        insertEntityQueryPrefix=insertEntityQueryPrefix+'"'+key+'",';
        insertEntityQueryPostfix=insertEntityQueryPostfix+'${'+key+'},';
    });
    insertEntityQueryPrefix=insertEntityQueryPrefix.slice(0,-1);
    insertEntityQueryPostfix=insertEntityQueryPostfix.slice(0,-1)+")";  
    insertEntityQuery=insertEntityQueryPrefix+insertEntityQueryMiddle+insertEntityQueryPostfix;

    //longStoryShort  this is how the query template i used looked like
    /*
        "insert into entities ("hash","location","mother","timestamp","content","sobList") values (${hash},${location},${mother},${timestamp},${content},${sobList})"
    */
    //and this is the parameters object i fed to the query i ran it when it failed
    /*
        {
            "hash": "912f6d85-8b47-4d44-98a2-0bbef3727bbd",
            "location": {
                "X": 2394876,
                "Y": 2342342
            },
            "mother": "87312241-3781-4d7c-bf0b-2159fb6f7f74",
            "timestamp": 1440760511354,
            "content": {
                "content": "bla"
            },
            "sobList": [
                "6f2417e1-b2a0-4e21-8f1d-31e64dea6358",
                "417ade4b-d438-4565-abd3-a546713be194",
                "e4681d92-0c67-4bdf-973f-2c6a900a5fe4"
            ]
        }
    */

    return db.tx(function () {
        var processedInsertEntityQuery = this.any(insertEntityQuery,newEntity);
        return Promise.all([processedInsertEntityQuery])
    })
    .then(
        function (data) {
            return newEntity;
        }, 
        function (reason) {
            throw new Error(reason);
        });
}
like image 826
Tal Avatar asked Sep 26 '22 21:09

Tal


1 Answers

Inserting an array of UUID-s is a special case that requires explicit type casting, because you are passing UUID-s into type uuid[] as an array of text strings.

You need to change your INSERT query: replace ${sobList} with ${sobList}::uuid[]. This will instruct PostgeSQL to convert the array of strings into array of UUID-s.

Unrelated to your question, you do not need to use Promise.all inside db.tx when executing just a single request. You can simply return the result from the insert request:

return this.none(insertEntityQuery,newEntity);

although using a transaction to execute a single request is equally pointless :)

UPDATE

The latest version of pg-promise supports Custom Type Formatting, so you can write your own custom types for query formatting, avoiding explicit type casting.

For your example of using UUID-s inside an array, you can implement your own UUID type:

const UUID = a => ({rawType = true, toPostgres = () => a.v4()});

And for any uuidValue in array or alone, you can use UUID(uuidValue) for automatic formatting.

like image 146
vitaly-t Avatar answered Sep 30 '22 09:09

vitaly-t