Forum OpenACS Q&A: Problems with Enhanced-News package

Collapse
Posted by Esti Alvarez on
I'm trying to use the Enhanced-news packge from ACS in my OpenACS v.3.2.4. I downloaded Jon Ellis' patches 60 and 62 to backport the ACS 4 db API, but still doesn't work.

I believe the problem is in the procedures specifically created for this package (i.e. news_get_user_priviledge), call old functions (ad_admistrator_p) that have been redefined and lack the "db" argument they needed before. For example, inside news_get_user_priviledge, ad_administrator_p is called like this:

[ad_administrator_p $user_id]
although as defined in /tcl/ad-admin.tcl it should be like this:
[ad_administrator_p $db $user_id]
I've tried to open by hand the handles inside these functions (news_get_user_priviledge)
set db [ns_db gethandle]
but I run out of handles or says:
Error: dbinit: db handle limit exceeded: thread already owns 1 handle
from pool 'main'
and don't know what to do. Moreover, I think that with the backport of the db API there can be an easier way to make it work than opening and releasing handles everywhere. Any clue?
Collapse
Posted by Jerry Asher on
The straightforward approach would be to allocate your handles from a pool other than main (most likely from subquery, or from a pool you set up for this port.)  https://openacs.org/doc/common-errors.html.

Or you can try the backport of the db API and see what happens.  I imagine though that while the backport works, you still cannot mix code from the two paradigms: your page either needs to use the db api, or not.

Collapse
Posted by Jonathan Ellis on
I've successfully mixed the two db apis in 3.x. You just have to be careful to release handles when stepping between APIs. For instance, when I needed to call ad_permission_p,
    ... 4.x calls ...
    db_release_unused_handles
    set db [ns_db gethandle]
    set admin_p [ad_permission_p $db site_wide]
    ns_db releasehandle $db
    ... back to 4.x API ...
It's a pain, but it's less of a pain than rewriting half the 3.x toolkit.
Collapse
Posted by Jonathan Ellis on
You could easily make a wrapper for this with uplevel so all you had to do was call db_with_3x_wrapper { code }. After adding this,
proc_doc db_with_3x_wrapper { code } {
    executes $code in uplevel, after allocating a db handle named $db
} {
    uplevel "
        db_release_unused_handles
        set db [ns_db gethandle]
        $code
        ns_db releasehandle $db
    "
}
Then my example becomes simply
    db_with_3x_wrapper { set admin_p [ad_permission_p $db site_wide] }
Collapse
Posted by Esti Alvarez on
Jonathan, I have tried using this wrapper you've posted, but it doesn't work:
Error: can't read "db": no such variable
can't read "db": no such variable
I guess it's because the substitution in db takes place before the wrapper is called, when the variable is still not defined.

The previous solution, getting and releasing the handle before ad_administrator_p is called, is fine when I call it directrly from the script, but doesn't work if I do it inside another procedure. For example:

proc_doc news_is_admin_p { user_id news_group_id} { Check if the 
user is a site-wide administrator or an admin for the newsgroup (can 
either be a member of the admin group, or the admin (role) of the 
newsgroup if admin_group_id is null). If yes, then 1 is returned. 
Otherwise, 0 is returned.} {
    # if site-wide admin, returns 1

    db_release_unused_handles
    set db [ns_db gethandle]
    if [ad_administrator_p $db $user_id] {
        return 1
    }
    ns_db releasehandle $db

 
    set admin_list [news_get_admins $news_group_id]
 
    if { [lsearch $admin_list $user_id] != -1 } {
        return 1
    } else {
        return 0
    }
}
shoots the following error:
Error: dbinit: db handle limit exceeded: thread already owns 1 
handle from pool 'main'

and I've tried with diferent pools.

I thought it could work to define a new ad_administrator_p_mine, and substitute ad_administrator_p by ad_administrator_p_mine everywhere in this module:

proc_doc ad_administrator_p_mine {{user_id}} {
    db_release_unused_handles
    set db [ns_db gethandle]
    return [ad_administrator_p $db $user_id]
    ns_db releasehandle $db
}
but again...
Error: dbinit: db handle limit exceeded: thread already owns 1 
handle from pool 'main'
and I've also tried different pools.

I run out of ideas, so what I'm doing, which is working quite well, is porting these functions from the ACS code. For the moment, the functions I need are not very complicated and the database queries are almost similar for oracle and postgres. For example:

proc_doc ad_administrator_p2 { {user_id ""} } {
    Returns 1 if the user is part of the site-wide administration 
group. 0 otherwise.
} {
    if [empty_string_p $user_id] {
        set user_id [ad_verify_and_get_user_id]
    }

    set ad_group_member_p [db_string user_is_group_member {
        select ad_group_member_p(:user_id, group_id) as member_p
        from   user_groups
        where  short_name = 'SWA'
    }]

    return [ad_decode $ad_group_member_p "t" 1 0]
}

But if someone has a better idea, it will be welcome. Thanks for the help

Collapse
Posted by Jerry Asher on
You're touching on a couple of problems. To get the wrapper to work, try the following (note the additional backslash). I haven't tried that myself, but I suspect that will work. (You might want to consider wrapping $code in a catch to ensure you will reach the ns_db releasehandle statement.)
proc_doc db_with_3x_wrapper { code } {
    executes $code in uplevel, after allocating a db handle named $db
} {
    uplevel "
        db_release_unused_handles
        set db [ns_db gethandle]
        $code
        ns_db releasehandle $db
    "
}
Can you say more about what you mean or what you did when you say that you tried different pools? In my experience, I have found the problem where you are asking for a handle from main when one has already been allocated is usually straightforward to correct, once you understand what the error message is telling you. To understand that, I recommend the URL above, as well as the AOLserver documentation for ns_db: http://www.aolserver.com/docs/tcldev/tapi-c44.htm#172554.
Collapse
Posted by Esti Alvarez on
Yes, this backslash works. The backslash didn't appear in your posting. For those who are wondering where it is:

proc_doc db_with_3x_wrapper { code } {
    executes $code in uplevel, after allocating a db handle named $db
} {
    uplevel "
        db_release_unused_handles
        set db [ns_db gethandle]
        $code
        ns_db releasehandle $db
    "
}

I'm still trying to make it work on if statements inside some procedures. For example:

proc news_is_admin_p { user_id news_group_id} {
    db_with_3x_wrapper {
      if [ad_administrator_p $db $user_id] {
        return 1
      }
    }

    ...
}

leaves the handle open since the return goes before the releasehandle.

To your question of what I meant when I said I tried with different polls, I mean that instead of set db [ns_db gethandle] y tried set db [ns_db gethandle subquery] or even new one I set up in nsd.tcl. That's what you meant yesterday when you said I should allocate my handles in another pool, isn't it?

Collapse
Posted by Esti Alvarez on
The backslash disappeared again, although I saw it in the preview!! Ok, it's preceeding $db in the releasehandle line.
Collapse
Posted by Jerry Asher on
Yes, Esti, you're absolutely right.  Thanks for the sharp eye.  That has happened to me twice now in the past three days, and I've ended up wondering if it was me or not.  Okay, lithium down, bug report up.
Collapse
Posted by Jerry Asher on
So exactly how did you try different pools, and what happened?  I ask because applying different pools should almost certainly work in your situation.
Collapse
Posted by Jonathan Ellis on
Sorry about that. Yes, the bboard has been chewing up backslashes for some time. This problem has been fixed and posted about but somewhat frustratingly the patch hasn't been applied here at openacs.org yet.
Collapse
Posted by Jerry Asher on
Ah, but what's especially cheshire about how the bug is expressed, what's not made clear in the bug reports (I've already incorporated that patch in my systems) is how the backslash vanishes slowly, appearing in the preview and then fading away in the final posting.
Collapse
Posted by Jonathan Ellis on
Yes, that does make it more fun, doesn't it? :)
Collapse
Posted by Don Baccus on
For folks using 4.x bboards backslashes won't be a problem, I escape them in the Postgres driver's bind variable emulation routine,,,
Collapse
Posted by Esti Alvarez on
The backslash thing seems to be fixed, so I'll go back to the initial theme: the pending handles 😟

Since I'm still getting server log errors of the type Error: could not allocate 1 handle from pool "whatever", I'm testing this wrapper via nscp:

1>db_with_3x_wrapper {set member_p [ad_user_group_member $db 1 14]}

2>return $set_member_p
0
3>b_with_3x_wrapper {set member_p [ad_user_group_member $db 1 1]}
could not allocate 1 handle from pool "whatever"

The same happens with this other example I yesterday thought was working well

db_with_3x_wrapper { set is_admin_p [ad_administrator_p $db 1]}

So the previous handle has somehow not been released!! I think the problem is something I mentioned yesterday: the output of ad_user_group_member (for this particular case) is a return, so the wrapper is left before the ns_db releasehandle $db is reached.
Am I right?
Is there any way to fix it?
Shouldn't the db_release_unused_handles line in db_with_3x_wrapper release this handle before trying to get a new one?

Collapse
Posted by Jonathan Ellis on
I'm not really following you... Could you give an example of a (small) script that breaks for you? For instance, this works fine for me:
ReturnHeaders

db_string 1_q "select 1"
db_with_3x_wrapper {set member_p [ad_user_group_member $db 1 14]}
db_with_3x_wrapper {set member_p [ad_user_group_member $db 1 1]}
db_string 2_q "select 2"

ns_write "done"
Collapse
Posted by Esti Alvarez on
This is something that looks pretty much like the procedure I want to make work:
proc news_get_user_priviledge {user_id } {
    db_with_3x_wrapper {
        if [ad_administrator_p $db $user_id] {
            return "Post"
        }
    }
 
    set priviledge ""
    db_foreach check_priviledge "
    select group_id
    from newsgroups
    where enabled_p = 't'" {
        db_with_3x_wrapper { 
          set member_p [ad_user_group_member $db $group_id $user_id]
	  }
        if $member_p {
            set priviledge "Suggest"
        }
    }
    return $priviledge
}

ReturnHeaders
 
set result [news_get_user_priviledge2 1]
 
ns_write "$result"

Works for user_id > 1 but user_id=1 returns:

Error: could not allocate 1 handle from pool "whatever"
could not allocate 1 handle from pool "whatever"
    while executing
"ns_db gethandle whatever"
    (procedure "db_with_3x_wrapper" line 4)
    invoked from within
"db_with_3x_wrapper {
          set member_p [ad_user_group_member $db $group_id $user_id]
          }"
    ("uplevel" body line 2)
    invoked from within
"uplevel 1 $code_block "
    ("1" arm line 1)
    invoked from within
"switch $errno {
                0 {
                    # TCL_OK
                }
                1 {
                    # TCL_ERROR
                    global errorInfo errorCode
                    error $error $errorInfo $errorCode
                }
                2 {
        ..."
    ("while" body line 20)
    invoked from within
"while { [db_getrow $db $selection] } {
            incr counter
            if { [info exists array_val] } {
                unset array_val
            }
            if { ![info exists column..."
    ("uplevel" body line 5)
    invoked from within
"uplevel 1 $code_block "
    invoked from within
"db_with_handle db {
        set selection [db_exec select $db $statement_name $sql]

        set counter 0
        while { [db_getrow $db $selection] } {
            incr counter..."
    (procedure "db_foreach" line 32)
    invoked from within
"db_foreach check_priviledge "
    select group_id
    from newsgroups
    where enabled_p = 't'" {
        db_with_3x_wrapper {
          set member_..."
    (procedure "news_get_user_priviledge" line 9)
    invoked from within
"news_get_user_priviledge 1"
    invoked from within
"set done [news_get_user_priviledge 1]"
    (file "/web/hezkuntza/www/enhanced-news/example2.tcl" line 27)
    invoked from within
"source $script"
    (procedure "ns_sourceproc" line 6)
    invoked from within
"ns_sourceproc cns1 {}"
Collapse
Posted by Jonathan Ellis on
You have two problems here.  First, as you suspected, the "return" inside the db_with_3x_wrapper means it never gets to the ns_db releasehandle inside db_with_3x.  You could get around this easily by setting a variable inside the db_with_3x and doing your return later.

Second, you later on have a db_with_3x_wrapper nested inside a db_foreach, which is a Bad Thing.  You'll have to do some rewriting on that one.  (Probably the "correct" thing to do is not use ad_user_group_member at all; nesting queries like that is inefficient and usually a bad idea.  Creating a new proc ad_user_newsgroup_member_p that does it in one query would probably be better; you may or may not be able to refactor some code from ad_user_group_member.)

Collapse
Posted by Esti Alvarez on
Yes, that's exactly what I thought. Ok, I'll try defining a new
function. Thank you!!!
Collapse
Posted by Esti Alvarez on

I made this enhanced-news package work for OpenACS-3.2.x. I ported the sql queries from Oracle to Postgres and made some fixes so that it could work with the backport of ACS4 db API. The package I now use is highly customized to our needs, but I saved a raw ported version to offer it to the community. Now, 4 months later (already!), I realize I haven't done it yet.

Is there somebody interested in getting this package? Basically it is the news package with the possibility of defining different groups with independent approval policies and administrators. If so, how should I provide it? Upload in the file storage, send it to someone...