Forum OpenACS Q&A: namespace procs and the query dispatcher

Hi all,

Before I go digging into the APM and QD code deeper I thought I'd ask here.

I have some namespace'd procs declared like this:

namespace eval ::logan_apartments {}
namespace eval ::logan_apartments::user {}

ad_proc -public ::logan_apartments::user::new 

In the "new" procedure above, I have a query named "user_insert". My corresponding .xql my queries are being named like this:

<fullquery name="logan_apartments::user::new.user_insert">

However, the QD can't seem to be able to find these queries. I put things in Debug (which became a non-intuitive thing since you have to modify 2 lines of 40-db-query-dispatcher-procs.tcl _and_ turn on debugging in AOLserver) and this is what the QD is telling me:

[26/Jan/2003:19:08:32][5250.163846][-conn1-] Notice: dbinit: sql(localhost::loganapartments): 'begin transaction'
[26/Jan/2003:19:08:32][5250.163846][-conn1-] Debug: calling namespace = logan_apartments::user
[26/Jan/2003:19:08:32][5250.163846][-conn1-] Debug: proc_name is -logan_apartments::user::new-
[26/Jan/2003:19:08:32][5250.163846][-conn1-] Debug: there is no documented proc with name logan_apartments::user::new -- we used default SQL
[26/Jan/2003:19:08:32][5250.163846][-conn1-] Notice: Querying 'abort transaction;'
[26/Jan/2003:19:08:32][5250.163846][-conn1-] Notice: Ns_PgExec: Rolling back transaction
[26/Jan/2003:19:08:32][5250.163846][-conn1-] Notice: dbinit: sql(localhost::loganapartments): 'abort transaction'
[26/Jan/2003:19:08:32][5250.163846][-conn1-] Error: Problem creating a new user: No fullquery for dbqd..NULL and default SQL empty - query for statement missing

So it seems th QD is hitting the point where it does: "if {![nsv_exists api_proc_doc $proc_name]}" and the test is failing.

But the question is why would this test fail? Looking through the logs, there are no errors in the tcl file (this is a -procs file) that could have cause it to not be parsed.

Thanks in advance.

-Roberto

Collapse
Posted by Roberto Mello on
Okay, since nobody replied, I went digging through the code.

It seems the ad_proc code that deals with namespaces is broken. The code does this:

    # (SDW - OpenACS). If proc_name is being defined inside a namespace, we
    # want to use the fully qualified name. Except for actually defining the
    # proc where we want to use the name as passed to us. We always set
    # proc_name_as_passed and conditionally make proc_name fully qualified
    # if we were called from inside a namespace eval.

    set proc_name_as_passed $proc_name
    set proc_namespace [uplevel {::namespace current}]
    if { $proc_namespace != "::" } {
        regsub {^::} $proc_namespace {} proc_namespace
        set proc_name "${proc_namespace}::${proc_name}"
    }

The problem with this is that it relies on a call to uplevel {::namespace current}, which doesn't return correctly when the procedure is not physically within a namespace block.

The problem only happens for procedures declared like this:

namespace eval foo {}

ad_proc -public ::foo::bar {}

In these cases the uplevel call returns "::" as the namespace (instead of ::foo), which screws up ad_proc, and consequently the QD, which relies on the ad_proc caching.

I'm looking into how to fix it.
-Roberto

Collapse
Posted by Dan Wickstrom on
I know ad_proc was modified at one point early on to handle both cases, so unless the changes got backed out somehow, I don't think that's the problem.  I know the templating system and cms have namespaces declared with the second form, and it's not a problem.
Collapse
Posted by Roberto Mello on
Okay,

We (I and Michael Cleverly) kept discussing the problem in IRC until we came up with a simple solution:

    set proc_name_as_passed $proc_name
    set proc_namespace [uplevel {::namespace current}]
    if { $proc_namespace != "::" } {
        regsub {^::} $proc_namespace {} proc_namespace
        set proc_name "${proc_namespace}::${proc_name}"

    } elseif { [string match ::* $proc_name] } {
        #
        # RBM: 2003-01-26: For procs declared like ::foo::bar, we only trim
        #                  the first :: 
        #
        set proc_name [string trimleft $proc_name ::]
    }

I'm committing this to 4.6 and will try to merge with HEAD. I've tested and works fine.

During our adventure through the QD and ad_proc code, we saw several places where code could be made more efficient, like using string map. We're trying to fix some of those things.

-Roberto

Collapse
Posted by Roberto Mello on
No, the templating system and CMS do not have procs declared like the second form. The problem was in ad_proc not handling the proc declaration with leading "::", not in the namespace declaration.

The leading :: tell Tcl that that proc goes into the global namespace. Templating and CMS do not have the leading ::

ad_proc -public template::form { command args } {

So the problem wasn't happening. The leading :: is not required in most cases (there's no occurrence of that form anywhere in the toolkit) but not allowing for it is broken.

That is fixed now.

-Roberto

Collapse
Posted by Dan Wickstrom on
Okay, I see.  I wasn't paying close enough attention.  I thought you were talking about the ::'s in the middle of the fully qualified name.
Collapse
Posted by Tom Jackson on

If the original code wanted the parent namespace, couldn't you use:

set proc_namespace [::namespace parent]
and avoid the uplevel command?
Collapse
Posted by Michael A. Cleverly on
The [namespace parent] will (in the context of ad_proc's call frame) always return the empty string, since ad_proc resides in the global namespace ::, which has no parent.