Author: phd
Date: 2005-10-04 15:47:59 +0000 (Tue, 04 Oct 2005)
New Revision: 1073
Modified:
SQLObject/trunk/docs/SQLObject.txt
SQLObject/trunk/sqlobject/__init__.py
SQLObject/trunk/sqlobject/col.py
SQLObject/trunk/sqlobject/conftest.py
SQLObject/trunk/sqlobject/converters.py
SQLObject/trunk/sqlobject/index.py
SQLObject/trunk/sqlobject/tests/dbtest.py
SQLObject/trunk/sqlobject/tests/test_auto.py
SQLObject/trunk/sqlobject/tests/test_auto_old.py
SQLObject/trunk/sqlobject/tests/test_blob.py
SQLObject/trunk/sqlobject/tests/test_decimal.py
SQLObject/trunk/sqlobject/tests/test_empty.py
SQLObject/trunk/sqlobject/tests/test_indexes.py
SQLObject/trunk/sqlobject/tests/test_select.py
SQLObject/trunk/sqlobject/tests/test_slice.py
SQLObject/trunk/sqlobject/tests/test_stringid.py
SQLObject/trunk/sqlobject/tests/test_stringid_old.py
Log:
A patch by Runar Petursson <gr...@gm...> that implements support for MSSQL.
Modified: SQLObject/trunk/docs/SQLObject.txt
===================================================================
--- SQLObject/trunk/docs/SQLObject.txt 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/docs/SQLObject.txt 2005-10-04 15:47:59 UTC (rev 1073)
@@ -45,7 +45,8 @@
Currently SQLObject supports MySQL_ via MySQLdb_ aka MySQL-python,
PostgreSQL_ via psycopg_, SQLite_ via PySQLite_, Firebird_ via kinterbasdb_,
-Sybase via Sybase_, and `MAX DB`_ (also known as SAP DB) via sapdb_.
+Sybase via Sybase_, and `MAX DB`_ (also known as SAP DB) via sapdb_,
+`MSSQL Server`_ via pymssql_ (+ FreeTDS_) or ADODBAPI_ (Win32).
.. _MySQL: http://mysql.com
.. _MySQLdb: http://sourceforge.net/projects/mysql-python/
@@ -58,6 +59,10 @@
.. _Sybase: http://www.object-craft.com.au/projects/sybase/
.. _`MAX DB`: http://www.mysql.com/products/maxdb/
.. _sapdb: http://www.sapdb.org/sapdbPython.html
+.. _`MSSQL Server`: http://www.microsoft.com/sql/
+.. _pymssql: http://pymssql.sourceforge.net/
+.. _FreeTDS: http://www.freetds.org/
+.. _ADODBAPI: http://adodbapi.sourceforge.net/
Python 2.2 or higher is required. SQLObject makes extensive use of
new-style classes.
@@ -135,7 +140,7 @@
scheme://[user[:password]@]host[:port]/database[?parameters]
Scheme is one of ``sqlite``, ``mysql``, ``postgres``, ``firebird``,
-``interbase``, ``maxdb``, ``sapdb``, ``sybase``.
+``interbase``, ``maxdb``, ``sapdb``, ``sybase``, ``mssql``.
Examples::
@@ -1397,9 +1402,9 @@
DBConnection: Database Connections
==================================
-The `DBConnection` module currently has four external classes,
+The `DBConnection` module currently has six external classes,
`MySQLConnection`, `PostgresConnection`, `SQLiteConnection`,
-`SybaseConnection`, and `MaxdbConnection`.
+`SybaseConnection`, `MaxdbConnection`, `MSSQLConnection`.
You can pass the keyword argument `debug` to any connector. If set to
true, then any SQL sent to the database will also be printed to the
@@ -1491,6 +1496,23 @@
.. _sapdb: http://www.sapdb.org/sapdbPython.html
+MS SQL Server
+-------------
+
+The `MSSQLConnection` objects wants to use new style connection strings
+in the format of
+
+mssql://user:pass@host:port/db
+
+This will then be mapped to either the correct driver format. If running
+SQL Server on a "named" port, make sure to specify the port number in the
+URI.
+
+The two drivers currently supported are:
+
+.. _pymssql: http://pymssql.sourceforge.net/
+.. _adodbapi: http://adodbapi.sourceforge.net/
+
Exported Symbols
================
Modified: SQLObject/trunk/sqlobject/__init__.py
===================================================================
--- SQLObject/trunk/sqlobject/__init__.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/__init__.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -42,3 +42,8 @@
def MaxdbConnection(*args, **kw):
_warn('MaxdbConnection is deprecated; use connectionForURI("maxdb://...") or "from sqlobject.maxdb import builder; MaxdbConnection = builder()"')
return _maxdb.builder()(*args, **kw)
+
+import mssql as _mssql
+def MSSQLConnection(*args, **kw):
+ _warn('MssqlConnection is deprecated; use connectionForURI("mssql://...") or "from sqlobject.mssql import builder; MSSQLConnection = builder()"')
+ return _mssql.builder()(*args, **kw)
Modified: SQLObject/trunk/sqlobject/col.py
===================================================================
--- SQLObject/trunk/sqlobject/col.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/col.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -310,6 +310,9 @@
def _sybaseType(self):
return self._sqlType()
+ def _mssqlType(self):
+ return self._sqlType()
+
def _firebirdType(self):
return self._sqlType()
@@ -328,6 +331,9 @@
def sybaseCreateSQL(self):
return ' '.join([self.dbName, self._sybaseType()] + self._extraSQL())
+ def mssqlCreateSQL(self):
+ return ' '.join([self.dbName, self._mssqlType()] + self._extraSQL())
+
def firebirdCreateSQL(self):
# Ian Sparks pointed out that fb is picky about the order
# of the NOT NULL clause in a create statement. So, we handle
@@ -465,6 +471,17 @@
type += ' NULL'
return type
+ def _mssqlType(self):
+ if not self.length:
+ type = 'varchar(4000)'
+ elif self.varchar:
+ type = 'VARCHAR(%i)' % self.length
+ else:
+ type = 'CHAR(%i)' % self.length
+ if not self.notNone and not self.alternateID:
+ type += ' NULL'
+ return type
+
def _firebirdType(self):
self._check_case_sensitive("FireBird")
if not self.length:
@@ -611,6 +628,9 @@
def _sybaseType(self):
return "BIT"
+ def _mssqlType(self):
+ return "BIT"
+
def _firebirdType(self):
return 'INT'
@@ -681,6 +701,9 @@
def _sybaseType(self):
return 'NUMERIC(18,0) NULL'
+
+ def _mssqlType(self):
+ return 'INT NULL'
def _firebirdType(self):
return 'INT'
@@ -741,6 +764,17 @@
sql = ' '.join([sql, reference])
return sql
+ def mssqlCreateSQL(self):
+ sql = SOKeyCol.mssqlCreateSQL(self)
+ other = findClass(self.foreignKey)
+ tName = other.sqlmeta.table
+ idName = other.sqlmeta.idName
+ reference = ('REFERENCES %(tName)s(%(idName)s) ' %
+ {'tName':tName,
+ 'idName':idName})
+ sql = ' '.join([sql, reference])
+ return sql
+
def maxdbCreateSQL(self):
other = findClass(self.foreignKey)
fidName = self.dbName
@@ -789,6 +823,9 @@
def _sybaseType(self):
return self._postgresType()
+ def _mssqlType(self):
+ return self._postgresType()
+
def _firebirdType(self):
length = max(map(self._getlength, self.enumValues))
enumValues = ', '.join([sqlbuilder.sqlrepr(v, 'firebird') for v in self.enumValues])
@@ -914,6 +951,9 @@
def _sybaseType(self):
return 'DATETIME'
+ def _mssqlType(self):
+ return 'DATETIME'
+
def _sqliteType(self):
return 'TIMESTAMP'
@@ -974,6 +1014,12 @@
def _sybaseType(self):
return self._postgresType()
+
+ def _mssqlType(self):
+ """
+ SQL Server doesn't have a DATE data type, to emulate we use a vc(10)
+ """
+ return 'VARCHAR(10)'
def _firebirdType(self):
return 'DATE'
@@ -1070,6 +1116,9 @@
def _postgresType(self):
return 'BYTEA'
+ def _mssqlType(self):
+ return "IMAGE"
+
class BLOBCol(StringCol):
baseClass = SOBLOBCol
Modified: SQLObject/trunk/sqlobject/conftest.py
===================================================================
--- SQLObject/trunk/sqlobject/conftest.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/conftest.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -20,6 +20,7 @@
'sqlite': 'sqlite:///%s/data/sqlite.data' % os.getcwd(),
'sybase': 'sybase://test:test123@sybase/test?autoCommit=0',
'firebird': 'firebird://sysdba:masterkey@localhost/var/lib/firebird/data/test.gdb',
+ 'mssql': 'mssql://sa:@127.0.0.1/test'
}
Option = py.test.Config.Option
Modified: SQLObject/trunk/sqlobject/converters.py
===================================================================
--- SQLObject/trunk/sqlobject/converters.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/converters.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -110,7 +110,7 @@
if db in ('mysql', 'postgres'):
for orig, repl in sqlStringReplace:
value = value.replace(orig, repl)
- elif db in ('sqlite', 'firebird', 'sybase', 'maxdb'):
+ elif db in ('sqlite', 'firebird', 'sybase', 'maxdb', 'mssql'):
value = value.replace("'", "''")
else:
assert 0, "Database %s unknown" % db
Modified: SQLObject/trunk/sqlobject/index.py
===================================================================
--- SQLObject/trunk/sqlobject/index.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/index.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -77,8 +77,7 @@
', '.join(spec))
return ret
- postgresCreateIndexSQL = maxdbCreateIndexSQL = sybaseCreateIndexSQL = firebirdCreateIndexSQL = sqliteCreateIndexSQL
-
+ postgresCreateIndexSQL = maxdbCreateIndexSQL = mssqlCreateIndexSQL = sybaseCreateIndexSQL = firebirdCreateIndexSQL = sqliteCreateIndexSQL
def mysqlCreateIndexSQL(self, soClass):
if self.unique:
uniqueOrIndex = 'UNIQUE'
Modified: SQLObject/trunk/sqlobject/tests/dbtest.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/dbtest.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/dbtest.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -34,10 +34,14 @@
supportsMatrix = {
'+restrictedEnum': 'postgres',
'-transactions': 'mysql',
- '-dropTableCascade': 'sybase',
+ '-dropTableCascade': 'sybase mssql',
'-dynamicColumn': 'sqlite sybase',
'-fromDatabase': 'sqlite sybase firebird',
- '-expressionIndex': 'mysql sqlite firebird',
+ '-expressionIndex': 'mysql sqlite firebird mssql',
+ '-blobData': 'mssql',
+ '-decimalColumn': 'mssql',
+ '-emptyTable': 'mssql',
+ '-limitSelect' : 'mssql'
}
Modified: SQLObject/trunk/sqlobject/tests/test_auto.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_auto.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_auto.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -106,10 +106,24 @@
age INT DEFAULT 0,
created VARCHAR(40) NOT NULL,
happy char(1) DEFAULT 'Y' NOT NULL,
- long_field TEXT
+ long_field TEXT,
+ wannahavefun BIT default(0) NOT NULL
)
"""
+ mssqlCreate = """
+ CREATE TABLE auto_test (
+ auto_id int identity(1,1),
+ first_name VARCHAR(100),
+ last_name VARCHAR(200) NOT NULL,
+ age INT DEFAULT 0,
+ created VARCHAR(40) NOT NULL,
+ happy char(1) DEFAULT 'Y' NOT NULL,
+ long_field TEXT,
+ wannahavefun BIT default(0) NOT NULL
+ )
+ """
+
mysqlDrop = """
DROP TABLE IF EXISTS auto_test
"""
@@ -122,6 +136,8 @@
DROP TABLE auto_test
"""
+ mssqlDrop = sybaseDrop
+
def setup_method(self, meth):
conn = getConnection()
dbName = conn.dbName
Modified: SQLObject/trunk/sqlobject/tests/test_auto_old.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_auto_old.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_auto_old.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -112,6 +112,19 @@
)
"""
+ mssqlCreate = """
+ CREATE TABLE old_auto_test (
+ auto_id int IDENTITY(1,1) primary key,
+ first_name VARCHAR(100),
+ last_name VARCHAR(200) NOT NULL,
+ age INT DEFAULT 0,
+ created VARCHAR(40) NOT NULL,
+ happy char(1) DEFAULT 'Y' NOT NULL,
+ long_field TEXT,
+ wannahavefun BIT default(0) NOT NULL
+ )
+ """
+
mysqlDrop = """
DROP TABLE IF EXISTS old_auto_test
"""
@@ -124,6 +137,9 @@
DROP TABLE old_auto_test
"""
+ mssqlDrop = """
+ DROP TABLE old_auto_test
+ """
def setup_method(self, meth):
conn = getConnection()
dbName = conn.dbName
Modified: SQLObject/trunk/sqlobject/tests/test_blob.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_blob.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_blob.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -9,6 +9,8 @@
image = BLOBCol(default='emptydata', length=65535)
def test_BLOBCol():
+ if not supports('blobData'):
+ return
setupClass(ImageData)
data = ''.join([chr(x) for x in range(256)])
Modified: SQLObject/trunk/sqlobject/tests/test_decimal.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_decimal.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_decimal.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -7,18 +7,20 @@
Decimal = None
########################################
-## Deciaml columns
+## Decimal columns
########################################
class DecimalTable(SQLObject):
col1 = DecimalCol(size=6, precision=4)
def test_1decimal():
+ if not supports('decimalColumn'):
+ return
setupClass(DecimalTable)
d = DecimalTable(col1=21.12)
assert d.col1 == 21.12
-if Decimal:
+if Decimal and supports('decimalColumn'):
def test_2Decimal():
setupClass(DecimalTable)
d = DecimalTable(col1=Decimal("21.12"))
Modified: SQLObject/trunk/sqlobject/tests/test_empty.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_empty.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_empty.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -6,6 +6,8 @@
pass
def test_empty():
+ if not supports('emptyTable'):
+ return
setupClass(EmptyClass)
e1 = EmptyClass()
e2 = EmptyClass()
Modified: SQLObject/trunk/sqlobject/tests/test_indexes.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_indexes.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_indexes.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -29,7 +29,7 @@
mod = SOIndex1._connection.module
try:
SOIndex1(name='blah', number=0)
- except (mod.ProgrammingError, mod.IntegrityError, mod.OperationalError):
+ except (mod.ProgrammingError, mod.IntegrityError, mod.OperationalError, mod.DatabaseError):
# expected
pass
else:
Modified: SQLObject/trunk/sqlobject/tests/test_select.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_select.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_select.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -56,6 +56,8 @@
assert count == len(names)
def test_04_indexed_ended_by_exception():
+ if not supports('limitSelect'):
+ return
all = IterTest.select()
count = 0
try:
Modified: SQLObject/trunk/sqlobject/tests/test_slice.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_slice.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_slice.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -17,6 +17,8 @@
Counter(number=i)
def counterEqual(self, counters, value):
+ if not supports('limitSelect'):
+ return
assert [c.number for c in counters] == value
def test_1(self):
Modified: SQLObject/trunk/sqlobject/tests/test_stringid.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_stringid.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_stringid.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -9,7 +9,7 @@
class sqlmeta(sqlmeta):
table = 'so_string_id'
- _idType = str
+ idType = str
val = StringCol(alternateID=True)
mysqlCreate = """
@@ -40,6 +40,13 @@
)
"""
+ mssqlCreate = """
+ CREATE TABLE so_string_id (
+ id VARCHAR(50) PRIMARY KEY,
+ val varchar(4000)
+ )
+ """
+
sqliteCreate = postgresCreate
mysqlDrop = """
@@ -52,8 +59,8 @@
sqliteDrop = postgresDrop
firebirdDrop = postgresDrop
+ mssqlDrop = postgresDrop
-
def test_stringID():
setupClass(SOStringID)
t = SOStringID(id='hey', val='whatever')
Modified: SQLObject/trunk/sqlobject/tests/test_stringid_old.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_stringid_old.py 2005-10-03 10:07:29 UTC (rev 1072)
+++ SQLObject/trunk/sqlobject/tests/test_stringid_old.py 2005-10-04 15:47:59 UTC (rev 1073)
@@ -34,6 +34,13 @@
)
"""
+ mssqlCreate = """
+ CREATE TABLE so_string_id (
+ id VARCHAR(50) primary key,
+ val VARCHAR(50) NULL
+ )
+ """
+
firebirdCreate = """
CREATE TABLE so_string_id (
id VARCHAR(50) NOT NULL PRIMARY KEY,
@@ -53,6 +60,7 @@
sqliteDrop = postgresDrop
firebirdDrop = postgresDrop
+ mssqlDrop = postgresDrop
def test_stringID():
|