On Tue, 5 Mar 2013 02:24:53 -0600
Nico Williams <n...@cryptonector.com> wrote:

> Lies!  :)  There's one more type of UDF in SQLite3: virtual tables.
> You could have a virtual table where selecting with an equality test
> for some column is "passing an argument" to a "table function".  I
> know, I've done this.  But it gets better!  SQLite3 is re-entrant, so
> you can actually format a SQL statement in the virtual table given its
> "arguments" and execute it (being careful to not create a SQL
> injection vulnerability).  IIRC FTS works this way.

Hmm, OK.  I think you're right; this could work, even without parsing
any SQL.  I'm imagining a virtual table like

        CREATE VIRTUAL TABLE output
        USING tfn( 'create table output ...', 'select ...' );

where the function executes the first argument to create a table,
solely to use the metadata to determine the names and types of its own
columns.  (That saves reinventing a column-defintion language.)  Then
it executes the SQL in the second argument, the output of which become
the rows in the table.  It could even be executed recursively (3rd
argument).  

> With a tiny bit of syntactic sugar we could have a way to define table
> functions given simple scalar functions, which would be very nice
> indeed.

I don't know if that's the route; perhaps the good doctor will offer
his views.  

I think of a table function not as a disaggregator -- ick! -- but as a
parameterized view.  Or, really, any function whose output can be
expressed as a relation using any combination of C and SQL.  

Most "real" DBMSs have some form of stored procedure that's just a
named blob of SQL.  Table functions could take the place of stored
procedures in SQLite in a more disciplined way.  That would indeed be
very nice.  

> +1 re: recursive queries.  Once or twice I've resorted to a UNION ALL
> of LEFT OUTER self-JOINs, each sub-query having more and more
> self-joins -- this limits recursion depth effectively, but there's
> going to be a limit anyways.  

I wrote a UDF that executes recursively and returns a string
indicating the number of recursions and total number of rows.  You pass
in the query and target table (because we don't have table functions!)
and then select from it.   That's not as bad as it sounds.   Consider
for example a table "tree" representing a filesystem hierarchy: 

create temporary table t 
        ( p int -- parent
        , c int -- child
        , primary key (c,p)
); 
insert into t 
select * from tree 
where inode in ( 
        select p from t -- "recursion"
        UNION 
        select inode from inodes -- seed
        where filename = 'usr'
)
and not exists (                -- limit
        select 1 from t 
        where p = pnode 
        and c = inode
)

can be invoked as 

$ sqlite3 -list ../inodes.db "create temporary table t (p int, c int,
primary key (c,p)); select recurse('insert into t select tree.* from
tree where inode in (select p from t UNION select inode from inodes
where filename = ''usr'') and not exists (select 1 from t where p =
pnode and c = inode)'); \
select catname(parent_name, filename) \
from inodes where inode in \
        (select c from t) \
order by catname(parent_name, filename);"

17 rows returned in 10 iterations 
catname(parent_name, filename) 
/usr/src/external
/usr/src/external/gpl3
/usr/src/external/gpl3/gcc
/usr/src/external/gpl3/gcc/dist
/usr/src/external/gpl3/gcc/dist/gcc
/usr/src/external/gpl3/gcc/dist/gcc/testsuite
/usr/src/external/gpl3/gcc/dist/gcc/testsuite/gcc.dg
/usr/src/external/gpl3/gcc/dist/gcc/testsuite/gcc.dg/cpp
/usr/src/external/gpl3/gcc/dist/gcc/testsuite/gcc.dg/cpp/usr
/usr/src/gnu
/usr/src/gnu/dist
/usr/src/gnu/dist/gcc4
/usr/src/gnu/dist/gcc4/gcc
/usr/src/gnu/dist/gcc4/gcc/testsuite
/usr/src/gnu/dist/gcc4/gcc/testsuite/gcc.dg
/usr/src/gnu/dist/gcc4/gcc/testsuite/gcc.dg/cpp
/usr/src/gnu/dist/gcc4/gcc/testsuite/gcc.dg/cpp/usr

Unlike a bunch of self-joins, this produces a correct result regardless
of depth; the recurse() function stops when an iteration produces zero
rows.  But it bears the overhead of inserting into another table and
re-executing the query (and retesting the target) on every iteration.
Moving the recursion inside the query engine would avoid all that. 

--jkl


_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to