Re: [sqlalchemy] custom Python functions as part of SQL queries

2011-06-01 Thread Michael Bayer
As discussed on IRC, for those reading the list, the difference here is between 
SQLite's ability to embed native functions (and in turn Pysqlite's ability to 
use Python functions in this way), and other databases that don't run as 
embedded libraries. The functions need to be expressed as SQL for all 
databases other than SQLite.   @compiles only deals with SQL rendering, not 
data processing.


On Jun 1, 2011, at 2:15 PM, Tom Kralidis wrote:

> Hi,
> 
> I'm using SQLAlchemy 0.6.6 for a web application.  At this point, the
> app uses SQLite3 as the underlying database; I'm currently working on
> abstracting this out to work with others (PostgreSQL, MySQL).
> 
> At a high level, the application receives CGI queries which are parsed
> and sent to SQLAlchemy to query and filter.  Much of the filtering is
> done against XML data (say dataset.xml) stored in a column (type text)
> in SQLite.
> 
> We use SQLite's create_function approach to pass the XML filter
> queries (basically lxml.etree xpath function wrapper) via (snippet):
> 
>self.session = create_session(engine)
>self.connection = engine.raw_connection()
>self.connection.create_function('query_xpath', 2,
> util.query_xpath)
> 
> Sample use case: a user provides a given XPath in their query which is
> passed to query_xpath(dataset.xml, '/some/xpath') and returns either
> None or the value of the XPath as a string, which would end up being
> the following in SQL:
> 
> "select * from records where query_xpath(xml, '/some/xpath') = 'foo'";
> 
> This has served us very well so far.
> 
> Quite simply, we're looking for a similar way to 'register' Python
> functions so that they can be used in the same manner as above, with
> any database.
> 
> http://www.sqlalchemy.org/docs/core/expression_api.html?highlight=func#sqlalchemy.sql.expression.func
> seems to suggest that custom Python functions can be called in the way
> that we are looking for (i.e. dot separated packages).  Testing this
> out like:
> 
> def foo()
>return 'bar'
> 
> ...
> 
> query = session.query(dataset.xml).filter(func.foo()=='bar')
> 
> sqlalchemy.exc.ProgrammingError: (ProgrammingError) function foo()
> does not exist
> LINE 3: WHERE foo() = E'bar'
>  ^
> HINT:  No function matches the given name and argument types. You
> might need to add explicit type casts.
> 'SELECT records.xml AS records_xml \nFROM records \nWHERE foo() = %
> (foo_1)s' {'foo_1': 'bar'}
> 
> Does this approach only apply to db queries which are set in the
> backend db?
> 
> Alternatively, I'm wondering whether 
> http://www.sqlalchemy.org/docs/core/compiler.html#greatest-function
> serves as a better example to what I want to do.  Testing this out
> like:
> 
> class query_xpath(expression.FunctionElement):
>type = String()
>name = 'query_xpath'
> 
> @compiles(query_xpath)
> def default_query_xpath(element, compiler, **kw):
>return compiler.visit_function(element)
> 
> @compiles(query_xpath, 'sqlite')
> @compiles(query_xpath, 'mysql')
> @compiles(query_xpath, 'postgresql')
> def case_query_xpath(element, compiler, **kw):
>arg1, arg2 = list(element.clauses)
> 
>from server import util
>return util.query_xpath(compiler.process(arg1),
> compiler.process(arg2))
> 
> ...
> 
> query = session.query(query_xpath(dataset.xml, xpath_string))
> 
> print query
> 
> 
> ...which gives me an lxml.etree error saying the XML is not well-
> formed.  Tracing through this, it looks like the column name is passed
> to the function verbatim, instead of being evaluated to the column
> value (I though compiler.process() would do this?)
> 
> I hope I have explained this well enough for some suggestions and / or
> workarounds.
> 
> Thanks
> 
> ..Tom
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> To unsubscribe from this group, send email to 
> sqlalchemy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sqlalchemy?hl=en.
> 

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



[sqlalchemy] custom Python functions as part of SQL queries

2011-06-01 Thread Tom Kralidis
Hi,

I'm using SQLAlchemy 0.6.6 for a web application.  At this point, the
app uses SQLite3 as the underlying database; I'm currently working on
abstracting this out to work with others (PostgreSQL, MySQL).

At a high level, the application receives CGI queries which are parsed
and sent to SQLAlchemy to query and filter.  Much of the filtering is
done against XML data (say dataset.xml) stored in a column (type text)
in SQLite.

We use SQLite's create_function approach to pass the XML filter
queries (basically lxml.etree xpath function wrapper) via (snippet):

self.session = create_session(engine)
self.connection = engine.raw_connection()
self.connection.create_function('query_xpath', 2,
util.query_xpath)

Sample use case: a user provides a given XPath in their query which is
passed to query_xpath(dataset.xml, '/some/xpath') and returns either
None or the value of the XPath as a string, which would end up being
the following in SQL:

"select * from records where query_xpath(xml, '/some/xpath') = 'foo'";

This has served us very well so far.

Quite simply, we're looking for a similar way to 'register' Python
functions so that they can be used in the same manner as above, with
any database.

http://www.sqlalchemy.org/docs/core/expression_api.html?highlight=func#sqlalchemy.sql.expression.func
seems to suggest that custom Python functions can be called in the way
that we are looking for (i.e. dot separated packages).  Testing this
out like:

def foo()
return 'bar'

...

query = session.query(dataset.xml).filter(func.foo()=='bar')

sqlalchemy.exc.ProgrammingError: (ProgrammingError) function foo()
does not exist
LINE 3: WHERE foo() = E'bar'
  ^
HINT:  No function matches the given name and argument types. You
might need to add explicit type casts.
 'SELECT records.xml AS records_xml \nFROM records \nWHERE foo() = %
(foo_1)s' {'foo_1': 'bar'}

Does this approach only apply to db queries which are set in the
backend db?

Alternatively, I'm wondering whether 
http://www.sqlalchemy.org/docs/core/compiler.html#greatest-function
serves as a better example to what I want to do.  Testing this out
like:

class query_xpath(expression.FunctionElement):
type = String()
name = 'query_xpath'

@compiles(query_xpath)
def default_query_xpath(element, compiler, **kw):
return compiler.visit_function(element)

@compiles(query_xpath, 'sqlite')
@compiles(query_xpath, 'mysql')
@compiles(query_xpath, 'postgresql')
def case_query_xpath(element, compiler, **kw):
arg1, arg2 = list(element.clauses)

from server import util
return util.query_xpath(compiler.process(arg1),
compiler.process(arg2))

...

query = session.query(query_xpath(dataset.xml, xpath_string))

print query


...which gives me an lxml.etree error saying the XML is not well-
formed.  Tracing through this, it looks like the column name is passed
to the function verbatim, instead of being evaluated to the column
value (I though compiler.process() would do this?)

I hope I have explained this well enough for some suggestions and / or
workarounds.

Thanks

..Tom

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.