We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
empty strings stored as NULL in db
https://groups.google.com/forum/#!topic/web2py/IYzhhd3eKXQ


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gluon/dal.py`
Content:
```
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 """
5 | This file is part of the web2py Web Framework
6 | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
7 | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
8 
9 Takes care of adapting pyDAL to web2py's needs
10 -----------------------------------------------
11 """
12 
13 from pydal import DAL as DAL
14 from pydal import Field
15 from pydal.objects import Row, Rows, Table, Query, Set, Expression
16 from pydal import SQLCustomType, geoPoint, geoLine, geoPolygon
17 
18 
19 def _default_validators(db, field):
20     """
21     Field type validation, using web2py's validators mechanism.
22 
23     makes sure the content of a field is in line with the declared
24     fieldtype
25     """
26     from gluon import validators
27     field_type, field_length = field.type, field.length
28     requires = []
29 
30     if field_type in (('string', 'text', 'password')):
31         requires.append(validators.IS_LENGTH(field_length))
32     elif field_type == 'json':
33         requires.append(validators.IS_EMPTY_OR(validators.IS_JSON()))
34     elif field_type == 'double' or field_type == 'float':
35         requires.append(validators.IS_FLOAT_IN_RANGE(-1e100, 1e100))
36     elif field_type == 'integer':
37         requires.append(validators.IS_INT_IN_RANGE(-2**31, 2**31))
38     elif field_type == 'bigint':
39         requires.append(validators.IS_INT_IN_RANGE(-2**63, 2**63))
40     elif field_type.startswith('decimal'):
41         requires.append(validators.IS_DECIMAL_IN_RANGE(-10**10, 10**10))
42     elif field_type == 'date':
43         requires.append(validators.IS_DATE())
44     elif field_type == 'time':
45         requires.append(validators.IS_TIME())
46     elif field_type == 'datetime':
47         requires.append(validators.IS_DATETIME())
48     elif db and field_type.startswith('reference') and \
49             field_type.find('.') < 0 and \
50             field_type[10:] in db.tables:
51         referenced = db[field_type[10:]]
52         if hasattr(referenced, '_format') and referenced._format:
53             requires = validators.IS_IN_DB(db, referenced._id,
54                                            referenced._format)
55             if field.unique:
56                 requires._and = validators.IS_NOT_IN_DB(db, field)
57             if field.tablename == field_type[10:]:
58                 return validators.IS_EMPTY_OR(requires)
59             return requires
60     elif db and field_type.startswith('list:reference') and \
61             field_type.find('.') < 0 and \
62             field_type[15:] in db.tables:
63         referenced = db[field_type[15:]]
64         if hasattr(referenced, '_format') and referenced._format:
65             requires = validators.IS_IN_DB(db, referenced._id,
66                                            referenced._format, multiple=True)
67         else:
68             requires = validators.IS_IN_DB(db, referenced._id,
69                                            multiple=True)
70         if field.unique:
71             requires._and = validators.IS_NOT_IN_DB(db, field)
72         if not field.notnull:
73             requires = validators.IS_EMPTY_OR(requires)
74         return requires
75     # does not get here for reference and list:reference
76     if field.unique:
77         requires.insert(0,validators.IS_NOT_IN_DB(db, field))
78     excluded_fields = ['string','upload','text','password','boolean']
79     if (field.notnull or field.unique) and not field_type in excluded_fields:
80         requires.insert(0,validators.IS_NOT_EMPTY())
81     elif not field.notnull and not field.unique and requires:
82         requires[0] = validators.IS_EMPTY_OR(requires[0])
83     return requires
84 
85 from gluon.serializers import custom_json, xml
86 from gluon.utils import web2py_uuid
87 from gluon import sqlhtml
88 
89 
90 DAL.serializers = {'json': custom_json, 'xml': xml}
91 DAL.validators_method = _default_validators
92 DAL.uuid = lambda x: web2py_uuid()
93 DAL.representers = {
94     'rows_render': sqlhtml.represent,
95     'rows_xml': sqlhtml.SQLTABLE
96     }
97 DAL.Field = Field
98 DAL.Table = Table
99 
100 #: add web2py contrib drivers to pyDAL
101 from pydal.drivers import DRIVERS
102 if not DRIVERS.get('pymysql'):
103     try:
104         from .contrib import pymysql
105         DRIVERS['pymysql'] = pymysql
106     except:
107         pass
108 if not DRIVERS.get('pyodbc'):
109     try:
110         from .contrib import pypyodbc as pyodbc
111         DRIVERS['pyodbc'] = pyodbc
112     except:
113         pass
114 if not DRIVERS.get('pg8000'):
115     try:
116         from .contrib import pg8000
117         DRIVERS['pg8000'] = pg8000
118     except:
119         pass
120
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gluon/dal.py b/gluon/dal.py
--- a/gluon/dal.py
+++ b/gluon/dal.py
@@ -74,12 +74,12 @@
         return requires
     # does not get here for reference and list:reference
     if field.unique:
-        requires.insert(0,validators.IS_NOT_IN_DB(db, field))
-    excluded_fields = ['string','upload','text','password','boolean']
+        requires.insert(0, validators.IS_NOT_IN_DB(db, field))
+    excluded_fields = ['string', 'upload', 'text', 'password', 'boolean']
     if (field.notnull or field.unique) and not field_type in excluded_fields:
-        requires.insert(0,validators.IS_NOT_EMPTY())
+        requires.insert(0, validators.IS_NOT_EMPTY())
     elif not field.notnull and not field.unique and requires:
-        requires[0] = validators.IS_EMPTY_OR(requires[0])
+        requires[0] = validators.IS_EMPTY_OR(requires[0], null='' if field in ('string', 'text', 'password') else None)
     return requires
 
 from gluon.serializers import custom_json, xml
@@ -93,7 +93,7 @@
 DAL.representers = {
     'rows_render': sqlhtml.represent,
     'rows_xml': sqlhtml.SQLTABLE
-    }
+}
 DAL.Field = Field
 DAL.Table = Table

{"golden_diff": "diff --git a/gluon/dal.py b/gluon/dal.py\n--- a/gluon/dal.py\n+++ b/gluon/dal.py\n@@ -74,12 +74,12 @@\n         return requires\n     # does not get here for reference and list:reference\n     if field.unique:\n-        requires.insert(0,validators.IS_NOT_IN_DB(db, field))\n-    excluded_fields = ['string','upload','text','password','boolean']\n+        requires.insert(0, validators.IS_NOT_IN_DB(db, field))\n+    excluded_fields = ['string', 'upload', 'text', 'password', 'boolean']\n     if (field.notnull or field.unique) and not field_type in excluded_fields:\n-        requires.insert(0,validators.IS_NOT_EMPTY())\n+        requires.insert(0, validators.IS_NOT_EMPTY())\n     elif not field.notnull and not field.unique and requires:\n-        requires[0] = validators.IS_EMPTY_OR(requires[0])\n+        requires[0] = validators.IS_EMPTY_OR(requires[0], null='' if field in ('string', 'text', 'password') else None)\n     return requires\n \n from gluon.serializers import custom_json, xml\n@@ -93,7 +93,7 @@\n DAL.representers = {\n     'rows_render': sqlhtml.represent,\n     'rows_xml': sqlhtml.SQLTABLE\n-    }\n+}\n DAL.Field = Field\n DAL.Table = Table\n", "issue": "empty strings stored as NULL in db\nhttps://groups.google.com/forum/#!topic/web2py/IYzhhd3eKXQ\n\n", "before_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"\n| This file is part of the web2py Web Framework\n| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>\n| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)\n\nTakes care of adapting pyDAL to web2py's needs\n-----------------------------------------------\n\"\"\"\n\nfrom pydal import DAL as DAL\nfrom pydal import Field\nfrom pydal.objects import Row, Rows, Table, Query, Set, Expression\nfrom pydal import SQLCustomType, geoPoint, geoLine, geoPolygon\n\n\ndef _default_validators(db, field):\n    \"\"\"\n    Field type validation, using web2py's validators mechanism.\n\n    makes sure the content of a field is in line with the declared\n    fieldtype\n    \"\"\"\n    from gluon import validators\n    field_type, field_length = field.type, field.length\n    requires = []\n\n    if field_type in (('string', 'text', 'password')):\n        requires.append(validators.IS_LENGTH(field_length))\n    elif field_type == 'json':\n        requires.append(validators.IS_EMPTY_OR(validators.IS_JSON()))\n    elif field_type == 'double' or field_type == 'float':\n        requires.append(validators.IS_FLOAT_IN_RANGE(-1e100, 1e100))\n    elif field_type == 'integer':\n        requires.append(validators.IS_INT_IN_RANGE(-2**31, 2**31))\n    elif field_type == 'bigint':\n        requires.append(validators.IS_INT_IN_RANGE(-2**63, 2**63))\n    elif field_type.startswith('decimal'):\n        requires.append(validators.IS_DECIMAL_IN_RANGE(-10**10, 10**10))\n    elif field_type == 'date':\n        requires.append(validators.IS_DATE())\n    elif field_type == 'time':\n        requires.append(validators.IS_TIME())\n    elif field_type == 'datetime':\n        requires.append(validators.IS_DATETIME())\n    elif db and field_type.startswith('reference') and \\\n            field_type.find('.') < 0 and \\\n            field_type[10:] in db.tables:\n        referenced = db[field_type[10:]]\n        if hasattr(referenced, '_format') and referenced._format:\n            requires = validators.IS_IN_DB(db, referenced._id,\n                                           referenced._format)\n            if field.unique:\n                requires._and = validators.IS_NOT_IN_DB(db, field)\n            if field.tablename == field_type[10:]:\n                return validators.IS_EMPTY_OR(requires)\n            return requires\n    elif db and field_type.startswith('list:reference') and \\\n            field_type.find('.') < 0 and \\\n            field_type[15:] in db.tables:\n        referenced = db[field_type[15:]]\n        if hasattr(referenced, '_format') and referenced._format:\n            requires = validators.IS_IN_DB(db, referenced._id,\n                                           referenced._format, multiple=True)\n        else:\n            requires = validators.IS_IN_DB(db, referenced._id,\n                                           multiple=True)\n        if field.unique:\n            requires._and = validators.IS_NOT_IN_DB(db, field)\n        if not field.notnull:\n            requires = validators.IS_EMPTY_OR(requires)\n        return requires\n    # does not get here for reference and list:reference\n    if field.unique:\n        requires.insert(0,validators.IS_NOT_IN_DB(db, field))\n    excluded_fields = ['string','upload','text','password','boolean']\n    if (field.notnull or field.unique) and not field_type in excluded_fields:\n        requires.insert(0,validators.IS_NOT_EMPTY())\n    elif not field.notnull and not field.unique and requires:\n        requires[0] = validators.IS_EMPTY_OR(requires[0])\n    return requires\n\nfrom gluon.serializers import custom_json, xml\nfrom gluon.utils import web2py_uuid\nfrom gluon import sqlhtml\n\n\nDAL.serializers = {'json': custom_json, 'xml': xml}\nDAL.validators_method = _default_validators\nDAL.uuid = lambda x: web2py_uuid()\nDAL.representers = {\n    'rows_render': sqlhtml.represent,\n    'rows_xml': sqlhtml.SQLTABLE\n    }\nDAL.Field = Field\nDAL.Table = Table\n\n#: add web2py contrib drivers to pyDAL\nfrom pydal.drivers import DRIVERS\nif not DRIVERS.get('pymysql'):\n    try:\n        from .contrib import pymysql\n        DRIVERS['pymysql'] = pymysql\n    except:\n        pass\nif not DRIVERS.get('pyodbc'):\n    try:\n        from .contrib import pypyodbc as pyodbc\n        DRIVERS['pyodbc'] = pyodbc\n    except:\n        pass\nif not DRIVERS.get('pg8000'):\n    try:\n        from .contrib import pg8000\n        DRIVERS['pg8000'] = pg8000\n    except:\n        pass\n", "path": "gluon/dal.py"}], "after_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"\n| This file is part of the web2py Web Framework\n| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>\n| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)\n\nTakes care of adapting pyDAL to web2py's needs\n-----------------------------------------------\n\"\"\"\n\nfrom pydal import DAL as DAL\nfrom pydal import Field\nfrom pydal.objects import Row, Rows, Table, Query, Set, Expression\nfrom pydal import SQLCustomType, geoPoint, geoLine, geoPolygon\n\n\ndef _default_validators(db, field):\n    \"\"\"\n    Field type validation, using web2py's validators mechanism.\n\n    makes sure the content of a field is in line with the declared\n    fieldtype\n    \"\"\"\n    from gluon import validators\n    field_type, field_length = field.type, field.length\n    requires = []\n\n    if field_type in (('string', 'text', 'password')):\n        requires.append(validators.IS_LENGTH(field_length))\n    elif field_type == 'json':\n        requires.append(validators.IS_EMPTY_OR(validators.IS_JSON()))\n    elif field_type == 'double' or field_type == 'float':\n        requires.append(validators.IS_FLOAT_IN_RANGE(-1e100, 1e100))\n    elif field_type == 'integer':\n        requires.append(validators.IS_INT_IN_RANGE(-2**31, 2**31))\n    elif field_type == 'bigint':\n        requires.append(validators.IS_INT_IN_RANGE(-2**63, 2**63))\n    elif field_type.startswith('decimal'):\n        requires.append(validators.IS_DECIMAL_IN_RANGE(-10**10, 10**10))\n    elif field_type == 'date':\n        requires.append(validators.IS_DATE())\n    elif field_type == 'time':\n        requires.append(validators.IS_TIME())\n    elif field_type == 'datetime':\n        requires.append(validators.IS_DATETIME())\n    elif db and field_type.startswith('reference') and \\\n            field_type.find('.') < 0 and \\\n            field_type[10:] in db.tables:\n        referenced = db[field_type[10:]]\n        if hasattr(referenced, '_format') and referenced._format:\n            requires = validators.IS_IN_DB(db, referenced._id,\n                                           referenced._format)\n            if field.unique:\n                requires._and = validators.IS_NOT_IN_DB(db, field)\n            if field.tablename == field_type[10:]:\n                return validators.IS_EMPTY_OR(requires)\n            return requires\n    elif db and field_type.startswith('list:reference') and \\\n            field_type.find('.') < 0 and \\\n            field_type[15:] in db.tables:\n        referenced = db[field_type[15:]]\n        if hasattr(referenced, '_format') and referenced._format:\n            requires = validators.IS_IN_DB(db, referenced._id,\n                                           referenced._format, multiple=True)\n        else:\n            requires = validators.IS_IN_DB(db, referenced._id,\n                                           multiple=True)\n        if field.unique:\n            requires._and = validators.IS_NOT_IN_DB(db, field)\n        if not field.notnull:\n            requires = validators.IS_EMPTY_OR(requires)\n        return requires\n    # does not get here for reference and list:reference\n    if field.unique:\n        requires.insert(0, validators.IS_NOT_IN_DB(db, field))\n    excluded_fields = ['string', 'upload', 'text', 'password', 'boolean']\n    if (field.notnull or field.unique) and not field_type in excluded_fields:\n        requires.insert(0, validators.IS_NOT_EMPTY())\n    elif not field.notnull and not field.unique and requires:\n        requires[0] = validators.IS_EMPTY_OR(requires[0], null='' if field in ('string', 'text', 'password') else None)\n    return requires\n\nfrom gluon.serializers import custom_json, xml\nfrom gluon.utils import web2py_uuid\nfrom gluon import sqlhtml\n\n\nDAL.serializers = {'json': custom_json, 'xml': xml}\nDAL.validators_method = _default_validators\nDAL.uuid = lambda x: web2py_uuid()\nDAL.representers = {\n    'rows_render': sqlhtml.represent,\n    'rows_xml': sqlhtml.SQLTABLE\n}\nDAL.Field = Field\nDAL.Table = Table\n\n#: add web2py contrib drivers to pyDAL\nfrom pydal.drivers import DRIVERS\nif not DRIVERS.get('pymysql'):\n    try:\n        from .contrib import pymysql\n        DRIVERS['pymysql'] = pymysql\n    except:\n        pass\nif not DRIVERS.get('pyodbc'):\n    try:\n        from .contrib import pypyodbc as pyodbc\n        DRIVERS['pyodbc'] = pyodbc\n    except:\n        pass\nif not DRIVERS.get('pg8000'):\n    try:\n        from .contrib import pg8000\n        DRIVERS['pg8000'] = pg8000\n    except:\n        pass\n", "path": "gluon/dal.py"}]}