]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- the behavior of String/Unicode types regarding that they auto-convert
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 26 Sep 2007 14:55:44 +0000 (14:55 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 26 Sep 2007 14:55:44 +0000 (14:55 +0000)
  to TEXT/CLOB when no length is present now occurs *only* for an exact type
  of String or Unicode with no arguments.  If you use VARCHAR or NCHAR
  (subclasses of String/Unicode) with no length, they will be interpreted
  by the dialect as VARCHAR/NCHAR; no "magic" conversion happens there.
  This is less surprising behavior and in particular this helps Oracle keep
  string-based bind parameters as VARCHARs and not CLOBs [ticket:793].

CHANGES
lib/sqlalchemy/sql/expression.py
lib/sqlalchemy/types.py
test/dialect/oracle.py
test/sql/testtypes.py

diff --git a/CHANGES b/CHANGES
index 05c00b92da6d383d54a43b4e25adbd7baa0f4208..5ebec6b471c75dbcd1e6ae17979e84b15d38fa56 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -5,8 +5,10 @@ CHANGES
 0.4.0beta6
 ----------
 
-- Added full list of SQLite reserved keywords so that they get escaped
-  properly.
+- the Session identity map is now *weak referencing* by default, use 
+weak_identity_map=False to use a regular dict.  The weak dict we are using
+is customized to detect instances which are "dirty" and maintain a temporary
+strong reference to those instances until changes are flushed.
 
 - Mapper compilation has been reorganized such that most compilation occurs
   upon mapper construction.  This allows us to have fewer calls to
@@ -17,6 +19,9 @@ CHANGES
   inheritance relationships need to be constructed in inheritance order
   (which should be the normal case anyway).
 
+- Added full list of SQLite reserved keywords so that they get escaped
+properly.
+
 - Removed "parameters" argument from clauseelement.compile(), replaced with
   "column_keys".  The parameters sent to execute() only interact with the
   insert/update statement compilation process in terms of the column names
@@ -48,14 +53,17 @@ CHANGES
   of foreign key attributes during a flush where the parent object is
   deleted.
 
-- the Session identity map is now *weak referencing* by default, use 
-  weak_identity_map=False to use a regular dict.  The weak dict we are using
-  is customized to detect instances which are "dirty" and maintain a temporary
-  strong reference to those instances until changes are flushed.
-  
 - Column defaults and onupdates, executing inline, will add parenthesis for
   subqueries and other parenthesis-requiring expressions
 
+- the behavior of String/Unicode types regarding that they auto-convert
+  to TEXT/CLOB when no length is present now occurs *only* for an exact type
+  of String or Unicode with no arguments.  If you use VARCHAR or NCHAR 
+  (subclasses of String/Unicode) with no length, they will be interpreted
+  by the dialect as VARCHAR/NCHAR; no "magic" conversion happens there.
+  This is less surprising behavior and in particular this helps Oracle keep 
+  string-based bind parameters as VARCHARs and not CLOBs [ticket:793].  
+
 - Fixes to ShardedSession to work with deferred columns [ticket:771].
 
 - User-defined shard_chooser() function must accept "clause=None" argument;
index dac5d7a743bb929a298f100cc39dd68974f20313..d649fc0ff08e2bd633c824577b9761b68e2f87d4 100644 (file)
@@ -1758,9 +1758,11 @@ class _BindParamClause(ClauseElement, _CompareMixin):
             self.type = type_
 
     # TODO: move to types module, obviously
+    # using VARCHAR/NCHAR so that we dont get the genericized "String"
+    # type which usually resolves to TEXT/CLOB
     type_map = {
-        str : sqltypes.String,
-        unicode : sqltypes.Unicode,
+        str : sqltypes.VARCHAR,
+        unicode : sqltypes.NCHAR,
         int : sqltypes.Integer,
         float : sqltypes.Numeric,
         type(None):sqltypes.NullType
index 71b4bbec1bc651477da1143eddfcabac13be928d..4b91a1e036afc01868f96b045e9c0cb2c9e421f3 100644 (file)
@@ -304,12 +304,14 @@ class String(TypeEngine, Concatenable):
 
     def get_search_list(self):
         l = super(String, self).get_search_list()
-        if self.length is None:
+        # if we are String or Unicode with no length,
+        # return TEXT as the highest-priority type
+        # to be adapted by the dialect
+        if self.length is None and l[0] in (String, Unicode):
             return (TEXT,) + l
         else:
             return l
 
-
     def get_dbapi_type(self, dbapi):
         return dbapi.STRING
 
index cbad7ced89e23d9ff11de646c23524ee98cbf976..f993536167f0538429304bfbb78ab1e6a1b05c88 100644 (file)
@@ -120,6 +120,25 @@ myothertable.othername != :myothertable_othername OR EXISTS (select yay from foo
             "ON addresses.address_type_id = address_types_1.id WHERE addresses.user_id = :addresses_user_id ORDER BY addresses.rowid, "
             "address_types.rowid")
 
+class TypesTest(SQLCompileTest):
+    def test_no_clobs_for_string_params(self):
+        """test that simple string params get a DBAPI type of VARCHAR, not CLOB.
+        this is to prevent setinputsizes from setting up cx_oracle.CLOBs on 
+        string-based bind params [ticket:793]."""
+        
+        class FakeDBAPI(object):
+            def __getattr__(self, attr):
+                return attr
+
+        dialect = oracle.OracleDialect()
+        dbapi = FakeDBAPI()
+                
+        b = bindparam("foo", "hello world!")
+        assert b.type.dialect_impl(dialect).get_dbapi_type(dbapi) == 'STRING'
+
+        b = bindparam("foo", u"hello world!")
+        assert b.type.dialect_impl(dialect).get_dbapi_type(dbapi) == 'STRING'
+        
 class SequenceTest(SQLCompileTest):
     def test_basic(self):
         seq = Sequence("my_seq_no_schema")
index 3620899672c75bc551192fc9d34d6440b02b4ae5..fced14e1d727e04581462d0e02a8e17124658796 100644 (file)
@@ -4,7 +4,7 @@ import datetime, os
 from sqlalchemy import *
 from sqlalchemy import types
 import sqlalchemy.engine.url as url
-from sqlalchemy.databases import mssql, oracle, mysql
+from sqlalchemy.databases import mssql, oracle, mysql, postgres
 from testlib import *
 
 
@@ -123,6 +123,33 @@ class AdaptTest(PersistTest):
         t2 = mysql.MSVarBinary()
         assert isinstance(dialect.type_descriptor(t1), mysql.MSVarBinary)
         assert isinstance(dialect.type_descriptor(t2), mysql.MSVarBinary)
+    
+    def teststringadapt(self):
+        """test that String with no size becomes TEXT, *all* others stay as varchar/String"""
+        
+        oracle_dialect = oracle.OracleDialect()
+        mysql_dialect = mysql.MySQLDialect()
+        postgres_dialect = postgres.PGDialect()
+        
+        for dialect, start, test in [
+            (oracle_dialect, String(), oracle.OracleText),
+            (oracle_dialect, VARCHAR(), oracle.OracleString),
+            (oracle_dialect, String(50), oracle.OracleString),
+            (oracle_dialect, Unicode(), oracle.OracleText),
+            (oracle_dialect, NCHAR(), oracle.OracleString),
+            (mysql_dialect, String(), mysql.MSText),
+            (mysql_dialect, VARCHAR(), mysql.MSString),
+            (mysql_dialect, String(50), mysql.MSString),
+            (mysql_dialect, Unicode(), mysql.MSText),
+            (mysql_dialect, NCHAR(), mysql.MSNChar),
+            (postgres_dialect, String(), postgres.PGText),
+            (postgres_dialect, VARCHAR(), postgres.PGString),
+            (postgres_dialect, String(50), postgres.PGString),
+            (postgres_dialect, Unicode(), postgres.PGText),
+            (postgres_dialect, NCHAR(), postgres.PGString),
+        ]:
+            assert isinstance(start.dialect_impl(dialect), test), "wanted %r got %r" % (test, start.dialect_impl(dialect))
+        
         
         
 class UserDefinedTest(PersistTest):