On Thu, Feb 6, 2014 at 5:17 PM, Wolfgang Winkler
<wolfgang.wink...@digital-concepts.com> wrote:
> Hello!
>
> I've added a datatype "dict" to the list of possible DbiResults. "dicts"
> returns a list of dicts, whereas "dict" returns a dict. The keys for the first
> level is a simple counter. So instead of
>
> {user_id 1 user_name Max} {user_id 2 user_name Lisa}
>
> 1 {user_id 1 user_name Max} 2 {user_id 2 user_name Lisa}
>
> There is a slight performance hit compared to "dicts", but the result is more
> useful, as you can do the following:
>
> dict for {k v} $result {
>         set user_id [dict get $v user_id]
>         dict set  result $k user_url "user?user_id=$user_id"
> }

Hmm, is the above better than the following:

    set rows [list [list c1 v1 c2 v2] [list c1 vv1 c2 vv2]]

    foreach row $rows {
        lappend rowdicts [dict set row c3 "c3=[dict get $row c2]"]
    }

    set rowdicts
    {c1 v1 c2 v2 c3 c3=v2} {c1 vv1 c2 vv2 c3 c3=vv2}

I guess the idea is that 'dict set result k1 k2 v1' is efficient, but
I would guess that the implementation above is about the same, due to
non-obvious dict duplication.

dicts are copy-on-write. If the dict is unshared then no one will see
the mutation so no copy is needed, but in the case above the row-dict
is shared.

dbi_rows returns a list and all the elements in the list have their
ref count incremented to  by it, and they are therefore unshared.

The v in 'dict for {k v} ...' refers to the list which holds the keys
and values of the row, and as v is a new variable passed to the script
block it has it's ref count incremented, which is now 2.

'dict get $v ...' shimmers the list to a dict, ref-count still 2.

'dict set result ...' navigates the outer dict, which was shimmered by
'dict for ...', with refcount hopefully 1, to the inner dict, with
refcount 2, copies it because it won't mutate a shared object, adds
the new key and value to the copy, puts the copy back in the outer
dict.

So, the row is created as a list. The the row is created as a dict. It
is created again as a dict, and the old dict is discarded.

You can see the effect here:

    set rows [list 1 [list c1 v1 c2 v2] 2 [list c1 vv1 c2 vv2]]

    dict for {rownum listydict} $rows {
        puts "(before) rownum: $rownum listydict: $listydict"
        puts "         rows: $rows"

        set c2 [dict get $listydict c2]
        dict set rows $rownum c3 "c3=$c2"

        puts "(after) rownum: $rownum listydict: $listydict"
        puts "         rows: $rows\n"
    }

    (before) rownum: 1 listydict: c1 v1 c2 v2
             rows: 1 {c1 v1 c2 v2} 2 {c1 vv1 c2 vv2}
    (after) rownum: 1 listydict: c1 v1 c2 v2
             rows: 1 {c1 v1 c2 v2 c3 c3=v2} 2 {c1 vv1 c2 vv2}

    (before) rownum: 2 listydict: c1 vv1 c2 vv2
             rows: 1 {c1 v1 c2 v2 c3 c3=v2} 2 {c1 vv1 c2 vv2}
    (after) rownum: 2 listydict: c1 vv1 c2 vv2
             rows: 1 {c1 v1 c2 v2 c3 c3=v2} 2 {c1 vv1 c2 vv2 c3 c3=vv2}

In 'after' the first row of rows has the new column but listydict does
not, even though it was a reference to it at the start of the loop.


How about something more like this:

    set rows [dbi_rows -extend {{c3 c4} {
        set c3 "c3=$c1"
        set c4 "c4=$c2"
    }} {
        select c1, c2 from table limit 2
    }]

    v1 v2 c3=v1 c4=v2 vv1 vv2 c3=vv1 c4=vv2

This is the flat-list version, which has advantages over the array-get
format mentioned in the other thread, but for the purposes of -extend
it doesn't matter.

It could works like this:

After the first row of values has been appended to the rows list, if
-extend was given then that number of empty objects are appended
before starting the next row.

Once all rows have been appended the db handle is released.

Now skip back to the empty objects and run the extend-script to fill
them in (should look closely at how the modern Tcl does this -- looks
like they have some new stuff to prevent variable clobbering).

I think this is an acceptable trick for something like -extend but not
dbi_foreach because there is no expectation you are working on one row
at a time: dbi_rows returns the whole result set. Nested queries here
are the equivalent of serial queries.

Now, you could do the above also with listy-dicts, but how about this:

    dbi_dicts -enumerate 1 $cols $rows

...which would return a list of 2-element lists, the first being
either a 0 or 1 based counter, the second an actual dict composed of
the zipped cols 'n row. All the keys 'n values in the dicts are just
ref-counted from the cols 'n rows, so the cols values will be well
shared. The dicts are created exactly once.

------------------------------------------------------------------------------
Managing the Performance of Cloud-Based Applications
Take advantage of what the Cloud has to offer - Avoid Common Pitfalls.
Read the Whitepaper.
http://pubads.g.doubleclick.net/gampad/clk?id=121051231&iu=/4140/ostg.clktrk
_______________________________________________
naviserver-devel mailing list
naviserver-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/naviserver-devel

Reply via email to