Xavier (Open ERP) has proposed merging 
lp:~openerp-dev/openobject-server/trunk-improve-get-pg-types-xmo into 
lp:openobject-server.

Requested reviews:
  OpenERP Core Team (openerp)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-improve-get-pg-types-xmo/+merge/74831

* Extracted creation of VARCHAR pg_type into a separate function, make missing 
size (or size 0) create an unlimited VARCHAR field (effectively limited by 
postgres to 1GB data)
* Extracted fields to pg_types mapping outside of get_pg_type
* Made fields.function recursively forward to get_pg_type (via a type overload) 
instead of reimplementing half get_pg_type in itself
* Simplified some get_pg_type cases

Note: if this is merged, it might be nice to convert fields.selection to use an 
API similar to fields.function: default to VARCHAR storage, if there's a type 
attribute override use that type. Currently, fields.selection is handled the 
following way:
* If the selection is a list and the type of the first half of the first item 
is an integer, use type int4
* If the field has a __size=-1__ attribute, use type int4
* Else use type varchar (with size specified on the field, if any)

One change from previous version is that if type of the first half of the first 
item of the selection was str or unicode, it tried to find the longest string 
and used that as the field's size. This meant silent loss of data if new, 
longer items were added to the selection without recreating the whole db (or at 
least manually altering the relevant fields). It also used the field's size or 
*16* as a minimum default, for some reason, and if there was no size specified 
on the selection (or size=0) it just hardcoded the size to 16.
-- 
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-improve-get-pg-types-xmo/+merge/74831
Your team OpenERP R&D Team is subscribed to branch 
lp:~openerp-dev/openobject-server/trunk-improve-get-pg-types-xmo.
=== modified file 'openerp/osv/orm.py'
--- openerp/osv/orm.py	2011-08-31 09:45:47 +0000
+++ openerp/osv/orm.py	2011-09-09 16:46:22 +0000
@@ -475,62 +475,64 @@
 
     __repr__ = __str__
 
-
-def get_pg_type(f):
-    """
-    returns a tuple
-    (type returned by postgres when the column was created, type expression to create the column)
-    """
-
-    type_dict = {
-            fields.boolean: 'bool',
-            fields.integer: 'int4',
-            fields.integer_big: 'int8',
-            fields.text: 'text',
-            fields.date: 'date',
-            fields.time: 'time',
-            fields.datetime: 'timestamp',
-            fields.binary: 'bytea',
-            fields.many2one: 'int4',
-            }
-    if type(f) in type_dict:
-        f_type = (type_dict[type(f)], type_dict[type(f)])
-    elif isinstance(f, fields.float):
-        if f.digits:
-            f_type = ('numeric', 'NUMERIC')
-        else:
-            f_type = ('float8', 'DOUBLE PRECISION')
-    elif isinstance(f, (fields.char, fields.reference)):
-        f_type = ('varchar', 'VARCHAR(%d)' % (f.size,))
-    elif isinstance(f, fields.selection):
-        if isinstance(f.selection, list) and isinstance(f.selection[0][0], (str, unicode)):
-            f_size = reduce(lambda x, y: max(x, len(y[0])), f.selection, f.size or 16)
-        elif isinstance(f.selection, list) and isinstance(f.selection[0][0], int):
-            f_size = -1
-        else:
-            f_size = getattr(f, 'size', None) or 16
-
-        if f_size == -1:
-            f_type = ('int4', 'INTEGER')
-        else:
-            f_type = ('varchar', 'VARCHAR(%d)' % f_size)
-    elif isinstance(f, fields.function) and eval('fields.'+(f._type), globals()) in type_dict:
-        t = eval('fields.'+(f._type), globals())
-        f_type = (type_dict[t], type_dict[t])
-    elif isinstance(f, fields.function) and f._type == 'float':
-        if f.digits:
-            f_type = ('numeric', 'NUMERIC')
-        else:
-            f_type = ('float8', 'DOUBLE PRECISION')
-    elif isinstance(f, fields.function) and f._type == 'selection':
-        f_type = ('text', 'text')
-    elif isinstance(f, fields.function) and f._type == 'char':
-        f_type = ('varchar', 'VARCHAR(%d)' % (f.size))
-    else:
-        logger = netsvc.Logger()
-        logger.notifyChannel("init", netsvc.LOG_WARNING, '%s type not supported!' % (type(f)))
-        f_type = None
-    return f_type
+def pg_varchar(size=0):
+    """ Returns the VARCHAR declaration for the provided size:
+
+    * If no size (or an empty size is provided) return an 'infinite' VARCHAR
+    * Otherwise return a VARCHAR(n)
+
+    :type int size: varchar size, optional
+    :rtype: str
+    """
+    if size:
+        if not isinstance(size, int):
+            raise TypeError("VARCHAR parameter should be an int, got %s"
+                            % type(size))
+        if size < 0:
+            raise ValueError("VARCHAR parameter can not be negative, got %d"
+                             % size)
+        return 'VARCHAR(%d)' % size
+    return 'VARCHAR'
+
+FIELDS_TO_PGTYPES = {
+    fields.boolean: 'bool',
+    fields.integer: 'int4',
+    fields.integer_big: 'int8',
+    fields.text: 'text',
+    fields.date: 'date',
+    fields.time: 'time',
+    fields.datetime: 'timestamp',
+    fields.binary: 'bytea',
+    fields.many2one: 'int4',
+}
+def get_pg_type(f, type_override=None):
+    """
+    :param fields._column f: field to get a Postgres type for
+    :param type type_override: use the provided type for dispatching instead of the field's own type
+    :returns: (postgres_identification_type, postgres_type_specification)
+    :rtype: (str, str)
+    """
+    field_type = type_override or type(f)
+
+    if field_type in FIELDS_TO_PGTYPES:
+        return (FIELDS_TO_PGTYPES[field_type], FIELDS_TO_PGTYPES[field_type])
+    elif issubclass(field_type, fields.float):
+        if f.digits:
+            return ('numeric', 'NUMERIC')
+        return ('float8', 'DOUBLE PRECISION')
+    elif issubclass(field_type, (fields.char, fields.reference)):
+        return ('varchar', pg_varchar(f.size))
+    elif issubclass(field_type, fields.selection):
+        if (isinstance(f.selection, list) and isinstance(f.selection[0][0], int))\
+                or getattr(f, 'size', None) == -1:
+            return ('int4', 'INTEGER')
+        return ('varchar', pg_varchar(getattr(f, 'size', None)))
+    elif issubclass(field_type, fields.function):
+        if f._type == 'selection':
+            return ('varchar', pg_varchar())
+        return get_pg_type(f, getattr(fields, f._type))
+    logging.getLogger('orm').warn('%s type not supported!', field_type)
+    return
 
 
 class MetaModel(type):
@@ -2838,7 +2840,7 @@
                         if f_obj_type:
                             ok = False
                             casts = [
-                                ('text', 'char', 'VARCHAR(%d)' % (f.size or 0,), '::VARCHAR(%d)'%(f.size or 0,)),
+                                ('text', 'char', pg_varchar(f.size), '::%s' % pg_varchar(f.size)),
                                 ('varchar', 'text', 'TEXT', ''),
                                 ('int4', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
                                 ('date', 'datetime', 'TIMESTAMP', '::TIMESTAMP'),
@@ -2848,8 +2850,8 @@
                             ]
                             if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size < f.size:
                                 cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
-                                cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" VARCHAR(%d)' % (self._table, k, f.size))
-                                cr.execute('UPDATE "%s" SET "%s"=temp_change_size::VARCHAR(%d)' % (self._table, k, f.size))
+                                cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size)))
+                                cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size)))
                                 cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
                                 cr.commit()
                                 self.__schema.debug("Table '%s': column '%s' (type varchar) changed size from %s to %s",

_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help   : https://help.launchpad.net/ListHelp

Reply via email to