Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

Changeset 2

Show
Ignore:
Timestamp:
02/11/07 02:31:31
Author:
fumanchu
Message:

New Database methods: make_table, save, insert. Added initial test suite (mostly copied from dejavu).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/geniusql/__init__.py

    r1 r2  
    1515""" 
    1616 
     17__version__ = "1.0alpha" 
     18 
    1719import threading 
     20from geniusql import xray 
    1821 
    1922from geniusql import errors, typerefs 
     
    2326from geniusql.isolation import * 
    2427from geniusql.select import * 
     28 
     29from geniusql import providers 
     30 
     31 
     32def db(cls, name, options): 
     33    """Create a Database model object for the given cls, name, and options. 
     34     
     35    cls: Either a subclass of geniusql.Database or a 'shortcut name' 
     36        registered in geniusql.providers.providers. 
     37    name: the database name as used by the underlying database. 
     38     
     39    This function does not call CREATE DATABASE, nor should it open any 
     40    database connections. It simply instantiates the proper Database class. 
     41    """ 
     42    if isinstance(cls, basestring): 
     43        try: 
     44            cls = providers.providers[cls] 
     45        except KeyError: 
     46            pass 
     47     
     48    if isinstance(cls, basestring): 
     49        cls = xray.classes(cls) 
     50     
     51    opts = dict([(str(k), v) for k, v in options.iteritems()]) 
     52     
     53    return cls(name, **opts) 
    2554 
    2655 
     
    126155        self.autoincrement = False 
    127156        self.sequence_name = None 
    128         self.initial = 0 
     157        self.initial = 1 
    129158         
    130159        self.imperfect_type = False 
     
    162191        set the 'db' arg late. 
    163192    indices: a dict-like IndexSet of Index objects. 
    164     references: a dict of the form: {name: (nearKey, farTable, farKey)}. 
     193    references: a dict of the form: {name: (nearColKey, farTableKey, farColKey)}. 
    165194    """ 
    166195     
     
    617646        return self.sql_name(columnkey) 
    618647     
    619     def make_column(self, tablekey, columnkey, pytype, default, hints): 
     648    def make_column(self, tablekey, columnkey, pytype=unicode, default=None, hints=None): 
    620649        """Return a Column object from the given table and column keys.""" 
    621650        name = self.column_name(tablekey, columnkey) 
    622         col = Column(name, self.quote(name), None, default, hints.copy()) 
     651        if hints is None: 
     652            hints = {} 
     653        else: 
     654            hints = hints.copy() 
     655        col = Column(name, self.quote(name), None, default, hints) 
    623656        col.dbtype = self.typeadapter.coerce(col, pytype) 
    624657        pytype2 = self.python_type(col.dbtype) 
     
    631664        # to DB table names, override this method. 
    632665        return self.sql_name(self.Prefix + key) 
     666     
     667    def make_table(self, name): 
     668        """Create and return a Table object for the given name.""" 
     669        name = self.table_name(name) 
     670        return self.tableclass(name, self.quote(name)) 
    633671     
    634672    def make_index(self, tablekey, columnkey): 
     
    846884            raise errors.TransactionLock("Unlock called inside transaction.") 
    847885        del self.transactions[key] 
    848  
    849  
     886     
     887    #                              OLTP/CRUD                               # 
     888     
     889    def id_clause(self, tablekey, **inputs): 
     890        """Return an SQL expression for the identifiers of the given table.""" 
     891        t = self[tablekey] 
     892        coerce = self.adaptertosql.coerce 
     893        pairs = [] 
     894        for key, col in t.iteritems(): 
     895            if col.key: 
     896                val = coerce(inputs[key], col.dbtype) 
     897                pairs.append("%s = %s" % (col.qname, val)) 
     898        return " AND ".join(pairs) 
     899     
     900    def insert(self, tablekey, **inputs): 
     901        """Insert a row and return {idcolkey: newid}.""" 
     902        t = self[tablekey] 
     903         
     904        fields = [] 
     905        idkeys = [] 
     906        values = [] 
     907        for key, col in t.iteritems(): 
     908            if col.autoincrement: 
     909                # Skip this field, since we're using a sequencer 
     910                idkeys.append(key) 
     911                continue 
     912            if key in inputs: 
     913                val = self.adaptertosql.coerce(inputs[key], col.dbtype) 
     914                fields.append(col.qname) 
     915                values.append(val) 
     916         
     917        transconn = self.get_transaction() 
     918         
     919        fields = ", ".join(fields) 
     920        values = ", ".join(values) 
     921        self.execute('INSERT INTO %s (%s) VALUES (%s);' % 
     922                        (t.qname, fields, values), transconn) 
     923         
     924        return self._grab_new_ids(t, idkeys, transconn) 
     925     
     926    def _grab_new_ids(self, table, idkeys): 
     927        raise NotImplementedError 
     928     
     929    def save(self, tablekey, **inputs): 
     930        """Update a row using the given inputs.""" 
     931        t = self[tablekey] 
     932         
     933        parms = [] 
     934        coerce = self.adaptertosql.coerce 
     935        for key, val in inputs.iteritems(): 
     936            col = t[key] 
     937            val = coerce(val, col.dbtype) 
     938            parms.append('%s = %s' % (col.qname, val)) 
     939         
     940        if parms: 
     941            sql = ('UPDATE %s SET %s WHERE %s;' % 
     942                   (t.qname, ", ".join(parms), self.id_clause(tablekey, **inputs))) 
     943            self.execute(sql, self.get_transaction()) 
     944 
  • trunk/geniusql/providers/__init__.py

    r1 r2  
    4141 
    4242providers = { 
    43     "access": "geniusql.providers.ado.StorageManagerADO_MSAccess", 
    44     "msaccess": "geniusql.providers.ado.StorageManagerADO_MSAccess", 
     43    "access": "geniusql.providers.ado.MSAccessDatabase", 
     44    "msaccess": "geniusql.providers.ado.MSAccessDatabase", 
    4545     
    46     "firebird": "geniusql.providers.firebird.StorageManagerFirebird", 
    47     "mysql": "geniusql.providers.mysql.StorageManagerMySQL", 
     46    "firebird": "geniusql.providers.firebird.FirebirdDatabase", 
     47    "mysql": "geniusql.providers.mysql.MySQLDatabase", 
    4848     
    49     "postgres": "geniusql.providers.pypgsql.StorageManagerPgSQL", 
    50     "postgresql": "geniusql.providers.pypgsql.StorageManagerPgSQL", 
    51     "pypgsql": "geniusql.providers.pypgsql.StorageManagerPgSQL", 
     49    "postgres": "geniusql.providers.pypgsql.PgDatabase", 
     50    "postgresql": "geniusql.providers.pypgsql.PgDatabase", 
     51    "pypgsql": "geniusql.providers.pypgsql.PgDatabase", 
    5252     
    53     "psycopg": "geniusql.providers.psycopg.StorageManagerPsycoPg", 
    54     "psycopg2": "geniusql.providers.psycopg.StorageManagerPsycoPg", 
     53    "psycopg": "geniusql.providers.psycopg.PsycoPgDatabase", 
     54    "psycopg2": "geniusql.providers.psycopg.PsycoPgDatabase", 
    5555     
    56     "sqlite": "geniusql.providers.sqlite.StorageManagerSQLite", 
     56    "sqlite": "geniusql.providers.sqlite.SQLiteDatabase", 
    5757     
    58     "sqlserver": "geniusql.providers.ado.StorageManagerADO_SQLServer", 
    59     "mssql": "geniusql.providers.ado.StorageManagerADO_SQLServer", 
     58    "sqlserver": "geniusql.providers.ado.SQLServerDatabase", 
     59    "mssql": "geniusql.providers.ado.SQLServerDatabase", 
    6060    } 
  • trunk/geniusql/providers/ado.py

    r1 r2  
    917917        return '%s %s%s' % (column.qname, dbtype, clause) 
    918918     
     919    def _grab_new_ids(self, table, idkeys, conn): 
     920        """Insert a row using the table's SERIAL field.""" 
     921        # For some reason, using SCOPE_IDENTITY or IDENTITY failed (returned 
     922        # None) when retrieving ID's just after a 99-thread-test ran. Moving 
     923        # the multithreading test fixed it. IDENT_CURRENT worked regardless. 
     924        data, _ = self.fetch("SELECT IDENT_CURRENT('%s');" % table.qname, conn) 
     925        return {idkeys[0]: data[0][0]} 
     926     
    919927    #                            Transactions                             # 
    920928     
     
    11271135        return '%s %s' % (column.qname, dbtype) 
    11281136     
     1137    def _grab_new_ids(self, table, idkeys, conn): 
     1138        data, _ = self.fetch("SELECT @@IDENTITY;", conn) 
     1139        return {idkeys[0]: data[0][0]} 
     1140     
    11291141    #                             Connecting                              # 
    11301142     
  • trunk/geniusql/providers/mysql.py

    r1 r2  
    11""" 
    2 References the MySQLdb package at: 
     2Uses the MySQLdb package at: 
    33http://sourceforge.net/projects/mysql-python 
    44 
  • trunk/geniusql/providers/psycopg.py

    r1 r2  
    2020replace_unoct = lambda m: chr(int(m.group(1), 8)) 
    2121 
    22 import dejavu 
    23 from dejavu import errors 
    24 from dejavu.storage import db 
    25  
    26  
    27 class AdapterToPsycoPg(db.AdapterToSQL): 
     22import geniusql 
     23from geniusql import errors, typerefs 
     24 
     25 
     26class AdapterToPsycoPg(geniusql.AdapterToSQL): 
    2827     
    2928    like_escapes = [("%", r"\\%"), ("_", r"\\_")] 
     
    7271 
    7372 
    74 class AdapterFromPsycoPg(db.AdapterFromDB): 
     73class AdapterFromPsycoPg(geniusql.AdapterFromDB): 
    7574     
    7675    def coerce_any_to_str(self, value): 
     
    9291 
    9392 
    94 class PsycoPgDecompiler(db.SQLDecompiler): 
     93class PsycoPgDecompiler(geniusql.SQLDecompiler): 
    9594     
    9695    def dejavu_icontainedby(self, op1, op2): 
    97         if isinstance(op1, db.ConstWrapper): 
     96        if isinstance(op1, geniusql.ConstWrapper): 
    9897            # Looking for text in a field. Use ILike (reverse terms). 
    9998            return op2 + " ILIKE '%" + self.adapter.escape_like(op1) + "%'" 
     
    124123 
    125124 
    126 class PsycoPgIndexSet(db.IndexSet): 
     125class PsycoPgIndexSet(geniusql.IndexSet): 
    127126     
    128127    def __delitem__(self, key): 
     
    137136 
    138137 
    139 class PsycoPgTable(db.Table): 
     138class PsycoPgTable(geniusql.Table): 
    140139     
    141140    indexsetclass = PsycoPgIndexSet 
    142141 
    143142 
    144 class PsycoPgDatabase(db.Database): 
     143class PsycoPgDatabase(geniusql.Database): 
    145144     
    146145    sql_name_max_length = 63 
     
    217216            else: 
    218217                dbtype = None 
    219             c = db.Column(row[0], self.quote(row[0]), dbtype, 
    220                           key=row[2] in indices) 
     218            c = geniusql.Column(row[0], self.quote(row[0]), dbtype, 
     219                                key=row[2] in indices) 
    221220             
    222221            if dbtype in ('FLOAT4', 'FLOAT8'): 
     
    279278                                  "WHERE attrelid = %s AND attnum = %s" 
    280279                                  % (table_OID, col), conn=conn) 
    281                 i = db.Index(row[0], self.quote(row[0]), tablename, 
    282                              d[0][0], bool(row[3])) 
     280                i = geniusql.Index(row[0], self.quote(row[0]), tablename, 
     281                                   d[0][0], bool(row[3])) 
    283282                indices.append(i) 
    284283         
     
    297296            return float 
    298297        elif dbtype.startswith('NUMERIC'): 
    299             if db.decimal: 
    300                 return db.decimal.Decimal 
    301             elif db.fixedpoint: 
    302                 return db.fixedpoint.FixedPoint 
     298            if typerefs.decimal: 
     299                return typerefs.decimal.Decimal 
     300            elif typerefs.fixedpoint: 
     301                return typerefs.fixedpoint.FixedPoint 
    303302        elif dbtype == 'DATE': 
    304303            return datetime.date 
     
    355354     
    356355    def sql_name(self, name): 
    357         name = db.Database.sql_name(self, name) 
     356        name = geniusql.Database.sql_name(self, name) 
    358357        if not self.quote_all: 
    359358            name = name.lower() 
     
    369368        except _psycopg.DatabaseError, x: 
    370369            if x.args[0].startswith('could not connect'): 
    371                 raise db.OutOfConnectionsError() 
     370                raise errors.OutOfConnectionsError() 
    372371            raise 
    373372     
     
    458457        return "%s\npsycopg version: %s" % (v, _psycopg.__version__) 
    459458 
    460  
    461  
    462 class StorageManagerPsycoPg(db.StorageManagerDB): 
    463     """StoreManager to save and retrieve Units via psycopg2.""" 
    464      
    465     databaseclass = PsycoPgDatabase 
    466      
    467     def __init__(self, arena, allOptions={}): 
    468         for atom in allOptions['Connect'].split(" "): 
    469             k, v = atom.split("=", 1) 
    470             if k == "dbname": 
    471                 allOptions['name'] = v 
    472         db.StorageManagerDB.__init__(self, arena, allOptions) 
    473      
    474     def _seq_UnitSequencerInteger(self, unit): 
    475         """Reserve a unit using the table's SERIAL field.""" 
    476         cls = unit.__class__ 
    477         t = self.db[cls.__name__] 
    478          
    479         fields = [] 
    480         values = [] 
    481         for key in cls.properties: 
    482             col = t[key] 
    483             if col.autoincrement: 
    484                 # Skip this field, since we're using a sequencer 
    485                 continue 
    486             val = self.db.adaptertosql.coerce(getattr(unit, key), col.dbtype) 
    487             fields.append(col.qname) 
    488             values.append(val) 
    489          
    490         transconn = self.db.get_transaction() 
    491          
    492         fields = ", ".join(fields) 
    493         values = ", ".join(values) 
    494         self.db.execute('INSERT INTO %s (%s) VALUES (%s);' % 
    495                         (t.qname, fields, values), transconn) 
    496          
    497         # Grab the new ID. This is threadsafe because db.reserve has a mutex. 
    498         idcol = cls.identifiers[0] 
    499         seqname = t[idcol].sequence_name 
    500         data, col_defs = self.db.fetch("SELECT last_value FROM %s;" % seqname, 
    501                                        transconn) 
    502         setattr(unit, idcol, data[0][0]) 
    503  
  • trunk/geniusql/providers/pypgsql.py

    r1 r2  
    415415        c.finish() 
    416416        return v 
    417  
     417     
     418    def _grab_new_ids(self, table, idkeys, conn): 
     419        """Insert a row using the table's SERIAL field.""" 
     420        newids = {} 
     421        for idkey in idkeys: 
     422            col = table[idkey] 
     423            seq = col.sequence_name 
     424            data, _ = self.fetch("SELECT last_value FROM %s;" % seq, conn) 
     425            newids[idkey] = data[0][0] 
     426        return newids 
     427 
  • trunk/geniusql/providers/sqlite.py

    r1 r2  
    11import datetime 
    22import os 
    3 import threading 
    43import time 
    54 
    6 import dejavu 
    7 from dejavu import errors, logic, storage 
    8 from dejavu.storage import db, isolation as _isolation 
     5import geniusql 
     6from geniusql import errors, typerefs, providers 
    97 
    108try: 
     
    1210    # This assumes the one built into Python 2.5+ 
    1311    import _sqlite3 as _sqlite 
    14     _version = storage.Version(_sqlite.sqlite_version) 
     12    _version = providers.Version(_sqlite.sqlite_version) 
    1513    _cursor_required = True 
    1614    _fetchall_required = True 
     
    2119    # using e.g. pysqlite 1.1.7 
    2220    import _sqlite 
    23     _version = storage.Version(_sqlite.sqlite_version()) 
     21    _version = providers.Version(_sqlite.sqlite_version()) 
    2422    _cursor_required = False 
    2523    _fetchall_required = False 
     
    2826 
    2927# ESCAPE keyword was added Nov 2004, 1 month after 3.0.8 release. 
    30 _escape_support = (_version > storage.Version([3, 0, 8])) 
     28_escape_support = (_version > providers.Version([3, 0, 8])) 
    3129if not _escape_support: 
    3230    _escape_warning = ("Version %s of sqlite does not support " 
    3331                       "wildcard literals." % _version) 
    34     import warnings 
    35     warnings.warn(_escape_warning, errors.StorageWarning) 
    36  
    37 _add_column_support = (_version >= storage.Version([3, 2, 0])) 
    38 _rename_table_support = (_version >= storage.Version([3, 1, 0])) 
    39 _autoincrement_support = (_version >= storage.Version([3, 1, 0])) 
    40 _cast_support = (_version >= storage.Version([3, 2, 3])) 
    41  
    42 class AdapterToSQLite(db.AdapterToSQL): 
     32    errors.warn(_escape_warning) 
     33 
     34_add_column_support = (_version >= providers.Version([3, 2, 0])) 
     35_rename_table_support = (_version >= providers.Version([3, 1, 0])) 
     36_autoincrement_support = (_version >= providers.Version([3, 1, 0])) 
     37_cast_support = (_version >= providers.Version([3, 2, 3])) 
     38 
     39 
     40class AdapterToSQLite(geniusql.AdapterToSQL): 
    4341     
    4442    # C-style backslash escapes are not supported. 
     
    6058 
    6159 
    62 class AdapterFromSQLite(db.AdapterFromDB): 
     60class AdapterFromSQLite(geniusql.AdapterFromDB): 
    6361     
    6462    def coerce_any_to_bool(self, value): 
     
    7068 
    7169 
    72 class SQLiteDecompiler(db.SQLDecompiler): 
     70class SQLiteDecompiler(geniusql.SQLDecompiler): 
    7371     
    7472    def visit_COMPARE_OP(self, lo, hi): 
     
    8381            # comparison. 
    8482            for i, op in enumerate((op1, op2)): 
    85                 if (isinstance(op, db.ConstWrapper) and 
     83                if (isinstance(op, geniusql.ConstWrapper) and 
    8684                    isinstance(op.basevalue, (datetime.date, datetime.datetime))): 
    8785                    op1 = "julianday(" + op1 + ")" 
     
    10098            self.stack.append(op2) 
    10199         
    102         db.SQLDecompiler.visit_COMPARE_OP(self, lo, hi) 
     100        geniusql.SQLDecompiler.visit_COMPARE_OP(self, lo, hi) 
    103101     
    104102    def attr_startswith(self, tos, arg): 
     
    121119     
    122120    def containedby(self, op1, op2): 
    123         if isinstance(op1, db.ConstWrapper): 
     121        if isinstance(op1, geniusql.ConstWrapper): 
    124122            # Looking for text in a field. Use Like (reverse terms). 
    125123            if _escape_support: 
     
    136134     
    137135    def dejavu_icontainedby(self, op1, op2): 
    138         if isinstance(op1, db.ConstWrapper): 
     136        if isinstance(op1, geniusql.ConstWrapper): 
    139137            # Looking for text in a field. Use Like (reverse terms). 
    140138            if _escape_support: 
     
    181179        else: 
    182180            self.imperfect = True 
    183             return db.cannot_represent 
     181            return geniusql.cannot_represent 
    184182     
    185183    dejavu_today = dejavu_now 
     
    190188        else: 
    191189            self.imperfect = True 
    192             return db.cannot_represent 
     190            return geniusql.cannot_represent 
    193191     
    194192    def dejavu_month(self, x): 
     
    197195        else: 
    198196            self.imperfect = True 
    199             return db.cannot_represent 
     197            return geniusql.cannot_represent 
    200198     
    201199    def dejavu_day(self, x): 
     
    204202        else: 
    205203            self.imperfect = True 
    206             return db.cannot_represent 
    207  
    208  
    209 class TypeAdapterSQLite(db.TypeAdapter): 
     204            return geniusql.cannot_represent 
     205 
     206 
     207class TypeAdapterSQLite(geniusql.TypeAdapter): 
    210208    """For a column and Python type, return a database type. 
    211209     
     
    262260 
    263261 
    264 class TypeAdapterSQLiteTypeless(db.TypeAdapter): 
     262class TypeAdapterSQLiteTypeless(geniusql.TypeAdapter): 
    265263     
    266264    def coerce(self, col, pytype): 
     
    270268 
    271269 
    272 class SQLiteTable(db.Table): 
     270class SQLiteTable(geniusql.Table): 
    273271     
    274272    def _parent_key(self): 
     
    418416 
    419417 
    420 class SQLiteSelectWriter(db.SelectWriter): 
     418class SQLiteSelectWriter(geniusql.SelectWriter): 
    421419     
    422420    def _joinclause(self, join): 
    423421        on_clauses = [] 
    424422         
    425         t1, t2 = join.table1, unitjoin.table2 
    426         if isinstance(t1, db.Join): 
     423        t1, t2 = join.table1, join.table2 
     424        if isinstance(t1, geniusql.Join): 
    427425            name1, oc = self._joinclause(t1) 
    428426            on_clauses.extend(oc) 
     
    433431            tlist1 = [t1] 
    434432         
    435         if isinstance(t2, Join): 
     433        if isinstance(t2, geniusql.Join): 
    436434            name2, oc = self._joinclause(t2) 
    437435            on_clauses.extend(oc) 
     
    476474 
    477475 
    478 class SQLiteDatabase(db.Database): 
     476class SQLiteDatabase(geniusql.Database): 
    479477     
    480478    sql_name_max_length = 0 
     
    488486    tableclass = SQLiteTable 
    489487     
     488    def __init__(self, name, **kwargs): 
     489        if name != ':memory:': 
     490            if not os.path.isabs(name): 
     491                name = os.path.join(os.getcwd(), name) 
     492        geniusql.Database.__init__(self, name, **kwargs) 
     493     
     494    def _get_upd(self): 
     495        return self.adaptertosql.using_perfect_dates 
     496    def _set_upd(self, value): 
     497        self.adaptertosql.using_perfect_dates = value 
     498    using_perfect_dates = property(_get_upd, _set_upd) 
     499     
    490500    def isrelatedtype(self, pytype1, pytype2): 
    491501        if (self.using_perfect_dates and 
     
    493503            issubclass(pytype2, self.python_type(self.typeadapter.coerce(None, pytype1)))): 
    494504            return True 
    495         return db.Database.isrelatedtype(self, pytype1, pytype2) 
     505        return geniusql.Database.isrelatedtype(self, pytype1, pytype2) 
    496506     
    497507    def _get_tables(self, conn=None): 
     
    516526        cols = [] 
    517527        for row in data: 
    518             c = db.Column(row[1], self.quote(row[1]), row[2].upper(), 
    519                           default=row[4], key=bool(row[5])) 
     528            c = geniusql.Column(row[1], self.quote(row[1]), row[2].upper(), 
     529                                default=row[4], key=bool(row[5])) 
    520530             
    521531            # "A single row can hold up to 2 ** 30 bytes of data 
     
    553563            if row[2]: 
    554564                colname = row[2].split("(")[-1] 
    555                 i = db.Index(row[0], self.quote(row[0]), row[1], colname[1:-2]) 
     565                i = geniusql.Index(row[0], self.quote(row[0]), 
     566                                   row[1], colname[1:-2]) 
    556567                indices.append(i) 
    557568        return indices 
     
    565576            return float 
    566577        elif dbtype == "NUMERIC": 
    567             if db.decimal: 
    568                 return db.decimal.Decimal 
    569             elif db.fixedpoint: 
    570                 return db.fixedpoint.FixedPoint 
     578            if typerefs.decimal: 
     579                return typerefs.decimal.Decimal 
     580            elif typerefs.fixedpoint: 
     581                return typerefs.fixedpoint.FixedPoint 
    571582        elif dbtype.startswith("INTEGER"): 
    572583            return int 
     
    695706            # http://www.sqlite.org/cvstrac/wiki?p=InMemoryDatabase 
    696707            # So we need to give :memory: databases a SingleConnection. 
    697             self.connection = db.SingleConnection(self._get_conn, self._del_conn) 
    698         else: 
    699             return db.Database.connect(self) 
     708            self.connection = geniusql.SingleConnection(self._get_conn, self._del_conn) 
     709        else: 
     710            return geniusql.Database.connect(self) 
    700711     
    701712    if _cursor_required: 
     
    750761            isolation = self.default_isolation 
    751762         
    752         if isinstance(isolation, _isolation.IsolationLevel): 
     763        if isinstance(isolation, geniusql.IsolationLevel): 
    753764            # Map the given IsolationLevel object to a native value. 
    754765            # This base class uses the four ANSI names as native values. 
     
    802813                         msg == "database schema has changed")): 
    803814                        tx = self.transactions.get(self.transaction_key()) 
    804                         if tx is None or isinstance(tx, db.TransactionLock): 
     815                        if tx is None or isinstance(tx, errors.TransactionLock): 
    805816                            # Bah. Shut down all connections and get a new one, 
    806817                            # since some previous connection changed the schema. 
     
    834845            res = self.execute(query, conn) 
    835846            return res.row_list, res.col_defs 
    836  
    837  
    838 class StorageManagerSQLite(db.StorageManagerDB): 
    839     """StoreManager to save and retrieve Units via _sqlite.""" 
    840      
    841     databaseclass = SQLiteDatabase 
    842      
    843     def __init__(self, arena, allOptions={}): 
    844         allOptions = allOptions.copy() 
    845         dbfile = allOptions.pop('Database', '') 
    846         if dbfile != ':memory:': 
    847             if not os.path.isabs(dbfile): 
    848                 dbfile = os.path.join(os.getcwd(), dbfile) 
    849         allOptions['name'] = dbfile 
    850         allOptions['mode'] = int(allOptions.pop('Mode', '0755'), 8) 
    851         db.StorageManagerDB.__init__(self, arena, allOptions) 
    852         pd = str(allOptions.get('Perfect Dates', 'False')).lower() == "true" 
    853         self.databaseclass.using_perfect_dates = pd 
    854         self.databaseclass.adaptertosql.using_perfect_dates = pd 
    855847     
    856848    def version(self): 
    857849        return "SQLite Version: %s" % _version 
    858      
    859     def reserve(self, unit): 
    860         """reserve(unit). -> Reserve a persistent slot for unit.""" 
    861         self.reserve_lock.acquire() 
    862         try: 
    863             # First, see if our db subclass has a handler that 
    864             # uses the DB to generate the appropriate identifier(s). 
    865             seqclass = unit.sequencer.__class__.__name__ 
    866             if seqclass == "UnitSequencerInteger" and _autoincrement_support: 
    867                 self._seq_UnitSequencerInteger(unit) 
    868             else: 
    869                 self._manual_reserve(unit) 
    870             unit.cleanse() 
    871         finally: 
    872             self.reserve_lock.release() 
    873      
    874     def _seq_UnitSequencerInteger(self, unit): 
    875         """Reserve a unit using the table's AUTOINCREMENT field.""" 
    876         cls = unit.__class__ 
    877         t = self.db[cls.__name__] 
    878          
    879         fields = [] 
    880         values = [] 
    881         for key in cls.properties: 
    882             col = t[key] 
    883             if col.autoincrement: 
    884                 # Skip this field, since we're using AUTOINCREMENT 
    885                 continue 
    886             val = self.db.adaptertosql.coerce(getattr(unit, key), col.dbtype) 
    887             fields.append(col.qname) 
    888             values.append(val) 
    889          
    890         fields = ", ".join(fields) 
    891         values = ", ".join(values) 
    892          
    893         # Use the same conn for INSERT and last row id 
    894         conn = self.db.get_transaction() 
    895         self.db.execute('INSERT INTO %s (%s) VALUES (%s);' % 
    896                         (t.qname, fields, values), conn) 
    897          
    898         # Grab the new ID. This is safe because db.reserve has a mutex. 
    899         if _lastrowid_support: 
    900             new_id = conn.lastrowid 
    901         else: 
    902             new_id = conn.sqlite_last_insert_rowid() 
    903         setattr(unit, cls.identifiers[0], new_id) 
    904      
    905     #                               Schemas                               # 
    906      
    907     def _make_column(self, cls, key): 
    908         col = db.StorageManagerDB._make_column(self, cls, key) 
    909         if col.autoincrement and not _autoincrement_support: 
    910             col.autoincrement = False 
    911             col.initial = 0 
    912         return col 
    913  
     850 
  • trunk/geniusql/select.py

    r1 r2  
    606606        portion of an SQL JOIN clause. 
    607607        """ 
    608         ref = A.table.references.get(path or B.table.name, None) 
     608        if path is None: 
     609            db = B.table.db 
     610            for tkey in db: 
     611                if db[tkey] is B.table: 
     612                    path = tkey 
     613                    break 
     614        ref = A.table.references.get(path, None) 
    609615        if ref: 
    610616            nearkey, _, farkey = ref