NodeJs PostgresQl: Supplied 2 Parameters But Prepared Statement "" Requires 0 - node.js

I have the following function which I call from my controller:
async function insertUser(user_name, icon_link) {
await pool.query("DO $$BEGIN IF EXISTS (SELECT * FROM targets WHERE user_name = $1 ) THEN UPDATE targets SET icon_link = $2 , complaints = complaints + 1; ELSE INSERT INTO targets(user_name, icon_link, complaints) VALUES( $1 , $2 , 0); END IF; END; $$;", data);
}
I get the following error when I try to call it:
const message = name === 'notice' ? new messages_1.NoticeMessage(length, messageValue) : new messages_1.DatabaseError(messageValue, length, name);
^
error: bind message supplies 2 parameters, but prepared statement "" requires 0
at Parser.parseErrorMessage (/Users/user/project/node_modules/pg-protocol/dist/parser.js:287:98)
at Parser.handlePacket (/Users/user/project/node_modules/pg-protocol/dist/parser.js:126:29)
at Parser.parse (/Users/user/project/node_modules/pg-protocol/dist/parser.js:39:38)
at Socket.<anonymous> (/Users/user/project/node_modules/pg-protocol/dist/index.js:11:42)
at Socket.emit (node:events:512:28)
at addChunk (node:internal/streams/readable:324:12)
at readableAddChunk (node:internal/streams/readable:297:9)
at Readable.push (node:internal/streams/readable:234:10)
at TCP.onStreamRead (node:internal/stream_base_commons:190:23) {
This was working fine until it suddenly stopped working for some reason that I cannot understand.
I know the issue is not because of the query itself because the same issue persists on my other queries as well. Also variables are passing on fine, I have checked it while debugging.
I tried passing the parameters like this:
async function insertUser(user_name, icon_link) {
await pool.query({text: 'DO $$BEGIN IF EXISTS (SELECT * FROM targets WHERE user_name = $1 ) THEN UPDATE targets SET icon_link = $2 , complaints = complaints + 1; ELSE INSERT INTO targets(user_name, icon_link, complaints) VALUES( $1 , $2 , 0); END IF; END; $$;', values: [user_name, icon_link]});
}
It didn't work.
I also tried passing them by adding ' to the string parameters:
async function insertUser(user_name, icon_link) {
await pool.query("DO $$BEGIN IF EXISTS (SELECT * FROM targets WHERE user_name = '$1' ) THEN UPDATE targets SET icon_link = '$2' , complaints = complaints + 1; ELSE INSERT INTO targets(user_name, icon_link, complaints) VALUES( '$1' , '$2' , 0); END IF; END; $$;", data);
}
It didn't also work for obvious reasons.
Edit:
I also discovered that If I pass no variables:
const res = await client.query('DO $$ BEGIN IF EXISTS (SELECT * FROM targets WHERE user_name = $1 ) THEN UPDATE targets SET icon_link = $2 , complaints = complaints + 1; ELSE INSERT INTO targets(user_name, icon_link, complaints) VALUES( $1 , $2 , 0); END IF; END $$;');
I get:
error: there is no parameter $1

Formatting the query before hand solves the issue, but I think passing the parameters should be working. You must use ` instead of " for this to work.
return await pool.query(`DO $$ BEGIN IF EXISTS (SELECT * FROM targets WHERE user_name = \'${user_name}\' ) THEN UPDATE targets SET icon_link = \'${icon_link}\' , complaints = complaints + 1; ELSE INSERT INTO targets(user_name, icon_link, complaints) VALUES(\'${user_name}\' , \'${icon_link}\' , 0); END IF; END $$;`);

Related

How to dynamically build Postgres query from API parameters in Nodejs?

I'm looking for a way to dynamically build a SQL query for an unknown number of API parameters that may come back. As a simple example, consider the following query:
router.get("/someEndpoint", authorizationFunction, async (req, res) => {
let sql = `
SELECT *
FROM some_table
WHERE username = $1
${req.query.since !== undefined ? "AND hire_date >= $2" : ""}
`
const results = await pool.query(sql, [req.query.user, req.query.since]);
}
where pool is defined as
const Pool = require("pg").Pool;
const pool = new Pool({<connection parameters>});
The problem I'm having is that if req.query.since is not provided, then the SQL query only requires a single bound parameter ($1). This is presented as an error that says bind message supplies 2 parameters, but prepared statement "" requires 1. Since I don't know which parameters a user will provide until the time of the query, I'm under the impression that I need to provide all possible value, and let the query figure it out.
I've seen a lot of posts that point to pg-promise as a solution, but I'm wondering if that's necessary. Is there a way that I can solve this with my current setup? Perhaps I'm thinking about the problem incorrectly?
Thanks in advance!
Add a trivial expression text that contains $2 and evaluates to true instead of "", for example
SELECT * FROM some_table WHERE username = $1 AND
${req.query.since !== undefined ? " hire_date >= $2": " (true or $2 = $2)"}
The planner will remove it anyway. Added true or just in case $2 is null.
Still it would be cleaner like this
if (req.query.since !== undefined)
{
let sql = `SELECT * FROM some_table WHERE username = $1 AND hire_date >= $2`;
const results = await pool.query(sql, [req.query.user, req.query.since]);
}
else
{
let sql = `SELECT * FROM some_table WHERE username = $1`;
const results = await pool.query(sql, [req.query.user]);
}
What about SQLi risk BTW?

Perl: Can't use string ("XXX") as a HASH ref while "strict refs" in use

I've been working on an old Perl script which stopped working after updating my Perl environment.
This is the script in question (I've added use Data::Dumper; print Dumper \#checks; as suggested in the comments):
#!/usr/bin/perl -w
use warnings;
use strict;
use sort 'stable';
use File::Spec;
use File::Temp qw(tempdir);
use Getopt::Long;
use Nagios::Plugin;
use Nagios::Plugin::Threshold;
my $PROGRAM = 'check_tsm';
my $VERSION = '0.2';
my $default_tsm_dir = '/opt/tivoli/tsm/client/ba/bin';
my $plugin = Nagios::Plugin->new(shortname => $PROGRAM);
my %opt = ('tsm-directory' => $default_tsm_dir);
my #checks;
Getopt::Long::config('bundling');
Getopt::Long::GetOptions(\%opt, 'host|H=s', 'username|U=s', 'password|P=s',
'port|p=i',
'tsm-directory=s', 'warning|w=s', 'critical|c=s', 'bytes', 'help', 'version',
'<>' => sub {
push #checks, {
'type' => $_[0]->{'name'},
'warning' => $opt{'warning'}, #$opt{'warning'} eq '-' ? undef : $opt{'warning'},
'critical' => $opt{'critical'}, #$opt{'critical'} eq '-' ? undef : $opt{'critical'},
};
}) || exit UNKNOWN;
if ($opt{'help'}) {
print "Usage: $0 [OPTION]... CHECK...\n";
}
$plugin->nagios_exit(UNKNOWN, "host not set\n") if !defined $opt{'host'};
$plugin->nagios_exit(UNKNOWN, "username not set\n") if !defined $opt{'username'};
$plugin->nagios_exit(UNKNOWN, "password not set\n") if !defined $opt{'password'};
$plugin->nagios_exit(UNKNOWN, "no check specified\n") if !#checks;
use Data::Dumper; print Dumper \#checks;
foreach my $check (#checks) {
if ($check->{'type'} eq 'drives') {
$check->{'text'} = 'Online drives';
$check->{'query'} = "select count(*) from drives where online='YES'";
$check->{'warning'} //= '2:';
$check->{'critical'} //= '1:';
$check->{'order'} = 0;
} elsif ($check->{'type'} eq 'paths') {
$check->{'text'} = 'Online paths';
$check->{'query'} = "select count(*) from paths where online='YES' and destination_type='DRIVE'";
$check->{'warning'} //= '2:';
$check->{'critical'} //= '1:';
$check->{'order'} = 0;
} elsif ($check->{'type'} eq 'dbspace') {
$check->{'text'} = 'Database space utilization';
$check->{'query'} = "select used_db_space_mb, tot_file_system_mb from db";
$check->{'warning'} //= 90;
$check->{'critical'} //= 95;
$check->{'order'} = 0;
} elsif ($check->{'type'} eq 'logspace') {
$check->{'text'} = 'Log space utilization';
$check->{'query'} = "select used_space_mb, total_space_mb from log";
$check->{'warning'} //= 90;
$check->{'critical'} //= 95;
$check->{'order'} = 0;
} elsif ($check->{'type'} eq 'badvols') {
$check->{'text'} = 'Error or read-only volumes';
#$check->{'query'} = "select count(*) from volumes where error_state='YES' or access='READONLY'";
$check->{'query'} = "select count(*) from volumes where (error_state='YES' and access='READONLY') or access='UNAVAILABLE'";
$check->{'warning'} //= 0;
$check->{'critical'} //= 0;
$check->{'order'} = 0;
} elsif ($check->{'type'} eq 'reclaimvols') {
$check->{'text'} = 'Volumes needing reclamation';
$check->{'query'} = "select count(*) from volumes join stgpools on volumes.stgpool_name=stgpools.stgpool_name where volumes.pct_reclaim>stgpools.reclaim and volumes.status='FULL' and volumes.access='READWRITE'";
$check->{'warning'} //= 50;
$check->{'critical'} //= 100;
$check->{'order'} = 0;
} elsif ($check->{'type'} eq 'freelibvols') {
$check->{'text'} = 'Scratch library volumes';
$check->{'query'} = "select count(*) from libvolumes where status='Scratch'";
$check->{'warning'} //= '5:';
$check->{'critical'} //= '1:';
$check->{'order'} = 0;
} elsif ($check->{'type'} eq 'reqs') {
$check->{'text'} = 'Outstanding requests';
$check->{'query'} = 'query request';
$check->{'warning'} //= 0;
$check->{'critical'} //= 1; # Critical not used since we only return 0 or 1
$check->{'order'} = 1;
} else {
$plugin->nagios_exit(UNKNOWN, "unknown check ".$check->{'type'}."\n");
}
}
# This needs stable sort in order so that reqs checks are always last
#checks = sort { $a->{'order'} <=> $b->{'order'} } #checks;
When I try to run the script I keep on getting this error, no matter which parameter I use (drives, paths, dbspace ...):
/usr/local/nagios/libexec/check_tsm --host=<IP ADDRESS> --port=<TCP PORT> --username=<USER> --password=<PASSWORD> --critical=85 --warning=80 dbspace
Can't use string ("dbspace") as a HASH ref while "strict refs" in use at /usr/local/nagios/libexec/check_tsm.tst line 23.
Line 23 is push #checks, {.
I currently don't understand what the problem is, because before upgrading my Perl version it was working fine.
The issue comes from the line
'type' => $_[0]->{'name'},
$_[0] refers to the first argument of the enclosing subroutine (which starts at '<>' => sub {). According to the documentation of Getopt::Long's <> option, this subroutine is called once per non-option argument of your command line, with this "non-option argument" as its single argument. If you add use Data::Dumper; print Dumper \#_; at the beginning of this subroutine, you'll get as output:
$VAR1 = [
'dbspace'
];
Thus, $_[0] is the string "dbspace", rather than a hash reference. Doing $_[0]->{'name'} makes no sense. Instead, you probably just want to use $_[0]:
push #checks, {
'type' => $_[0],
...
See #shawn's answer to understand why updating Perl broke your script.
#Dada describes the issue, but you're seeing the same code work on an old version and fail on a newer release, which is unusual - why didn't it fail on the old setup too? Here's why:
In Getopt::Long version 2.37, the argument passed to callback functions in argument handlers was changed from a plain string to an object (A blessed hashref in this case), with fields including name. However, in 2.39...
Passing an object as first argument to the callback handler for <> turned out to be a problem in cases where the argument was passed to other modules, e.g., Archive::Tar. Revert the change since the added functionality of the object is not really relevant for the <> callback function.
So your old, working, installation must have been using version 2.37 or 2.38, where the provided code accessing the name field worked fine. 2.39 or newer breaks it (As would 2.36 or older).

How to write migration script for a function in postgresql using a module node-pg-migrate

I am tring to write a migration script using node-pg-migrate module, but not able to succeed in it, as I am getting below error.
Error: Can't get migration files: //dbMigration/migrations/1534615332847_new_test_function.js:11
},'DECLARE
^^^^^^^^^
SyntaxError: Invalid or unexpected token
My migration script is as follows :
exports.shorthands = undefined
exports.up = (pgm) => {
pgm.createFunction('mew_test_function', [
{ mode: 'IN', name: 'soldtocode', type: 'text', default: null }
],
{
return: 'json',
language: 'plpgsql',
replace: true
},'DECLARE
customer_list json;
final_response json;
begin
if not exists(select 1 from company_info where supplier_location_id = soldtocode) then
final_response:=json_build_object("status","fail","message","No Record Found","customer_list", customer_list);
else
SELECT array_to_json(array_agg(row_to_json(t))) FROM
(select distinct ci."b2x_registration_id",ci.supplier_location_id,ci."name",mo.id,mo.name "customerName"
from public.company_info ci
join public.company_oem_mapping com on ci.id = com.company_info_id
join mst_oem mo on mo.id = com.oem_id
and ci.supplier_location_id = soldtocode) t
INTO customer_list;
final_response:=json_build_object("status","pass","message","Record Found Successfully","customer_list", customer_list);
end if;
RETURN final_response;
end
')
}
exports.down = (pgm) => {
pgm.dropFunction('mew_test_function', [
{ mode: 'IN', name: 'soldtocode', type: 'text', default: null }
],{
ifExists : true,
cascade : false
})
}
& below is my actual postgresql function :
CREATE OR REPLACE FUNCTION public.get_customer_name(soldtocode text)
RETURNS json
LANGUAGE plpgsql
AS $function$
DECLARE
customer_list json;
final_response json;
begin
if not exists(select 1 from company_info where supplier_location_id = soldtocode) then
final_response:=json_build_object("status","fail","message","No Record Found","customer_list", customer_list);
else
SELECT array_to_json(array_agg(row_to_json(t))) FROM
(select distinct ci."b2x_registration_id",ci.supplier_location_id,ci."name",mo.id,mo.name "customerName"
from public.company_info ci
join public.company_oem_mapping com on ci.id = com.company_info_id
join mst_oem mo on mo.id = com.oem_id
and ci.supplier_location_id = soldtocode) t
INTO customer_list;
final_response:=json_build_object("status","pass","message","Record Found Successfully","customer_list", customer_list);
end if;
RETURN final_response;
end
$function$
Can anyone give me a proper example of function written in node-pg-migrate module. I am able to write create table & other scripts but it gives issue for adding migrations of functions. Thanks in advance.
Found the workaround to solve this issue. pgm also provides way by which we can directly add raw sql queries into migration file.
find below code, which will be as follows :
exports.shorthands = undefined
exports.up = (pgm) => {
pgm.sql(`CREATE OR REPLACE FUNCTION public.get_customer_name(soldtocode text)
RETURNS json
LANGUAGE plpgsql
AS $function$
DECLARE
customer_list json;
final_response json;
begin
if not exists(select 1 from company_info where supplier_location_id = soldtocode) then
final_response:=json_build_object("status","fail","message","No Record Found","customer_list", customer_list);
else
SELECT array_to_json(array_agg(row_to_json(t))) FROM
(select distinct ci."b2x_registration_id",ci.supplier_location_id,ci."name",mo.id,mo.name "customerName"
from public.company_info ci
join public.company_oem_mapping com on ci.id = com.company_info_id
join mst_oem mo on mo.id = com.oem_id
and ci.supplier_location_id = soldtocode) t
INTO customer_list;
final_response:=json_build_object("status","pass","message","Record Found Successfully","customer_list", customer_list);
end if;
RETURN final_response;
end
$function$`)
}
exports.down = (pgm) => {
pgm.sql(`DROP FUNCTION IF EXISTS public.get_customer_name(soldtocode text)`)
}

Change thread priority ERROR_INVALID_HANDLE

I'm trying to change a thread priority within my script, without success, here are the details.
$thr = threads->new(\&someFunction,
$shared variable 1,
$shared variable 2,
);
I've tried using threads::State;
$thr->priority(2);
Without success
So, I thought the Win32::API must work
my $functionGetLastError= Win32::API->new('Kernel32',
'GetLastError',
'',
'N'
);
my $functionSetThreadPriority= Win32::API->new('Kernel32',
'SetThreadPriority',
'II', # I've tried 'PI' and 'II' as well
'N'
);
my $h = $thr->_handle();
my $success = $functionSetThreadPriority->Call( $h, 2 );
warn "Return Error #".$functionGetLastError->Call() if !$success;
Again, without success: (, but now I have a clue, the script return error number
last Error 6
From MSDN site, System Error Codes (0-499), it seems that the error is
ERROR_INVALID_HANDLE
What am I doing wrong?
$thread->_handle weirdly returns a HANDLE*, while SetThreadPriority expects a HANDLE. You need to dereference the pointer, which you can do as follows:
use constant THREAD_PRIORITY_HIGHEST => 2;
sub SetThreadPriority {
my ($thread, $priority) = #_;
# $thread->_handle() returns a HANDLE*.
my $handle_ptr = $thread->_handle();
my $packed_handle = unpack('P'.HANDLE_SIZE, pack(PTR_FORMAT, $handle_ptr));
my $handle = unpack(HANDLE_FORMAT, $packed_handle);
state $SetThreadPriority = (
Win32::API->new('Kernel32', 'SetThreadPriority', 'Ni', 'i')
or die("Loading SetThreadPriority: $^E\n")
);
return $SetThreadPriority->Call($handle, $priority);
}
Here's the full test program:
use strict;
use warnings;
use feature qw( say state );
use threads;
use threads::shared;
use Carp qw( croak );
use Config qw( %Config );
use Win32::API qw( );
sub uint_format {
$_[0] == 4 ? 'L'
: $_[0] == 8 ? 'Q'
: croak("Unsupported")
}
use constant PTR_SIZE => $Config{ptrsize};
use constant PTR_FORMAT => uint_format(PTR_SIZE);
use constant HANDLE_SIZE => PTR_SIZE;
use constant HANDLE_FORMAT => PTR_FORMAT;
use constant THREAD_PRIORITY_HIGHEST => 2;
sub SetThreadPriority {
my ($thread, $priority) = #_;
# $thread->_handle() returns a HANDLE*.
my $handle_ptr = $thread->_handle();
my $packed_handle = unpack('P'.HANDLE_SIZE, pack(PTR_FORMAT, $handle_ptr));
my $handle = unpack(HANDLE_FORMAT, $packed_handle);
state $SetThreadPriority = (
Win32::API->new('Kernel32', 'SetThreadPriority', 'Ni', 'i')
or die("Loading SetThreadPriority: $^E\n")
);
return $SetThreadPriority->Call($handle, $priority);
}
{
my $done :shared = 0;
my $thread = async {
{ lock($done); cond_wait($done) while !$done; }
};
my $rv = SetThreadPriority($thread, THREAD_PRIORITY_HIGHEST);
say $rv ? "Success" : "Error: $^E";
{ lock($done); $done = 1; cond_broadcast($done); }
$thread->join();
}
Notice that you can use $^E to access GetLastError.
SetThreadPriority($handle, THREAD_PRIORITY_HIGHEST)
or die("SetThreadPriority: $^E\n";
ERROR_INVALID_HANDLE
Which suggests that what _handle returns is not something Win32::API understands. I suspect "P" wants a string buffer not an integer-casted pointer. "I" may be the wrong thing because it's the wrong size on 64-bit, I would try "N" myself.
Also, for future readers running into this issue on Unix: try my POSIX::RT::Scheduler module.

Unable to get value of the property 'split': object is null or undefined

Newbie to HTML here. Using IE9, I'm getting the Error on Page Unable to get value of the property 'split': object is null or undefined. The selected code section is
var retDate = null
var oscriptDateString = form._1_1_56_1.value
if ( oscriptDateString != '?' )
{
var temp = oscriptDateString.split( '/' )
var temp2 = temp[ 3 ].split( ':' ) //Getting Error Here
var yearX = parseInt( temp[ 1 ] )
var monthX = parseInt( temp[ 2 ] ) - 1
var dayX = parseInt( temp2[ 0 ] )
var hourX = parseInt( temp2[ 1 ] )
var minuteX = parseInt( temp2[ 2 ] )
var secondX = parseInt( temp2[ 3 ] )
retDate = new Date( yearX, monthX, dayX, hourX, minuteX, secondX )
}
return retDate
It only returns this error when the field is blank. Otherwise works fine. Am I missing something?
You are reading from a field called _1_1_56_1
var oscriptDateString = form._1_1_56_1.value
You then split this field, and name the result temp
var temp = oscriptDateString.split( '/' )
If your temp is empty/null/undefined, because the above assignment failed (because the field was empty or it contained no slash...
temp[ 3 ].split( ':' )
Then temp here will be undefined. You can't call the fourth element (third position) of nothing.

Resources