On 01/06/2013 12:16 AM, Sean Conner wrote:
> It was thus said that the Great Daniel Gruno once stated:
>> With regards to how :query should work, this could either be done
>> synchronously, wherein all rows are fetched at once, or async where rows
>> are fetched as needed. The sync way is rather easy, but could fill up
>> more memory than needed, while the async has a smaller footprint but
>> proves rather difficult to implement, as the darn dbd driver keeps
>> wanting to free up the result set before it's finished being used
>> (apr_dbd_get_row keeps segfaulting when I request a row that is out of
>> bounds... :( ). Also,there is the consideration of what happens if you
>> query a db, get a result set, close the db handle and try to fetch more
>> rows - this would most likely result in a segfault, as the db handle
>> would have been freed when you try to use it again (how to check that?).
>> Also, getting the number of rows, or even doing: for k, v in pairs(rows)
>> ... proves to be quite difficult with the async method.
>>
>> What I've pieced together so far would work something like this:
>>
>> local results, err = db:query("SELECT .....")
>> it not err then
>>     -- Async method here:
>>     local x = 1
>>     local row = results(x) -- fetch row 1
>>     while row do
>>         ....
>>         x = x + 1
>>         row = results(x) -- fetch row 2,3,4,5...N
>>     end
>>
>>     -- Sync method here:
>>     local rows = results() -- fetch all rows at once
>>     for index, row in pairs(rows) do
>>         ....
>>     end
>> end
> 
>   You could always create an interator for the results and hide the method
> (sync/async) behind it:
> 
>       function db:query(q)
>         ...
>         local function results_async() 
>           local function getnext(state) -- state is the db "object"
>             return db:results_next(state)
>           end
>           return getnext,self
>         end
> 
>         local function results_sync()
>           local row = db:results_all(db)
>           return pairs(row)
>         end
> 
>         ... 
> 
>         return results_async,err
>       end
> 
>       local results, err = db:query("SELECT ... ")
>       if not err then
>         for row in results() do
>           blah_de_blah_blah()
>         end
>       end
> 
> Untested code and all that (so it may very be wrong, but the intent is
> there)
> 
>   -spc
> 
I may have been a tad too hasty with my proposal there. I realized that
the async fetching can be made quite simple via apr_dbd:

local results, err = db:query("SELECT ....")

if not err then
    while true do
        row = results(-1) -- fetch the next row
        if not row then break
        do_stuff()
    end
end

The example you provided could be used in the documentation to provide
methods to get a pairs iterator for both sync and async, so thanks for
that :) One could in fact construct a function that takes async as a
boolean parameter:

function rows(resultset, async)
    local a = 0
    local function getnext()
        a = a + 1
        local row = resultset(-1)
        return row and a or nil, row
    end
    if not async then
        return pairs(resultset(0))
    else
        return getnext, self
    end
end

and then do:

-- async method:
for index, row in rows(resultset, true) do
    ...
end

-- sync method:
for index, row in rows(resultset, false) do -- or just rows(resultset)
    ....
end

Now I just have to take a good look at how to rewrite the prepared
statement stuff, and I believe I'll have a decent proposal ready for
commit :)

With regards,
Daniel.

Reply via email to