On 1/30/19 3:27 PM, Andy Goth wrote:
The next chance I get (probably tomorrow morning), I'll go ahead and add
"step" or "final" as the initial argument to aggregate functions.  I'll
also lift the prohibition on aggregate functions with no arguments.

This change is now committed.

https://chiselapp.com/user/andy/repository/sqlite-andy/info/4fc35d5e09e2a486

I went with a string table approach that can be later expanded to handle
method names for window functions.

oo::class create ListAggregate {
     method step {state args} {
         concat $state $args
     }
     method final {state} {
         my destroy
         return $state
     }
}
db function list -deterministic -aggregate [ListAggregate new]

This approach won't work.  ListAggregate needs to be separately
instantiated for every single group, not just once per database
connection.  Instead, something horrible like this is needed:

oo::class create ListAggregate {
    variable state
    method step {args} {
        lappend state {*}$args
    }
    method final {} {
        return $state
    }
}
db function list -deterministic -aggregate {
    apply {{method obj args} {
        if {$obj eq {}} {
            set obj [ListAggregate new]
        }
        set result [$obj $method {*}$args]
        if {$method eq "final"} {
            $obj destroy
            return $result
        } else {
            return $obj
        }
    }}
}

I don't like this one bit.  There's more logic adapting between the
TclOO implementation and the tclsqlite calling convention than there is
actual work being done.  It's much simpler to just store the state data
in the return value than in an object instantiation.  Nevertheless, this
approach may be more appropriate for complex window functions, so I'm
exploring it rather than dismissing it out of hand.

Let's have a choice of calling conventions, so different-sized tasks can
have more infrastructure if they need it and less if they don't.  By
default, use the simple convention I started with, though augmented with
method name, because why not.  But as an alternative, well... start by
looking at this example:

oo::class create ListAggregate {
    variable state
    method step {args} {
        lappend state {*}$args
    }
    method final {} {
        return $state
    }
}
db function list -deterministic -aggregate -class ListAggregate

The addition of the -class switch changes the script to instead be the
name of a TclOO class, to which I will refer as $class:

1. Before the first invocation of the step function (or final function,
if there are no rows), [$class new] is be called.  Its return value is
saved as the object instance name, referred to below as $obj.

2. For each row, [$obj step ?arg ...?] is called.

3. After the last row, [$obj final] is called, and its return value is
used as the return value of the SQL function.

4. To clean up, [$obj destroy] is called.

If an error occurs in step 1, terminate immediately.  If an error occurs
in steps 2 or 3, go straight to step 4.

You may notice TclOO is not actually required.  Other object systems
such as [incr tcl], XOTcl, Snit, and stooop may be used, though possibly
with wrapper shims, but honestly anybody can write commands that behave
like $class and $obj are expected to behave.  Thus, there is no real
dependency on Tcl 8.6.

[$class new] and [$obj destroy] may call user-defined constructors and
destructors, but while this may be useful to the programmer, it is
internal to TclOO and is not SQLite's concern.

To recap, when -class is not used, the procedure is as follows, where
$script is the script argument to [db function]:

1. For each row, [$script step $state ?arg ...?] is called, where $state
is (first row) empty string or (subsequent rows) the return value of the
previous call to [$script step].

2. After the last row, [$script final $state] is called, and its return
value is used as the return value of the SQL function.  $state is the
return value of the last call to [$script step] or empty string if there
were no rows.

Unless there are comments or objections, I'll try adding -class the next
time I get a chance to hack on this.  I think it may be overkill for
aggregate functions but will probably be useful for window functions.

--
Andy Goth | <andrew.m.goth/at/gmail/dot/com>
_______________________________________________
sqlite-users mailing list
sqlite-users@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to