PL/Python result object str handler

Started by Peter Eisentrautabout 13 years ago8 messages
#1Peter Eisentraut
peter_e@gmx.net
1 attachment(s)

For debugging PL/Python functions, I'm often tempted to write something
like

rv = plpy.execute(...)
plpy.info(rv)

which prints something unhelpful like

<PLyResult object at 0xb461d8d8>

By implementing a "str" handler for the result object, it now prints
something like

<PLyResult status=5 nrows=2 rows=[{'foo': 1, 'bar': '11'}, {'foo': 2, 'bar': '22'}]>

Patch attached for review.

Attachments:

pg-plpy-result-str.patchtext/x-patch; charset=UTF-8; name=pg-plpy-result-str.patchDownload
diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out
index 3cda958..b07a429 100644
--- a/src/pl/plpython/expected/plpython_spi.out
+++ b/src/pl/plpython/expected/plpython_spi.out
@@ -263,6 +263,24 @@ CONTEXT:  PL/Python function "result_empty_test"
  
 (1 row)
 
+CREATE FUNCTION result_str_test(cmd text) RETURNS text
+AS $$
+plan = plpy.prepare(cmd)
+result = plpy.execute(plan)
+return str(result)
+$$ LANGUAGE plpythonu;
+SELECT result_str_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
+                                   result_str_test                                    
+--------------------------------------------------------------------------------------
+ <PLyResult status=5 nrows=2 rows=[{'foo': 1, 'bar': '11'}, {'foo': 2, 'bar': '22'}]>
+(1 row)
+
+SELECT result_str_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
+           result_str_test            
+--------------------------------------
+ <PLyResult status=4 nrows=0 rows=[]>
+(1 row)
+
 -- cursor objects
 CREATE FUNCTION simple_cursor_test() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
diff --git a/src/pl/plpython/plpy_resultobject.c b/src/pl/plpython/plpy_resultobject.c
index ea93ad7..077bde6 100644
--- a/src/pl/plpython/plpy_resultobject.c
+++ b/src/pl/plpython/plpy_resultobject.c
@@ -22,6 +22,7 @@
 static PyObject *PLy_result_item(PyObject *arg, Py_ssize_t idx);
 static PyObject *PLy_result_slice(PyObject *arg, Py_ssize_t lidx, Py_ssize_t hidx);
 static int	PLy_result_ass_slice(PyObject *arg, Py_ssize_t lidx, Py_ssize_t hidx, PyObject *slice);
+static PyObject *PLy_result_str(PyObject *arg);
 static PyObject *PLy_result_subscript(PyObject *arg, PyObject *item);
 static int	PLy_result_ass_subscript(PyObject *self, PyObject *item, PyObject *value);
 
@@ -74,7 +75,7 @@
 	&PLy_result_as_mapping,		/* tp_as_mapping */
 	0,							/* tp_hash */
 	0,							/* tp_call */
-	0,							/* tp_str */
+	&PLy_result_str,			/* tp_str */
 	0,							/* tp_getattro */
 	0,							/* tp_setattro */
 	0,							/* tp_as_buffer */
@@ -249,6 +250,26 @@
 }
 
 static PyObject *
+PLy_result_str(PyObject *arg)
+{
+	PLyResultObject *ob = (PLyResultObject *) arg;
+
+#if PY_MAJOR_VERSION >= 3
+	return PyUnicode_FromFormat("<%s status=%S nrows=%S rows=%S>",
+								Py_TYPE(ob)->tp_name,
+								ob->status,
+								ob->nrows,
+								ob->rows);
+#else
+	return PyString_FromFormat("<%s status=%ld nrows=%ld rows=%s>",
+							   ob->ob_type->tp_name,
+							   PyInt_AsLong(ob->status),
+							   PyInt_AsLong(ob->nrows),
+							   PyString_AsString(PyObject_Str(ob->rows)));
+#endif
+}
+
+static PyObject *
 PLy_result_subscript(PyObject *arg, PyObject *item)
 {
 	PLyResultObject *ob = (PLyResultObject *) arg;
diff --git a/src/pl/plpython/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql
index 6250e90..7a84473 100644
--- a/src/pl/plpython/sql/plpython_spi.sql
+++ b/src/pl/plpython/sql/plpython_spi.sql
@@ -169,6 +169,16 @@ CREATE FUNCTION result_empty_test() RETURNS void
 
 SELECT result_empty_test();
 
+CREATE FUNCTION result_str_test(cmd text) RETURNS text
+AS $$
+plan = plpy.prepare(cmd)
+result = plpy.execute(plan)
+return str(result)
+$$ LANGUAGE plpythonu;
+
+SELECT result_str_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
+SELECT result_str_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
+
 -- cursor objects
 
 CREATE FUNCTION simple_cursor_test() RETURNS int AS $$
#2Magnus Hagander
magnus@hagander.net
In reply to: Peter Eisentraut (#1)
Re: PL/Python result object str handler

On Tue, Jan 8, 2013 at 3:58 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

For debugging PL/Python functions, I'm often tempted to write something
like

rv = plpy.execute(...)
plpy.info(rv)

which prints something unhelpful like

<PLyResult object at 0xb461d8d8>

By implementing a "str" handler for the result object, it now prints
something like

<PLyResult status=5 nrows=2 rows=[{'foo': 1, 'bar': '11'}, {'foo': 2, 'bar': '22'}]>

Patch attached for review.

How does it work if there are many rows in there? Say the result
contains 10,000 rows - will the string contain all of them? If so,
might it be worthwhile to cap the number of rows shown and then follow
with a "..." or something?

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Daniele Varrazzo
daniele.varrazzo@gmail.com
In reply to: Magnus Hagander (#2)
Re: PL/Python result object str handler

On Tue, Jan 8, 2013 at 9:32 AM, Magnus Hagander <magnus@hagander.net> wrote:

On Tue, Jan 8, 2013 at 3:58 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

For debugging PL/Python functions, I'm often tempted to write something
like

rv = plpy.execute(...)
plpy.info(rv)

which prints something unhelpful like

<PLyResult object at 0xb461d8d8>

By implementing a "str" handler for the result object, it now prints
something like

<PLyResult status=5 nrows=2 rows=[{'foo': 1, 'bar': '11'}, {'foo': 2, 'bar': '22'}]>

This looks more a repr-style format to me (if you implement repr but
not str, the latter will default to the former).

Patch attached for review.

How does it work if there are many rows in there? Say the result
contains 10,000 rows - will the string contain all of them? If so,
might it be worthwhile to cap the number of rows shown and then follow
with a "..." or something?

I think it would: old django versions were a pain in the neck because
when a page broke an entire dump of gigantic queries was often dumped
as debug info.

-- Daniele

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Peter Eisentraut
peter_e@gmx.net
In reply to: Magnus Hagander (#2)
Re: PL/Python result object str handler

On 1/8/13 4:32 AM, Magnus Hagander wrote:

How does it work if there are many rows in there? Say the result
contains 10,000 rows - will the string contain all of them? If so,
might it be worthwhile to cap the number of rows shown and then follow
with a "..." or something?

I don't think so. Any number you pick will be too low for someone.
Since this would only be executed when explicitly asked for, it's up to
the user to manage this. It's analogous to print(long_list) -- you
wouldn't truncate that.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Magnus Hagander
magnus@hagander.net
In reply to: Peter Eisentraut (#4)
Re: PL/Python result object str handler

On Tue, Jan 8, 2013 at 10:23 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 1/8/13 4:32 AM, Magnus Hagander wrote:

How does it work if there are many rows in there? Say the result
contains 10,000 rows - will the string contain all of them? If so,
might it be worthwhile to cap the number of rows shown and then follow
with a "..." or something?

I don't think so. Any number you pick will be too low for someone.
Since this would only be executed when explicitly asked for, it's up to
the user to manage this. It's analogous to print(long_list) -- you
wouldn't truncate that.

Fair enough. I was thinking of a specific example when I wrote that,
bu I can't recall what it was, and clearly using print or the python
console would be the most similar scenarios. And they both do it the
way you suggest. So that's probably the right thing to do.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Peter Eisentraut
peter_e@gmx.net
In reply to: Daniele Varrazzo (#3)
Re: PL/Python result object str handler

On 1/8/13 11:55 AM, Daniele Varrazzo wrote:

<PLyResult status=5 nrows=2 rows=[{'foo': 1, 'bar': '11'}, {'foo': 2, 'bar': '22'}]>

This looks more a repr-style format to me (if you implement repr but
not str, the latter will default to the former).

The repr style was the only guideline I found. There is no guideline
for how str should look like when it's not repr. Do you have a better
suggestion for the output format?

(The reason this is str and not repr is that it doesn't contain other
information such as the tuple descriptor, so str of two different
results could easily be the same.)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Steve Singer
steve@ssinger.info
In reply to: Peter Eisentraut (#1)
Re: PL/Python result object str handler

On 13-01-07 09:58 PM, Peter Eisentraut wrote:

By implementing a "str" handler for the result object, it now prints
something like

<PLyResult status=5 nrows=2 rows=[{'foo': 1, 'bar': '11'}, {'foo': 2, 'bar': '22'}]>

Patch attached for review.

Here is a review:

This patch adds a function that pl/python functions can call to convert
a query result hash into a string suitable for debug purposes. The use
case for this feature is primarily for debugging and logging purposes.
I feel that this is useful since a lot of debugging of stored functions
is usually done with print/elog style debugging.

There already some discussion on the thread as if the number of rows
printed should be limited, the consensus seemed to be 'no' since someone
would be unhappy with any limit and printing everything is the same
behaviour you get with the standard python print.

I've tested this with python2.6 and 3.1 and it seems to work as described.

I've looked through the code and everything looks fine.

The patch includes no documentation. Adding a few lines to the
"Utility Functions" section of the plpython documentation so people know
about this feature would be good.

Other than that I think it is fine to commit. I am setting this as
ready for committer, I assume you'll commit this yourself and that you
can add a paragraph to the docs as you commit it.

Steve

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Peter Eisentraut
peter_e@gmx.net
In reply to: Steve Singer (#7)
Re: PL/Python result object str handler

On Sat, 2013-02-02 at 15:43 -0500, Steve Singer wrote:

I've looked through the code and everything looks fine.

The patch includes no documentation. Adding a few lines to the
"Utility Functions" section of the plpython documentation so people know
about this feature would be good.

Added some documentation and committed. Thanks.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers