Can I use lock from the threads::shared module like a mutex? - multithreading

i'm using AnyEvent to trigger event every X minutes. Some process are long and the result to record in the database are sometimes long to record (more than 3 seconds sometimes). I want to be sure that there is only one function that access to my database the same time. So I use lock from the threads::shared module. Here is my code :
main.pl :
my $var :shared;
$var = 0;
my $w1 = AnyEvent->timer(after => 0, interval => 120, cb => sub {
my #targeted_users = TargetUsers::get_targeted_account_from_followers($dbh,$account,'myrhline');
TargetUsers::save_users_to_follow($dbh,'RH',$var,#targeted_users);
});
my $w2 = AnyEvent->timer(after => 0, interval => 120, cb => sub {
my #targeted_users2 = TargetUsers::get_targeted_account_from_followers($dbh,$account2,'actuel_rh');
TargetUsers::save_users_to_follow($dbh,'RH',$var,#targeted_users2);
});
AnyEvent::Loop::run;
TargetUsers :
sub save_users_to_follow{
my ($dbh,$topic,$var,#targeted_users) = #_;
lock($var); #I call lock($var) to lock the access to the database.
my #array1 = ();
$dbh->{AutoCommit} = 0;
my $sth2 = $dbh->prepare(
"UPDATE People_TO_FOLLOW
SET Pertinence = Pertinence + 1
WHERE Id_user = ?");
my $tuples_update = $sth2->execute_array(
{ ArrayTupleStatus => \my #tuple_status_update },
\#targeted_users,
);
if ($tuples_update) {
print "Successfully updated $tuples_update records\n";
}
else {
for my $tuple_1 (0..$#targeted_users) {
my $status_1 = $tuple_status_update[$tuple_1];
$status_1 = [0, "Skipped"] unless defined $status_1;
next unless ref $status_1;
printf "Failed to update (%s): %s\n",
$targeted_users[$tuple_1], $status_1->[1];
}
}
$sth2->finish();
my $sth = $dbh ->prepare(
"INSERT OR IGNORE INTO People_TO_FOLLOW(Id_user,Topic,Already_followed,Pertinence) VALUES (?,'$topic',0,1)");
my $tuples_insert = $sth->execute_array(
{ ArrayTupleStatus => \my #tuple_status_insert },
\#targeted_users,
);
if ($tuples_insert) {
print "Successfully inserted $tuples_insert records\n";
}
else {
for my $tuple_2 (0..$#targeted_users) {
my $status_2 = $tuple_status_insert[$tuple_2];
$status_2 = [0, "Skipped"] unless defined $status_2;
next unless ref $status_2;
printf "Failed to insert (%s): %s\n",
$targeted_users[$tuple_2], $status_2->[1];
}
}
$sth->finish();
$dbh->commit;
$dbh->{AutoCommit} = 1;
}
But I get this error : "lock can only be used on shared values"
Have you some idea please ? Thanks

You have two vars named $var, the one in main.pl, and the one in save_users_to_follow. The former is the shared variable, but you are trying to lock the latter.
Move the var from main to TargetUsers, or pass a reference to the lock var to save_users_to_follow.

Related

Silverstripe 4 PaginatedList get Page-number - Link (Backlink)

In Silverstripe 4 I have a
DataObject 'PublicationObject',
a 'PublicationPage' and
a 'PublicationPageController'
PublicationObjects are displayed on PublicationPage through looping a PaginatedList. There is also a Pagination, showing the PageNumbers and Prev & Next - Links.
The Detail of PublicationObject is shown as 'Dataobject as Pages' using the UrlSegment.
If users are on the Detail- PublicationObject i want them to get back to the PublicationPage (the paginated list) with a Back - Link.
Two situations:
The user came from PublicationPage
and
The User came from a Search - Engine (Google) directly to the
DataObject- Detail.
I have done this like so:
$parentPage = Page::get()->Filter(array('UrlSegment' => $request->param('pageID')))->First();
$back = $_SERVER['HTTP_REFERER'];
if ((isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER']))) {
if (strtolower(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) != strtolower($_SERVER['HTTP_HOST'])) {
// referer not from the same domain
$back = $parentPage->Link();
}
}
Thats not satisfying.
Question:
How do i get the Pagination - Link ( e.g: ...publicationen?start=20 ) when we are on the Detail - DataObject? How can we find the Position of the current Dataobject in that paginatedList in correlation with the Items per Page? (The Page- Link This Dataobject is on)
<?php
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\View\Requirements;
use SilverStripe\Core\Convert;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\ORM\PaginatedList;
use SilverStripe\Control\Director;
use SilverStripe\ORM\DataObject;
use SilverStripe\ErrorPage\ErrorPage;
use SilverStripe\Dev\Debug;
use SilverStripe\Dev\Backtrace;
class PublicationPageController extends PageController
{
private static $allowed_actions = ['detail'];
private static $url_handlers = array(
);
public static $current_Publication_id;
public function init() {
parent::init();
}
public function detail(HTTPRequest $request)
{
$publication = PublicationObject::get_by_url_segment(Convert::raw2sql($request->param('ID')));
if (!$publication) {
return ErrorPage::response_for(404);
}
// HERE I WANT TO FIND THE POSITION OF THE DATAOBJECT IN THE PAGINATEDLIST OR RATHER THE PAGE - LINK THIS DATAOBJECT IS IN
//$paginatedList = $this->getPaginatedPublicationObjects();
//Debug::show($paginatedList->find('URLSegment', Convert::raw2sql($request->param('ID'))));
//Debug::show($paginatedList->toArray());
$parentPage = Page::get()->Filter(array('UrlSegment' => $request->param('pageID')))->First();
$back = $_SERVER['HTTP_REFERER'];
if ((isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER']))) {
if (strtolower(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) != strtolower($_SERVER['HTTP_HOST'])) {
// referer not from the same domain
$back = $parentPage->Link();
}
}
static::$current_Publication_id = $publication->ID;
$id = $publication->ID;
if($publication){
$arrayData = array (
'Publication' => $publication,
'Back' => $back,
'SubTitle' => $publication->Title,
'MetaTitle' => $publication->Title,
);
return $this->customise($arrayData)->renderWith(array('PublicationDetailPage', 'Page'));
}else{
return $this->httpError(404, "Not Found");
}
}
public function getPaginatedPublicationObjects()
{
$list = $this->PublicationObjects()->sort('SortOrder');
return PaginatedList::create($list, $this->getRequest()); //->setPageLength(4)->setPaginationGetVar('start');
}
}
EDIT:
is there a more simple solution ? than this ? :
public function detail(HTTPRequest $request)
{
$publication = PublicationObject::get_by_url_segment(Convert::raw2sql($request->param('ID')));
if (!$publication) {
return ErrorPage::response_for(404);
}
//*******************************************************
// CREATE BACK - LINK
$paginatedList = $this->getPaginatedPublicationObjects();
$dO = PublicationObject::get();
$paginationVar = $paginatedList->getPaginationGetVar();
$sqlQuery = new SQLSelect();
$sqlQuery->setFrom('PublicationObject');
$sqlQuery->selectField('URLSegment');
$sqlQuery->setOrderBy('SortOrder');
$rawSQL = $sqlQuery->sql($parameters);
$result = $sqlQuery->execute();
$list = [];
foreach($result as $row) {
$list[]['URLSegment'] = $row['URLSegment'];
}
$list = array_chunk($list, $paginatedList->getPageLength(), true);
$start = '';
$back = '';
$i = 0;
$newArray = [];
foreach ($list as $k => $subArr) {
$newArray[$i] = $subArr;
unset($subArr[$k]['URLSegment']);
foreach ($newArray[$i] as $key => $val) {
if ($val['URLSegment'] === Convert::raw2sql($request->param('ID'))) {
$start = '?'.$paginationVar.'='.$i;
}
}
$i = $i + $paginatedList->getPageLength();
}
$back = Controller::join_links($this->Link(), $start);
// END CREATE BACK - LINK
//*****************************************************
static::$current_Publication_id = $publication->ID;
$id = $publication->ID;
if($publication){
$arrayData = array (
'Publication' => $publication,
'Back' => $back,
'MyLinkMode' => 'active',
'SubTitle' => $publication->Title,
'MetaTitle' => $publication->Title,
);
return $this->customise($arrayData)->renderWith(array('PublicationDetailPage', 'Page'));
}else{
return $this->httpError(404, "Not Found");
}
}
You can simply pass the page number as a get var in the URL from the PublicationPage. The detail() method can then grab the get var and if there's a value for the page number passed in, add it to the back link's URL.
In the template:
<% loop $MyPaginatedList %>
Click me
<% end_loop %>
In your controller's detail() method:
$pageNum = $request->getVar('onPage');
if ($pageNum) {
// Add the pagenum to the back link here
}
Edit
You've expressed that you want this to use the page start offset (so you can just plug it into the url using ?start=[offset]), and that you want an example that also covers people coming in from outside your site. I therefore propose the following:
Do as above, but using PageStart instead of CurrentPage - this will mean you don't have to re-compute the offset any time someone clicks the link from your paginated list.
If there is no onPage (or pageOffset as I've renamed it below, assuming you'll use PageStart) get variable, then assuming you can ensure your SortOrder values are all unique, you can check how many items are in the list before the current item - which gives you your item offset. From that you can calculate what page it's on, and what the page offset is for that page, which is your start value.
public function detail(HTTPRequest $request)
{
$publication = PublicationObject::get_by_url_segment(Convert::raw2sql($request->param('ID')));
$paginatedList = $this->getPaginatedPublicationObjects();
// Back link logic starts here
$pageOffset = $request->getVar('pageOffset');
if ($pageOffset === null) {
$recordOffset = $paginatedList->filter('SortOrder:LessThan', $publication->SortOrder)->count() + 1;
$perPage = $paginatedList->getPageLength();
$page = floor($recordOffset / $perPage) + 1;
$pageOffset = ($page - 1) * $perPage;
}
$back = $this->Link() . '?' . $paginatedList->getPaginationGetVar() . '=' . $pageOffset;
// Back link logic ends here
//.....
}

Perl - multithreading on foreach loop:

I have a big code that somewhere in the middle of it, there's a foreach loop filling up some dictionaries. Each iteration is independant and fills those dictionaries with disjoint keys.
I'm trying to turn the "foreach" loop in the middle to multithreaded in order to decrease time.
In the following example, $a1, $b1 are the pointers to dictionaries.
I tried "thread::shared" this way:
my $a1 = {};
my $b1 = {};
my $c1 = {};
my $d1 = {};
# a lot of code using $a1 and $b1
share($a1);
share($b1);
share($c1);
share($d1);
my #threads;
foreach my $Inst ( sort keys %{ $a1->{ports} }) {
push( #threads, threads->create('some_func', $Inst, $a1, $b1, $c1, $d1, $e ...));
}
for my $thr (#threads) {
thr->join();
}
# all the other code
But I get an error of:
Invalid value for shared scalar at ...
Any ideas how to get the data-structures filled, but not that it would interfere with the code before and after the for-each loop?
It is not possible to make a hash shared after it has been created/declared without losing the data in the hash. Instead you could try use shared_clone() like this:
use feature qw(say);
use strict;
use warnings;
use threads ;
use threads::shared ;
use Data::Dumper qw(Dumper);
my %h1 = (a => 1, b => 2);
my %h2 = (c => 3, d => 4);
my $a1 = \%h1;
my $b1 = \%h2;
my $a1c = shared_clone($a1);
my $b1c = shared_clone($b1);
my $lockvar:shared;
my $nthreads = 3;
for ( 1..$nthreads ) {
threads->create('job_to_parallelize', $a1c, $b1c, \$lockvar ) ;
}
$_->join() for threads->list();
sub job_to_parallelize {
my ($a1, $b1, $lockvar) = #_;
{
lock $lockvar;
$a1->{a}++;
$b1->{d}++;
}
}
print Dumper({a1c => $a1c});
print Dumper({b1c => $b1c});
Output:
$VAR1 = {
'a1c' => {
'a' => 4,
'b' => 2
}
};
$VAR1 = {
'b1c' => {
'd' => 7,
'c' => 3
}
};

Why is the following a memory leak? [duplicate]

I've got code that looks like this:
for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
bool isActive = (*i)->update();
//if (!isActive)
// items.remove(*i);
//else
other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);
I'd like remove inactive items immediately after update them, inorder to avoid walking the list again. But if I add the commented-out lines, I get an error when I get to i++: "List iterator not incrementable". I tried some alternates which didn't increment in the for statement, but I couldn't get anything to work.
What's the best way to remove items as you are walking a std::list?
You have to increment the iterator first (with i++) and then remove the previous element (e.g., by using the returned value from i++). You can change the code to a while loop like so:
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i++); // alternatively, i = items.erase(i);
}
else
{
other_code_involving(*i);
++i;
}
}
You want to do:
i= items.erase(i);
That will correctly update the iterator to point to the location after the iterator you removed.
You need to do the combination of Kristo's answer and MSN's:
// Note: Using the pre-increment operator is preferred for iterators because
// there can be a performance gain.
//
// Note: As long as you are iterating from beginning to end, without inserting
// along the way you can safely save end once; otherwise get it at the
// top of each loop.
std::list< item * >::iterator iter = items.begin();
std::list< item * >::iterator end = items.end();
while (iter != end)
{
item * pItem = *iter;
if (pItem->update() == true)
{
other_code_involving(pItem);
++iter;
}
else
{
// BTW, who is deleting pItem, a.k.a. (*iter)?
iter = items.erase(iter);
}
}
Of course, the most efficient and SuperCool® STL savy thing would be something like this:
// This implementation of update executes other_code_involving(Item *) if
// this instance needs updating.
//
// This method returns true if this still needs future updates.
//
bool Item::update(void)
{
if (m_needsUpdates == true)
{
m_needsUpdates = other_code_involving(this);
}
return (m_needsUpdates);
}
// This call does everything the previous loop did!!! (Including the fact
// that it isn't deleting the items that are erased!)
items.remove_if(std::not1(std::mem_fun(&Item::update)));
I have sumup it, here is the three method with example:
1. using while loop
list<int> lst{4, 1, 2, 3, 5};
auto it = lst.begin();
while (it != lst.end()){
if((*it % 2) == 1){
it = lst.erase(it);// erase and go to next
} else{
++it; // go to next
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
2. using remove_if member funtion in list:
list<int> lst{4, 1, 2, 3, 5};
lst.remove_if([](int a){return a % 2 == 1;});
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
3. using std::remove_if funtion combining with erase member function:
list<int> lst{4, 1, 2, 3, 5};
lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
return a % 2 == 1;
}), lst.end());
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
4. using for loop , should note update the iterator:
list<int> lst{4, 1, 2, 3, 5};
for(auto it = lst.begin(); it != lst.end();++it){
if ((*it % 2) == 1){
it = lst.erase(it); erase and go to next(erase will return the next iterator)
--it; // as it will be add again in for, so we go back one step
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
Use std::remove_if algorithm.
Edit:
Work with collections should be like:
prepare collection.
process collection.
Life will be easier if you won't mix this steps.
std::remove_if. or list::remove_if ( if you know that you work with list and not with the TCollection )
std::for_each
The alternative for loop version to Kristo's answer.
You lose some efficiency, you go backwards and then forward again when deleting but in exchange for the extra iterator increment you can have the iterator declared in the loop scope and the code looking a bit cleaner. What to choose depends on priorities of the moment.
The answer was totally out of time, I know...
typedef std::list<item*>::iterator item_iterator;
for(item_iterator i = items.begin(); i != items.end(); ++i)
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i--);
}
else
{
other_code_involving(*i);
}
}
Here's an example using a for loop that iterates the list and increments or revalidates the iterator in the event of an item being removed during traversal of the list.
for(auto i = items.begin(); i != items.end();)
{
if(bool isActive = (*i)->update())
{
other_code_involving(*i);
++i;
}
else
{
i = items.erase(i);
}
}
items.remove_if(CheckItemNotActive);
Removal invalidates only the iterators that point to the elements that are removed.
So in this case after removing *i , i is invalidated and you cannot do increment on it.
What you can do is first save the iterator of element that is to be removed , then increment the iterator and then remove the saved one.
If you think of the std::list like a queue, then you can dequeue and enqueue all the items that you want to keep, but only dequeue (and not enqueue) the item you want to remove. Here's an example where I want to remove 5 from a list containing the numbers 1-10...
std::list<int> myList;
int size = myList.size(); // The size needs to be saved to iterate through the whole thing
for (int i = 0; i < size; ++i)
{
int val = myList.back()
myList.pop_back() // dequeue
if (val != 5)
{
myList.push_front(val) // enqueue if not 5
}
}
myList will now only have numbers 1-4 and 6-10.
Iterating backwards avoids the effect of erasing an element on the remaining elements to be traversed:
typedef list<item*> list_t;
for ( list_t::iterator it = items.end() ; it != items.begin() ; ) {
--it;
bool remove = <determine whether to remove>
if ( remove ) {
items.erase( it );
}
}
PS: see this, e.g., regarding backward iteration.
PS2: I did not thoroughly tested if it handles well erasing elements at the ends.
You can write
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive) {
i = items.erase(i);
} else {
other_code_involving(*i);
i++;
}
}
You can write equivalent code with std::list::remove_if, which is less verbose and more explicit
items.remove_if([] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
});
The std::vector::erase std::remove_if idiom should be used when items is a vector instead of a list to keep compexity at O(n) - or in case you write generic code and items might be a container with no effective way to erase single items (like a vector)
items.erase(std::remove_if(begin(items), end(items), [] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
}));
do while loop, it's flexable and fast and easy to read and write.
auto textRegion = m_pdfTextRegions.begin();
while(textRegion != m_pdfTextRegions.end())
{
if ((*textRegion)->glyphs.empty())
{
m_pdfTextRegions.erase(textRegion);
textRegion = m_pdfTextRegions.begin();
}
else
textRegion++;
}
I'd like to share my method. This method also allows the insertion of the element to the back of the list during iteration
#include <iostream>
#include <list>
int main(int argc, char **argv) {
std::list<int> d;
for (int i = 0; i < 12; ++i) {
d.push_back(i);
}
auto it = d.begin();
int nelem = d.size(); // number of current elements
for (int ielem = 0; ielem < nelem; ++ielem) {
auto &i = *it;
if (i % 2 == 0) {
it = d.erase(it);
} else {
if (i % 3 == 0) {
d.push_back(3*i);
}
++it;
}
}
for (auto i : d) {
std::cout << i << ", ";
}
std::cout << std::endl;
// result should be: 1, 3, 5, 7, 9, 11, 9, 27,
return 0;
}
I think you have a bug there, I code this way:
for (std::list<CAudioChannel *>::iterator itAudioChannel = audioChannels.begin();
itAudioChannel != audioChannels.end(); )
{
CAudioChannel *audioChannel = *itAudioChannel;
std::list<CAudioChannel *>::iterator itCurrentAudioChannel = itAudioChannel;
itAudioChannel++;
if (audioChannel->destroyMe)
{
audioChannels.erase(itCurrentAudioChannel);
delete audioChannel;
continue;
}
audioChannel->Mix(outBuffer, numSamples);
}

How to control the number of Oracle's request in Node.js?

I'm trying to do the following using Node.js and the 'oracledb' node:
Consult Database A to get all bills released on a specific date.
Assign the result to a variable list.
Use the function .map() on list, and inside this function consult Database B to get client's info by a common key, for each item of list.
The problem is: the Database B requests are done all together, so if there's 1000 bills to map, it returns only 100 and treats the rest as error. It is probably related to the number of requests at the same time.
So, given the details, I'd like to know if there's a way to divide the number of requests (e.g. 100 at the time), or any other solution.
ps.: i apologize in advance for my mistakes. I also apologize for not demonstrate on code.
Here's an example of how you can do this by leveraging the new executeMany in v2.2.0 (recently released) and global temporary tables to minimize round trips.
Given these objects:
-- Imagine this is the table you want to select from based on the common keys
create table t (
common_key number,
info varchar2(50)
);
-- Add 10,000 rows with keys 1-10,000 and random data for info
insert into t (common_key, info)
select rownum,
dbms_random.string('p', 50)
from dual
connect by rownum <= 10000;
commit;
-- Create a temp table
create global temporary table temp_t (
common_key number not null
)
on commit delete rows;
The following should work:
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
const startKey = 1000;
const length = 2000;
// Uses a promise to simulate async work.
function getListFromDatabaseA() {
return new Promise((resolve) => {
const list = [];
const count = length - startKey;
for (let x = 0; x < count; x += 1) {
list.push(startKey + x);
}
resolve(list);
});
}
// The list returned from A likely isn't in the right format for executeMany.
function reformatAsBinds(list) {
const binds = [];
for (let x = 0; x < list.length; x += 1) {
binds.push({
key: list[x]
});
}
return binds;
}
async function runTest() {
let conn;
try {
const listFromA = await getListFromDatabaseA();
const binds = reformatAsBinds(listFromA);
conn = await oracledb.getConnection(config);
// Send the keys to the temp table with executeMany for a single round trip.
// The data in the temp table will only be visible to this session and will
// be deleted automatically at the end of the transaction.
await conn.executeMany('insert into temp_t (common_key) values (:key)', binds);
// Now get your common_key and info based on the common keys in the temp table.
let result = await conn.execute(
`select common_key, info
from t
where common_key in (
select common_key
from temp_t
)
order by common_key`
);
console.log('Got ' + result.rows.length + ' rows');
console.log('Showing the first 10 rows');
for (let x = 0; x < 10; x += 1) {
console.log(result.rows[x]);
}
} catch (err) {
console.error(err);
} finally {
if (conn) {
try {
await conn.close();
} catch (err) {
console.error(err);
}
}
}
}
runTest();
After I posted the solution above, I thought I should provide an alternative that keeps the keys going from Node.js to the DB "in memory". You'll have to run tests and review explain plans to see which is the best option for you (will depend on a number of factors).
Given these objects:
-- This is the same as before
create table t (
common_key number,
info varchar2(50)
);
-- Add 10,000 rows with keys 1-10,000 and random data for info
insert into t (common_key, info)
select rownum,
dbms_random.string('p', 50)
from dual
connect by rownum <= 10000;
-- But here, we use a nested table instead of a temp table
create or replace type number_ntt as table of number;
This should work:
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
const startKey = 1000;
const length = 2000;
// Uses a promise to simulate async work.
function getListFromDatabaseA() {
return new Promise((resolve) => {
const list = [];
const count = length - startKey;
for (let x = 0; x < count; x += 1) {
list.push(startKey + x);
}
resolve(list);
});
}
async function runTest() {
let conn;
try {
const listFromA = await getListFromDatabaseA();
const binds = {
keys: {
type: oracledb.NUMBER,
dir: oracledb.BIND_IN,
val: listFromA
},
rs: {
type: oracledb.CURSOR,
dir: oracledb.BIND_OUT
}
};
conn = await oracledb.getConnection(config);
// Now get your common_key and info based on what's in the temp table.
let result = await conn.execute(
`declare
type number_aat is table of number index by pls_integer;
l_keys number_aat;
l_key_tbl number_ntt := number_ntt();
begin
-- Unfortunately, we have to bind in with this data type, but
-- it can't be used as a table...
l_keys := :keys;
-- So we'll transfer the data to another array type that can. This
-- variable's type was created at the schema level so that it could
-- be seen by the SQL engine.
for x in 1 .. l_keys.count
loop
l_key_tbl.extend();
l_key_tbl(l_key_tbl.count) := l_keys(x);
end loop;
open :rs for
select common_key, info
from t
where common_key in (
select column_value
from table(l_key_tbl)
)
order by common_key;
end;`,
binds
);
const resultSet = result.outBinds.rs;
console.log('Showing the first 10 rows');
for (x = 0; x < 10; x += 1) {
let row = await resultSet.getRow();
console.log(row);
}
} catch (err) {
console.error(err);
} finally {
if (conn) {
try {
await conn.close();
} catch (err) {
console.error(err);
}
}
}
}
runTest();
The formatting for the binds was different (a bit simpler here). Also, because I was executing PL/SQL, I needed to have an out bind cursor/result set type.
See this post regarding cardinality with nested tables:
http://www.oracle-developer.net/display.php?id=427
If you try both, please leave some feedback about which worked better.

How to control relays using Raspberry PI and Nodejs?

I am using the watch method from the onoff module to watch to status of the input, the callback function will be called for any change in the input (from 0 to 1 or from 1 to 0) which is what I want.
The problem is when I first run the application if the main is on (inputMain is 1 ) or the main is off (inputMain is 0 ) the callback function is not executed because there is no change in the value of the input.
So if the main is on I can't call the main() function until it becomes off then on, this problem only happens when I first run the application.
How do I get around this ? Is there a better approach to handle relays ?
var GPIO = require('onoff').Gpio,
inputMain = new GPIO(17,'in','both'),
inputGen1 = new GPIO(4,'in','both'),
inputGen2 = new GPIO(27,'in','both'),
inputGen3 = new GPIO(22,'in','both'),
outMain = new GPIO(11,'high'),
outGen1 = new GPIO(15,'high'),
outGen2P = new GPIO(18,'high'), // Generator 2 power
outGen2SM = new GPIO(23,'high'), //Generator 2 starting motor
outGen2 = new GPIO(24,'high'), // Generator 2 contactor
outGen3P = new GPIO(25,'high'),
outGen3SM = new GPIO(8,'high'),
outGen3 = new GPIO(7,'high'),
objects =[outMain,outGen1,outGen2P,outGen2SM,outGen2,outGen3P,outGen3SM,outGen3];
// Checking if there is main or not which is the refrence for all other power sources
inputMain.watch(function(err,state){
if (state)
main(1);
});
// Switch to Main contactor
function main (arg) {
console.log('test');
if (arg == 1) {
value = [0,1,1,1,1,1,1,1];}
else {
value = [0,0,0,0,0,0,0,0];
}
out(value);
}
// Turn on all the relays depending on values of the array ...
function out(value) {
for ( var i = 0; i< value.length; i++) {
if (value[i] == 0 ) {
var a = objects[i];
} else {
objects[i].writeSync(value[i]);
}
}
setTimeout(function() {
a.writeSync(0);
},5000);
}
I'm afraid you will need to call read or readSync for that.
...
outGen3SM = new GPIO(8,'high'),
outGen3 = new GPIO(7,'high'),
objects =[outMain,outGen1,outGen2P,outGen2SM,outGen2,outGen3P,outGen3SM,outGen3];
//Forces the first output update
main(inputMain.readSync());

Resources