Everytime I deploy a new stored procedure, it bothers me I have to update the middle-layer to allow my front-end code to use the function.
My middle-layer between the front-end code and the database simply translates JSON-RPC requests
to SELECT queries calling stored procedures returning JSON back to the front-end.
This is almost perfect.
But, I still have to update a file where I keep all queries, queries.xml, every time I add a new function.
<query name="Get_Bank_Transaction_Fee"
params="Username, Password, Host, TransactionType, ClearingHouse, BankCode, Amount, Currency"
result="hashref">
SELECT * FROM Get_Bank_Transaction_Fee(?, ?, ?, ?, ?, ?, ?, ?)
</query>
This is a quite nice way to separate the SQL code from the programming code, but since the SQL query consists of
nothing more than a SELECT statement, the name of the stored procedure and the input arguments,
it might as well be automatically generated based on the JSON-RPC request.
I wanted to do CREATE FUNCTION and nothing more before the front-end could start using the new function!
PostgreSQL 9.0 introduced calling functions using named parameters, instead of relying on the position of the arguments.
-- this,
SELECT myfunc(foo := 'Hello', bar := 'World!');
-- is the same thing as,
SELECT myfunc(bar := 'World!', foo := 'Hello');
-- before 9.0 you had to do,
SELECT myfunc('Hello','World!');
-- which means you must not get the positions wrong
This nice feature made it trivial and safe to map a JSON-RPC request to a stored procedure.
The method is the named of the stored procedure and the params is simply a key/value hash,
where each key maps to a named input argument.
The only tricky part which required some usage of pg_catalog.pg_proc was to determine if the function
returned a SET OF or a single row.
I’ve created two perl modules to do the trick, DBIx::Pg::CallFunction and pg_proc_jsonrpc.psgi.
http://search.cpan.org/~joeljac/DBIx-Pg-CallFunction/scripts/pg_proc_jsonrpc.psgi
http://search.cpan.org/~joeljac/DBIx-Pg-CallFunction/lib/DBIx/Pg/CallFunction.pm
Proof-of-concept demonstration:
joel@ubuntu:~$ psql -c 'CREATE OR REPLACE FUNCTION myfunc(foo text, bar text) RETURNS TEXT AS $ SELECT $1 || $2 $$ LANGUAGE sql SECURITY DEFINER'
CREATE FUNCTION
joel@ubuntu:~$ curl 'http://localhost/postgres/myfunc?foo=Hello&bar=World'
{
"error" : null,
"result" : "HelloWorld"
}
joel@ubuntu:~$ curl 'http://localhost/postgres/now'
{
"error" : null,
"result" : "2012-06-03 10:46:23.972644+07"
}
joel@ubuntu:~$ curl 'http://localhost/postgres/random'
{
"error" : null,
"result" : "0.664589043240994"
}
joel@ubuntu:~$
Extract from PostgreSQL log:
2012-06-03 10:46:14.270 ICT,"www-data","joel",3601,"[local]",4fcade06.e11,2,"SELECT",2012-06-03 10:46:14 ICT,2/52,0,LOG,00000,"execute dbdpg_p26924_1: SELECT * FROM myfunc(bar := $1,foo := $2);","parameters: $1 = 'World', $2 = 'Hello'",,,,,,,,"pg_proc_jsonrpc"
2012-06-03 10:46:23.972 ICT,"www-data","joel",3603,"[local]",4fcade0f.e13,2,"SELECT",2012-06-03 10:46:23 ICT,2/58,0,LOG,00000,"execute dbdpg_p27386_2: SELECT * FROM now();",,,,,,,,,"pg_proc_jsonrpc"
2012-06-03 10:46:27.732 ICT,"www-data","joel",3605,"[local]",4fcade13.e15,2,"SELECT",2012-06-03 10:46:27 ICT,2/64,0,LOG,00000,"execute dbdpg_p17663_2: SELECT * FROM random();",,,,,,,,,"pg_proc_jsonrpc"
No more middle-layer hassle!
Well, OK, not yet, this is just a proof-of-concept, a lot of work and testing remains until it can be put into production.
I think this could potentially be a very interesting way to make a PostgreSQL database more accessible to the outside world,
in a transparent and convenient way, not requiring any extra work or restarts.
Imagine front-end developers writing their own stored procedures in PLV8 (Javascript) and accessing them using JQuery (Javascript).
All that’s left is the browser and the database!
Less is more.
That is really nice!
This is interesting but what are the security ramifications?
Brian: The security model relies totally on PostgreSQL. The Apache user is www-data, which normally has no database access. In my example, I’ve granted CONNECT access to the database and EXECUTE rights for the functions which should be accessible by the front-end.
The www-data user cannot do any SELECT, UPDATE, INSERT, DELETE or any other SQL commands.
The functions are created by a different user, which has the necessary SELECT/UPDATE/INSERT/DELETE access. The functions are also declared SECURITY DEFINER, which means they are being run as the user who created them.
When www-data executes a function, it will be run as the other user which created the function.
If there is a security bug in Apache and someone gains access to the front-end machine, they will only be able to connect to the database and execute the functions granted to www-data. I.e., they wouldn’t be able to do much more than what they already can via the JSON-RPC API.
If there is also a security bug in PostgreSQL, then it’s game over.
I think it’s fine to rely on the security of two popular open source projects.
Interesting,I have question if you know other primoamgrng languages is it faster yo use a store procedure or just getting the data a nd filtering it with a primoamgrng languageAnyway, thanks for the post
Cool, and I guess you can make functions which return SETOF to “federate” the database. Interesting stuff.
Great step in the right direction. I think if you guys can give most of the stadrand features of the carts that are out there now (ie CS cart) along with the branding free that would be great. The great thing I like about send studio is the multi user support and admin. Your store suite product should have this as well. What I mean by that is the option to permission customers to use store suite but allow them to have their own domain name without installing anything new. That would be the perfect solution.
trying to install open sis on a sarehd server, it giving me This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) ” error , i also tried to set mysql> SET GLOBAL log_bin_trust_function_creators = 1; it ask for privileges ,also tried manual installation ,but no luck, any suggestions??