[sqlalchemy] Re: SQLError vs. FlushError

2006-11-11 Thread Michael Bayer

it depends on who is generating the error.  in 0.3.0 we have some  
more automated code for reconciling two objects, one of which is  
deleted and the other saved, with the same primary key value.  this  
is handy for association object mappings in particular.  so when  
detecting an invalid condition related to that, it raises a  
FlushError, which is an error that was raised without issuing any SQL  
to the database.  Whereas a SQLError is always generated by the  
database in response to a SQL execution, and someday we hope to even  
have the databases' error codes attached to those as well (which is  
hard becuase none of the DBAPIs seem to support this, with the  
possible exception of psycopg which doesnt document it).

I dont think its appropriate for a non-SQL operation to raise a  
SQLError.  also for the flush-time SQLErrors to be FlushErrors, we'd  
have  to wrap one inside the other.  which seems a little weird to me  
since we arent wrapping any other kind of errors into FlushError.

the only solution i can see is that we dont actually raise the  
FlushError when we detect that condition and just let the bad primary  
key go through.  but that doenst fix anything beyond this issue; im  
sure theres a lot of other conditions that are on the borderline  
between being detected at organization time vs. sql execution time.



On Nov 10, 2006, at 11:18 PM, Sol wrote:


 Hello list,
 while migrating from 0.2.8 to 0.3.0 I realized some changes in (error
 raising) behavior.

 If you try to flush an object with a duplicated primary key and the  
 object
 (the one that existed first!) is in your session you get a FlushError
 (previously you got SQLError).

 If you do the same with the other object not in your session you  
 get a SQLError.

 If you try to flush with a conflicts due to an unique constraint  
 you alway
 get a SQLError.

 Bug or feature?
 -- 
 Cheers, Sol.

 


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Constructing where-clauses dynamically

2006-11-11 Thread Daniel Miller


BTW, this post started out as a reply to James but grew into a collective 
summary of my thoughts on the entire thread so far. You've been warned :)

James Taylor wrote:
 But wouldn't the join from orders to items be OUTER in this case  
 since you can have orders with a total  50 but no items. (Your  
 application logic probably forbids this, but it is allowed at the  
 relational level).
 
 Gets complicated quick!

Yes, that's one of the things I started to dislike as I got more into the 
details. The 'obj1.obj2.obj3' join syntax does not imply the type of join (i.e. 
inner, outer, cross, etc.).  My initial thought is to say that this type of 
join syntax would always use inner joins, which is easy enough to remember.

One thing I'm not quite clear on is how the join type is specified with the 
other join syntaxes in SA. There's join_to/join_via and Mike talked about 
from_obj=[users.join(ordertable).join(...)], but both of those are just as 
ambiguous as obj1.obj2.obj3 when it comes to the join type (and they're a lot 
more verbose at that). I guess SelectResults has outerjoin_to()...more on that 
later.

Here is a more complete join syntax proposal:

INNER JOIN (join path syntax sugar):
User.c.orders
User.c.orders.items

INNER JOIN (non-sugar):
join(User.c.orders)
join(User.c.orders.items)
join(User, Order, User.c.user_id  Order.c.user_id)

OUTER JOIN:
outerjoin(User.c.orders)
outerjoin(User.c.orders.items)
outerjoin(User, Order, User.c.user_id  Order.c.user_id)

RANDOM JOIN:
random_join(User.c.orders)

Just kidding on that last one :P

Each join function requires one or three positional arguments. The single-arg 
version takes a join path as it's argument, and performs the join type 
represented by function on all joins along the path. A stand-alone join path is 
syntax sugar for join(join path). The three-arg version takes two join-able 
objects and a condition on which to join them. I realize that a two-arg version 
is also available where SA tries to find a relationship between the first two 
args, and IMHO that should be deprecated because it is error-prone.

To do a join based on a mapper relationship, use a join path. To do an ad-hoc 
join use the three-argument version.

In addition to join paths and classes, the 3-arg version should also accept 
tables, strings (entity names), select clauses, and other join objects as 
arguments. When using anything other than a join path as the first argument, 
three arguments are required. So this:

outerjoin(
join(User.c.orders),
Items,
Order.c.order_id == Item.c.order_id
)

and would generate this:

FROM users
INNER JOIN orders
ON users.user_id = orders.user_id
LEFT OUTER JOIN items
ON orders.order_id = items.order_id

An optional 'alias' keyword argument may also be specified in a join function. 
If the single-arg version is used then the alias applies to the last table in 
the join path. Otherwise the alias applies to the second argument.

join(User.c.orders, alias='ox')

join(
User, Order,
User.c.user_id == Order.c.user_id,
alias='ox'
)

both of these expressions generate the following (pseudo) SQL:

FROM users
LEFT OUTER JOIN orders ox
ON users.user_id = ox.user_id



Possible alternative syntax:

User.c.join(Order)

The biggest problem with this alternative is that it clutters the User.c 
namespace. Like we were saying earlier:

 One thing we might want to change is the 'alias' function.  
 Since the 'orders' table may have a column named 'alias', it might  
 be better to make it a stand-alone function like this:
 alias(c.orders, 'ox')

 Mike wrote:
 i think this is the problem when you start munging namespaces  
 together, as opposed to grouping them under explicit prefixes (i.e.  
 'c').

But 'c' is actually a conglomeration of namespaces including columns, 
functions, operations, etc. 'c' should only contain columns and relationships 
and everything else should be moved to a separate package (maybe 
sqlalchemy.query or even the root sqlalchemy package).


I also think something similar to the SelectResults transformative style of 
creating joins would be useful if it was built-in to Query (with a few name 
changes such as 'join_to' should be 'join' and 'outerjoin_to' should be 
'outer_join'). One thing I don't like about SelectResults is that it gives 
special attention to a few functions (avg, sum, min, max, etc.). These 
functions can incorporated into a query with the more versatile 'func.xyz' 
syntax, and would just add to the confusion about how to use functions.


Anyway, there's another brain dump for now.

~ Daniel

--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en