- fix to oracle table aliasing

- added select.prefix_with(), adds arbitrary prefixes to a SELECT's columns clause, [ticket:504]
This commit is contained in:
Mike Bayer
2007-07-26 17:37:30 +00:00
parent 02a3be96e8
commit 28b86bcd4f
5 changed files with 53 additions and 18 deletions
+2
View File
@@ -181,6 +181,8 @@
- select(scalar=True) argument is deprecated; use select(..).as_scalar().
the resulting object obeys the full "column" interface and plays better
within expressions
- added select().with_prefix('foo') allowing any set of keywords to be
placed before the columns clause of the SELECT [ticket:504]
- result sets make a better attempt at matching the DBAPI types present
in cursor.description to the TypeEngine objects defined by the dialect,
which are then used for result-processing. Note this only takes effect
+1 -1
View File
@@ -527,7 +527,7 @@ class ANSICompiler(engine.Compiled):
collist = string.join(inner_columns.difference(util.Set([None])), ', ')
text = "SELECT "
text = " ".join(["SELECT"] + [self.process(x) for x in select._prefixes]) + " "
text += self.get_select_precolumns(select)
text += collist
+2 -2
View File
@@ -526,9 +526,9 @@ class OracleCompiler(ansisql.ANSICompiler):
"""Oracle doesn't like ``FROM table AS alias``. Is the AS standard SQL??"""
if asfrom:
return self.process(alias.original) + " " + alias.name
return self.process(alias.original, asfrom=asfrom, **kwargs) + " " + alias.name
else:
return self.process(alias.original)
return self.process(alias.original, **kwargs)
def visit_insert(self, insert):
"""``INSERT`` s are required to have the primary keys be explicitly present.
+32 -10
View File
@@ -752,6 +752,14 @@ def _literal_as_text(element):
else:
return element
def _literal_as_column(element):
if isinstance(element, Operators):
return element.clause_element()
elif _is_literal(element):
return literal_column(str(element))
else:
return element
def _literal_as_binds(element, name='literal', type_=None):
if isinstance(element, Operators):
return element.clause_element()
@@ -1285,16 +1293,16 @@ class _CompareMixin(ColumnOperators):
def __compare(self, op, obj, negate=None):
if obj is None or isinstance(obj, _Null):
if op == operator.eq:
return _BinaryExpression(self.clause_element(), null(), Operators.is_, negate=Operators.isnot)
return _BinaryExpression(self.expression_element(), null(), Operators.is_, negate=Operators.isnot)
elif op == operator.ne:
return _BinaryExpression(self.clause_element(), null(), Operators.isnot, negate=Operators.is_)
return _BinaryExpression(self.expression_element(), null(), Operators.isnot, negate=Operators.is_)
else:
raise exceptions.ArgumentError("Only '='/'!=' operators can be used with NULL")
else:
obj = self._check_literal(obj)
return _BinaryExpression(self.clause_element(), obj, op, type_=sqltypes.Boolean, negate=negate)
return _BinaryExpression(self.expression_element(), obj, op, type_=sqltypes.Boolean, negate=negate)
def __operate(self, op, obj):
obj = self._check_literal(obj)
@@ -1305,7 +1313,7 @@ class _CompareMixin(ColumnOperators):
if op == operator.add and isinstance(type_, (sqltypes.Concatenable)):
op = ColumnOperators.concat_op
return _BinaryExpression(self.clause_element(), obj, op, type_=type_)
return _BinaryExpression(self.expression_element(), obj, op, type_=type_)
operators = {
operator.add : (__operate,),
@@ -1413,6 +1421,11 @@ class _CompareMixin(ColumnOperators):
return other
def clause_element(self):
"""Allow ``_CompareMixins`` to return the underlying ``ClauseElement``, for non-``ClauseElement`` ``_CompareMixins``."""
return self
def expression_element(self):
"""Allow ``_CompareMixins`` to return the appropriate object to be used in expressions."""
return self
@@ -2547,9 +2560,9 @@ class _Label(ColumnElement):
_label = property(lambda s: s.name)
orig_set = property(lambda s:s.obj.orig_set)
def clause_element(self):
def expression_element(self):
return self.obj
def _copy_internals(self):
self.obj = self.obj._clone()
@@ -2923,7 +2936,7 @@ class Select(_SelectBaseMixin, FromClause):
"""
def __init__(self, columns, whereclause=None, from_obj=None, distinct=False, having=None, correlate=True, **kwargs):
def __init__(self, columns, whereclause=None, from_obj=None, distinct=False, having=None, correlate=True, prefixes=None, **kwargs):
"""construct a Select object.
The public constructor for Select is the [sqlalchemy.sql#select()] function;
@@ -2938,6 +2951,7 @@ class Select(_SelectBaseMixin, FromClause):
self._froms = util.OrderedSet()
self._whereclause = None
self._having = None
self._prefixes = []
if columns is not None:
for c in columns:
@@ -3148,6 +3162,11 @@ class Select(_SelectBaseMixin, FromClause):
s.distinct = True
return s
def prefix_with(self, clause):
s = self._generate()
s.append_prefix(clause)
return s
def select_from(self, fromclause):
s = self._generate()
s.append_from(fromclause)
@@ -3171,14 +3190,17 @@ class Select(_SelectBaseMixin, FromClause):
self.__correlate.add(fromclause)
def append_column(self, column):
if _is_literal(column):
column = literal_column(str(column))
column = _literal_as_column(column)
if isinstance(column, _ScalarSelect):
column = column.self_group(against=ColumnOperators.comma_op)
self._raw_columns.append(column)
def append_prefix(self, clause):
clause = _literal_as_text(clause)
self._prefixes.append(clause)
def append_whereclause(self, whereclause):
if self._whereclause is not None:
self._whereclause = and_(self._whereclause, _literal_as_text(whereclause))
+16 -5
View File
@@ -414,13 +414,18 @@ sq.myothertable_othername AS sq_myothertable_othername FROM (" + sqstring + ") A
def testalias(self):
# test the alias for a table1. column names stay the same, table name "changes" to "foo".
self.runtest(
select([alias(table1, 'foo')])
select([table1.alias('foo')])
,"SELECT foo.myid, foo.name, foo.description FROM mytable AS foo")
for dialect in (firebird.dialect(), oracle.dialect()):
self.runtest(
select([table1.alias('foo')])
,"SELECT foo.myid, foo.name, foo.description FROM mytable foo"
,dialect=dialect)
self.runtest(
select([alias(table1, 'foo')])
,"SELECT foo.myid, foo.name, foo.description FROM mytable foo"
,dialect=firebird.dialect())
select([table1.alias()])
,"SELECT mytable_1.myid, mytable_1.name, mytable_1.description FROM mytable AS mytable_1")
# create a select for a join of two tables. use_labels means the column names will have
# labels tablename_columnname, which become the column keys accessible off the Selectable object.
@@ -441,6 +446,12 @@ myothertable.otherid AS myothertable_otherid FROM mytable, myothertable \
WHERE mytable.myid = myothertable.otherid) AS t2view WHERE t2view.mytable_myid = :t2view_mytable_myid"
)
def test_prefixes(self):
self.runtest(table1.select().prefix_with("SQL_CALC_FOUND_ROWS").prefix_with("SQL_SOME_WEIRD_MYSQL_THING"),
"SELECT SQL_CALC_FOUND_ROWS SQL_SOME_WEIRD_MYSQL_THING mytable.myid, mytable.name, mytable.description FROM mytable"
)
def testtext(self):
self.runtest(
text("select * from foo where lala = bar") ,