summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES22
-rw-r--r--lib/sqlalchemy/sql/expression.py6
-rw-r--r--lib/sqlalchemy/types.py6
-rw-r--r--test/dialect/oracle.py19
-rw-r--r--test/sql/testtypes.py29
5 files changed, 70 insertions, 12 deletions
diff --git a/CHANGES b/CHANGES
index 05c00b92d..5ebec6b47 100644
--- 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;
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index dac5d7a74..d649fc0ff 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -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
diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py
index 71b4bbec1..4b91a1e03 100644
--- a/lib/sqlalchemy/types.py
+++ b/lib/sqlalchemy/types.py
@@ -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
diff --git a/test/dialect/oracle.py b/test/dialect/oracle.py
index cbad7ced8..f99353616 100644
--- a/test/dialect/oracle.py
+++ b/test/dialect/oracle.py
@@ -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")
diff --git a/test/sql/testtypes.py b/test/sql/testtypes.py
index 362089967..fced14e1d 100644
--- a/test/sql/testtypes.py
+++ b/test/sql/testtypes.py
@@ -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):