mirror of
				https://github.com/psycopg/psycopg2.git
				synced 2025-10-31 15:57:31 +03:00 
			
		
		
		
	Added cursor.cast() method
The method exposes the typecasters lookup algorithm. Useful to create recursive typecasters.
This commit is contained in:
		
							parent
							
								
									f9be48d89e
								
							
						
					
					
						commit
						159cda3688
					
				|  | @ -122,20 +122,6 @@ The ``cursor`` class | ||||||
|         values can be retrieved using |fetch*|_ methods. |         values can be retrieved using |fetch*|_ methods. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     .. method:: mogrify(operation [, parameters]) |  | ||||||
| 
 |  | ||||||
|         Return a query string after arguments binding. The string returned is |  | ||||||
|         exactly the one that would be sent to the database running the |  | ||||||
|         `~cursor.execute()` method or similar. |  | ||||||
| 
 |  | ||||||
|             >>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) |  | ||||||
|             "INSERT INTO test (num, data) VALUES (42, E'bar')" |  | ||||||
| 
 |  | ||||||
|         .. extension:: |  | ||||||
| 
 |  | ||||||
|             The `mogrify()` method is a Psycopg extension to the |DBAPI|. |  | ||||||
| 
 |  | ||||||
|          |  | ||||||
|     .. method:: executemany(operation, seq_of_parameters) |     .. method:: executemany(operation, seq_of_parameters) | ||||||
|        |        | ||||||
|         Prepare a database operation (query or command) and then execute it |         Prepare a database operation (query or command) and then execute it | ||||||
|  | @ -167,6 +153,34 @@ The ``cursor`` class | ||||||
|         does nothing but it is safe to call it. |         does nothing but it is safe to call it. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |     .. method:: mogrify(operation [, parameters]) | ||||||
|  | 
 | ||||||
|  |         Return a query string after arguments binding. The string returned is | ||||||
|  |         exactly the one that would be sent to the database running the | ||||||
|  |         `~cursor.execute()` method or similar. | ||||||
|  | 
 | ||||||
|  |             >>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar')) | ||||||
|  |             "INSERT INTO test (num, data) VALUES (42, E'bar')" | ||||||
|  | 
 | ||||||
|  |         .. extension:: | ||||||
|  | 
 | ||||||
|  |             The `mogrify()` method is a Psycopg extension to the |DBAPI|. | ||||||
|  | 
 | ||||||
|  |     .. method:: cast(oid, s) | ||||||
|  | 
 | ||||||
|  |         Convert a value from the PostgreSQL string representation to a Python | ||||||
|  |         object. | ||||||
|  | 
 | ||||||
|  |         Use the most specific of the typecasters registered by | ||||||
|  |         `~psycopg2.extensions.register_type()`. | ||||||
|  | 
 | ||||||
|  |         .. versionadded:: 2.3.3 | ||||||
|  | 
 | ||||||
|  |         .. extension:: | ||||||
|  | 
 | ||||||
|  |             The `cast()` method is a Psycopg extension to the |DBAPI|. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     .. |fetch*| replace:: `!fetch*()` |     .. |fetch*| replace:: `!fetch*()` | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -84,6 +84,7 @@ typedef struct { | ||||||
| } cursorObject; | } cursorObject; | ||||||
| 
 | 
 | ||||||
| /* C-callable functions in cursor_int.c and cursor_ext.c */ | /* C-callable functions in cursor_int.c and cursor_ext.c */ | ||||||
|  | HIDDEN PyObject *curs_get_cast(cursorObject *self, PyObject *oid); | ||||||
| HIDDEN void curs_reset(cursorObject *self); | HIDDEN void curs_reset(cursorObject *self); | ||||||
| 
 | 
 | ||||||
| /* exception-raising macros */ | /* exception-raising macros */ | ||||||
|  |  | ||||||
|  | @ -32,6 +32,41 @@ | ||||||
| #include "psycopg/psycopg.h" | #include "psycopg/psycopg.h" | ||||||
| #include "psycopg/cursor.h" | #include "psycopg/cursor.h" | ||||||
| #include "psycopg/pqpath.h" | #include "psycopg/pqpath.h" | ||||||
|  | #include "psycopg/typecast.h" | ||||||
|  | 
 | ||||||
|  | /* curs_get_cast - return the type caster for an oid.
 | ||||||
|  |  * | ||||||
|  |  * Return the most specific type caster, from cursor to connection to global. | ||||||
|  |  * If no type caster is found, return the default one. | ||||||
|  |  * | ||||||
|  |  * Return a borrowed reference. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | PyObject * | ||||||
|  | curs_get_cast(cursorObject *self, PyObject *oid) | ||||||
|  | { | ||||||
|  |     PyObject *cast; | ||||||
|  | 
 | ||||||
|  |     /* cursor lookup */ | ||||||
|  |     if (self->string_types != NULL && self->string_types != Py_None) { | ||||||
|  |         cast = PyDict_GetItem(self->string_types, oid); | ||||||
|  |         Dprintf("curs_get_cast:        per-cursor dict: %p", cast); | ||||||
|  |         if (cast) { return cast; } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* connection lookup */ | ||||||
|  |     cast = PyDict_GetItem(self->conn->string_types, oid); | ||||||
|  |     Dprintf("curs_get_cast:        per-connection dict: %p", cast); | ||||||
|  |     if (cast) { return cast; } | ||||||
|  | 
 | ||||||
|  |     /* global lookup */ | ||||||
|  |     cast = PyDict_GetItem(psyco_types, oid); | ||||||
|  |     Dprintf("curs_get_cast:        global dict: %p", cast); | ||||||
|  |     if (cast) { return cast; } | ||||||
|  | 
 | ||||||
|  |     /* fallback */ | ||||||
|  |     return psyco_default_cast; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| /* curs_reset - reset the cursor to a clean state */ | /* curs_reset - reset the cursor to a clean state */ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -629,6 +629,29 @@ psyco_curs_mogrify(cursorObject *self, PyObject *args, PyObject *kwargs) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /* cast method - convert an oid/string into a Python object */ | ||||||
|  | #define psyco_curs_cast_doc \ | ||||||
|  | "cast(oid, s) -> value\n\n" \ | ||||||
|  | "Convert the string s to a Python object according to its oid.\n\n" \ | ||||||
|  | "Look for a typecaster first in the cursor, then in its connection," \ | ||||||
|  | "then in the global register. If no suitable typecaster is found," \ | ||||||
|  | "leave the value as a string." | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | psyco_curs_cast(cursorObject *self, PyObject *args) | ||||||
|  | { | ||||||
|  |     PyObject *oid; | ||||||
|  |     PyObject *s; | ||||||
|  |     PyObject *cast; | ||||||
|  | 
 | ||||||
|  |     if (!PyArg_ParseTuple(args, "OO", &oid, &s)) | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     cast = curs_get_cast(self, oid); | ||||||
|  |     return PyObject_CallFunctionObjArgs(cast, s, (PyObject *)self, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* fetchone method - fetch one row of results */ | /* fetchone method - fetch one row of results */ | ||||||
| 
 | 
 | ||||||
| #define psyco_curs_fetchone_doc \ | #define psyco_curs_fetchone_doc \ | ||||||
|  | @ -1524,6 +1547,8 @@ static struct PyMethodDef cursorObject_methods[] = { | ||||||
|      METH_VARARGS|METH_KEYWORDS, psyco_curs_scroll_doc}, |      METH_VARARGS|METH_KEYWORDS, psyco_curs_scroll_doc}, | ||||||
|     /* psycopg extensions */ |     /* psycopg extensions */ | ||||||
| #ifdef PSYCOPG_EXTENSIONS | #ifdef PSYCOPG_EXTENSIONS | ||||||
|  |     {"cast", (PyCFunction)psyco_curs_cast, | ||||||
|  |      METH_VARARGS, psyco_curs_cast_doc}, | ||||||
|     {"mogrify", (PyCFunction)psyco_curs_mogrify, |     {"mogrify", (PyCFunction)psyco_curs_mogrify, | ||||||
|      METH_VARARGS|METH_KEYWORDS, psyco_curs_mogrify_doc}, |      METH_VARARGS|METH_KEYWORDS, psyco_curs_mogrify_doc}, | ||||||
|     {"copy_from", (PyCFunction)psyco_curs_copy_from, |     {"copy_from", (PyCFunction)psyco_curs_copy_from, | ||||||
|  |  | ||||||
|  | @ -958,19 +958,7 @@ _pq_fetch_tuples(cursorObject *curs) | ||||||
| 
 | 
 | ||||||
|         type = PyInt_FromLong(ftype); |         type = PyInt_FromLong(ftype); | ||||||
|         Dprintf("_pq_fetch_tuples: looking for cast %d:", ftype); |         Dprintf("_pq_fetch_tuples: looking for cast %d:", ftype); | ||||||
|         if (curs->string_types != NULL && curs->string_types != Py_None) { |         cast = curs_get_cast(curs, type); | ||||||
|             cast = PyDict_GetItem(curs->string_types, type); |  | ||||||
|             Dprintf("_pq_fetch_tuples:     per-cursor dict: %p", cast); |  | ||||||
|         } |  | ||||||
|         if (cast == NULL) { |  | ||||||
|             cast = PyDict_GetItem(curs->conn->string_types, type); |  | ||||||
|             Dprintf("_pq_fetch_tuples:     per-connection dict: %p", cast); |  | ||||||
|         } |  | ||||||
|         if (cast == NULL) { |  | ||||||
|             cast = PyDict_GetItem(psyco_types, type); |  | ||||||
|             Dprintf("_pq_fetch_tuples:     global dict: %p", cast); |  | ||||||
|         } |  | ||||||
|         if (cast == NULL) cast = psyco_default_cast; |  | ||||||
| 
 | 
 | ||||||
|         /* else if we got binary tuples and if we got a field that
 |         /* else if we got binary tuples and if we got a field that
 | ||||||
|            is binary use the default cast |            is binary use the default cast | ||||||
|  |  | ||||||
|  | @ -67,6 +67,38 @@ class CursorTests(unittest.TestCase): | ||||||
|         self.assertEqual('SELECT 10.3;', |         self.assertEqual('SELECT 10.3;', | ||||||
|             cur.mogrify("SELECT %s;", (Decimal("10.3"),))) |             cur.mogrify("SELECT %s;", (Decimal("10.3"),))) | ||||||
| 
 | 
 | ||||||
|  |     def test_cast(self): | ||||||
|  |         curs = self.conn.cursor() | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(42, curs.cast(20, '42')) | ||||||
|  |         self.assertAlmostEqual(3.14, curs.cast(700, '3.14')) | ||||||
|  | 
 | ||||||
|  |         try: | ||||||
|  |             from decimal import Decimal | ||||||
|  |         except ImportError: | ||||||
|  |             self.assertAlmostEqual(123.45, curs.cast(1700, '123.45')) | ||||||
|  |         else: | ||||||
|  |             self.assertEqual(Decimal('123.45'), curs.cast(1700, '123.45')) | ||||||
|  | 
 | ||||||
|  |         from datetime import date | ||||||
|  |         self.assertEqual(date(2011,1,2), curs.cast(1082, '2011-01-02')) | ||||||
|  |         self.assertEqual("who am i?", curs.cast(705, 'who am i?'))  # unknown | ||||||
|  | 
 | ||||||
|  |     def test_cast_specificity(self): | ||||||
|  |         curs = self.conn.cursor() | ||||||
|  |         self.assertEqual("foo", curs.cast(705, 'foo')) | ||||||
|  | 
 | ||||||
|  |         D = psycopg2.extensions.new_type((705,), "DOUBLING", lambda v, c: v * 2) | ||||||
|  |         psycopg2.extensions.register_type(D, self.conn) | ||||||
|  |         self.assertEqual("foofoo", curs.cast(705, 'foo')) | ||||||
|  | 
 | ||||||
|  |         T = psycopg2.extensions.new_type((705,), "TREBLING", lambda v, c: v * 3) | ||||||
|  |         psycopg2.extensions.register_type(T, curs) | ||||||
|  |         self.assertEqual("foofoofoo", curs.cast(705, 'foo')) | ||||||
|  | 
 | ||||||
|  |         curs2 = self.conn.cursor() | ||||||
|  |         self.assertEqual("foofoo", curs2.cast(705, 'foo')) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| def test_suite(): | def test_suite(): | ||||||
|     return unittest.TestLoader().loadTestsFromName(__name__) |     return unittest.TestLoader().loadTestsFromName(__name__) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user