= The boulder-oracle-sprint branch = This branch began November 4, 2006 at a [http://wiki.python.org/moin/BoulderSprint code sprint] in Boulder, Colorado, whence it takes its name. It aims to implement full support for the [http://www.oracle.com/ Oracle RDBMS]. == How to get the branch == {{{ svn co http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint/ }}} See our [http://www.djangoproject.com/documentation/contributing/#branch-policy branch policy] for full information on how to use a branch. == Goals == The main goals of this branch are: * Improve the oracle backend to the point where it can be used interchangeably with the existing supported backends. == Status == === Tablespace options === A common paradigm for optimizing performance in Oracle-based systems is the use of [http://en.wikipedia.org/wiki/Tablespace tablespaces] to organize disk layout. The Oracle branch supports this use case by adding ''db_tablespace'' options to Meta and Field classes. When using a backend that lacks support for tablespaces, these options are ignored. A tablespace can be specified for the table(s) generated by a model by supplying the "db_tablespace" option inside the model's Meta class. Additionally, the "db_tablespace" option can be passed to a Field constructor to specify an alternate tablespace for the Field's column index. If no index would be created for the column, the "db_tablespace" option is ignored. {{{ class TablespaceExample(models.Model): name = models.CharField(maxlength=30, db_index=True, db_tablespace="indexes") data = models.CharField(maxlength=255, db_index=True) edges = models.ManyToManyField(to="self", db_tablespace="indexes") class Meta: db_tablespace = "tables" }}} In this example, the tables generated by the !TablespaceExample model (''i.e.'', the model table and the many-to-many table) would be stored in the "tables" tablespace. The index for the name field and the indexes on the many-to-many table would be stored in the "indexes" tablespace. The "data" field would also generate an index, but no tablespace for it is specified, so it would be stored in the model tablespace "tables" by default. === Naming issues === Oracle imposes a name length limit of 30 characters. To accommodate this, the django.db.backends.util.truncate_name function has been added to truncate names to backend.get_max_name_length(). To prevent name collisions, the final 4 characters of the truncated name are generated from an MD5 hash. === Backend !QuerySet classes === A new mechanism is supplied by which backends can supply their own !QuerySet subclasses that will be used in place of !QuerySet, and that core !QuerySet subclasses (such as !DateQuerySet) will subclass. The oracle backend uses this to override the iterator and _get_sql_clause methods in order to construct proper SQL for Oracle. It is also used to provide a new resolve_columns method that the iterator method is expected to call in order to fetch corrected values. === Empty strings === Django generally prefers to use the empty string (!'') rather than NULL, but Oracle treats the empty string as NULL. To get around this, the oracle backend silently changes !'' to ' ' when saving data. The inverse operation is automatically performed by !OracleQuerySet.resolve_columns when fetching from the database. === Datetime conversion === cx_Oracle always returns either datetime.datetime objects or its own Timestamp objects when fetching date or time-related fields. The !OracleQuerySet.resolve_columns method is used to cast values from the database to the proper type for the Field. === LOB manipulation === The oracle backend stores !TextFields as NCLOB columns, for which cx_Oracle requires some extra manipulation to fetch. This is automatically handled by !OracleQuerySet.resolve_columns. == To-do list == (Updated April 19, 2007) * The branch could use additional testing. * Additional code refactoring for the !QuerySet.iterator and !QuerySet._get_sql_clause methods would be nice.