From: InetDavid on
On May 5, 1:06 am, "Donal K. Fellows"
<donal.k.fell...(a)manchester.ac.uk> wrote:
> On 4 May, 22:59, "tom.rmadilo" <tom.rmad...(a)gmail.com> wrote:
>
> > SELECT
> >  f_name || ' ' || l_name as full_name,
> >  last_four(ssn)
> >  from emp
> > order by
> >  upper(l_name)
> > where
> >  l_name like 'X%';
>
> > This requires a new type of object/record. This is a simple example,
> > table joins is another example. The point is that every query which
> > returns rows creates new object types/record types, so they require a
> > new mapping. The above SQL query above describes the mapping.
>
> Quite apart from the fact that your example probably gets the name
> composition wrong (names, like addresses[*], phone numbers and dates,
> are trickier than they appear to be at first) the query above does not
> actually describe a record. It describes an ordered collection of
> records.
>
> This means that the above would have to map to some kind of "list
> objects" operation that is not a method of the instances being
> returned. It's more like a constructor, except it's not really that
> either; it's a "list objects" method. :-) Luckily, if we look at how
> real ORM schemes work, they tend to include space for this sort of
> thing on the factory/discovery classes. If we were using TclOO, I'd be
> strongly tempted to make it into a method on the class object, perhaps
> to be called like this:
>
>   set emps [db::emp listByLastNameStartingWithX]
>
> OK, that's an ugly name but even so. Yes, the mapping systems that
> I've seen, such as EJB entity beans, do it this way. (Mind you, you'd
> need to fetch some kind of unique ID for each row too; otherwise
> there's no way to manage the identity of the returned objects.)
>
> > Select queries also have search parameters, how do you name them and
> > associate them with a column?
>
> You can't automatically do all that. ORMs do not expose all the
> features and power of a relational database automatically. OTOH, the
> practical ones do let you get control over the mapping so you can
> write the queries and have them within the system; it's more work than
> fully automated, but a lot more powerful too. I could certainly
> imagine that the above query might be generalized to:
>
>   set emps [db::emp findByLastName -match "X%"]
>
> Then I could do this:
>
>   foreach e $emps {
>      set n   [$e -full_name]
>      set ssn [$e -last_four_ssn_]; # OK, I remapped the name
>      puts "$n -> $ssn"
>   }
>
> (Damn! That might be a usable API!)
>
> I'd not expect writes back to the DB to work at all in this case,
> given that all the (non-ID) columns are the result of expressions.
>
> Donal.

Good catch, Donal. That's how ActiveRecord models handle cases like
that. Just define methods on the derived class to handle special
attributes. A more common method is to retrieve the entire record and
just create methods to display data in a different way. For example,
creating a "full_name" method to concatenate "first_name" and
"last_name" columns. This could be done in my TclOO version in the
derived class like this:

oo::class create Client {
superclass ActiveRecord::Base

# "Glue" to pick up ActiveRecord::Base class methods
classmethod unknown args { ::ActiveRecord::Base {*}$args }
oo::objdefine State unexport create

method full_name { {new_name {}} } {
if { $new_name != {} } {
my first_name [lindex $new_name 0] ;# Simple version for
example
my last_name [lindex $new_name 1]
} else {
return [concat [my first_name] " " [my last_name]]
}
}
}

Then you just retrieve the records as normal and access full_name from
the object(s):

% set client [Client find_by_first_name_and_last_name First Last]
oo::Obj6
% puts [$client full_name]
First Last
% $client full_name "New Name"
New Name
% puts [$client full_name]
New Name
% $client save

This allows you to *set* names by full name (which sets first_name and
last_name) and you can then *save* the records.

BTW, the "find_by_" method above *does* work now in my
implementation. It's a dynamic finder using method unknown and would
generate "SELECT * FROM `clients` WHERE `states`.`first_name` =
'First' AND `states`.`last_name` = 'Last' LIMIT 1".

David
From: tom.rmadilo on
On May 5, 1:06 am, "Donal K. Fellows"
<donal.k.fell...(a)manchester.ac.uk> wrote:
> On 4 May, 22:59, "tom.rmadilo" <tom.rmad...(a)gmail.com> wrote:
>
> > SELECT
> >  f_name || ' ' || l_name as full_name,
> >  last_four(ssn)
> >  from emp
> > order by
> >  upper(l_name)
> > where
> >  l_name like 'X%';
>
> > This requires a new type of object/record. This is a simple example,
> > table joins is another example. The point is that every query which
> > returns rows creates new object types/record types, so they require a
> > new mapping. The above SQL query above describes the mapping.
>
> Quite apart from the fact that your example probably gets the name
> composition wrong (names, like addresses[*], phone numbers and dates,
> are trickier than they appear to be at first) the query above does not
> actually describe a record. It describes an ordered collection of
> records.

The collection is implicit, the record is defined in the first part of
the SELECT statement. A valid SELECT statement defines a collection of
zero or more records. Unless you include an ordering clause, the
collection is unordered. IMHO, every statement should include an
ordering clause.

> This means that the above would have to map to some kind of "list
> objects" operation that is not a method of the instances being
> returned. It's more like a constructor, except it's not really that
> either; it's a "list objects" method. :-) Luckily, if we look at how
> real ORM schemes work, they tend to include space for this sort of
> thing on the factory/discovery classes.

This isn't necessarily true. Any database system which returns this
result list of records (result table) could auto-generate objects
within the context of the caller. It could also return a list of these
objects if you need to iterate over them. This is my preference: a
query returns a list of references of created objects.

> If we were using TclOO, I'd be
> strongly tempted to make it into a method on the class object, perhaps
> to be called like this:
>   set emps [db::emp listByLastNameStartingWithX]
> OK, that's an ugly name but even so. Yes, the mapping systems that
> I've seen, such as EJB entity beans, do it this way. (Mind you, you'd
> need to fetch some kind of unique ID for each row too; otherwise
> there's no way to manage the identity of the returned objects.)

I would say that speaks well of EJB, but there is no guarantee that
the query objects can be updated. My example includes only one table,
but the system must be able to handle something more interesting:
joins.

Basically there are two separate problems that need to be solved with
ORM:

The first is simple mapping of a database record to some kind of list/
object in the programming language. This mapping can be easily
enhanced to provide any number of limited views of these records.

The second is generic queries, including most interesting select
queries. In this case, the query defines or must match the record
type. With Tcl, you can leverage the introspection and dynamic
capabilities to allow the query to define the record/object.

Unfortunately it is very difficult to match up these two. If we had
real SQL, as originally defined, then all views would be updateable
and the application developer would simply create views, but the fact
that no SQL database offers automatically updateable views should
inform programmers that the problem is difficult to solve.

> > Select queries also have search parameters, how do you name them and
> > associate them with a column?
>
> You can't automatically do all that. ORMs do not expose all the
> features and power of a relational database automatically. OTOH, the
> practical ones do let you get control over the mapping so you can
> write the queries and have them within the system; it's more work than
> fully automated, but a lot more powerful too. I could certainly
> imagine that the above query might be generalized to:
>
>   set emps [db::emp findByLastName -match "X%"]
>
> Then I could do this:
>
>   foreach e $emps {
>      set n   [$e -full_name]
>      set ssn [$e -last_four_ssn_]; # OK, I remapped the name
>      puts "$n -> $ssn"
>   }
>
> (Damn! That might be a usable API!)
>
> I'd not expect writes back to the DB to work at all in this case,
> given that all the (Send non-ID) columns are the result of expressions.

Your are describing what I call named queries. There is probably a
more formal name, but the basic idea is to create an interface to the
database system which removes SQL language from the user code, but
still allows "wrapped" SQL code which retains the full power of
whatever database you are using.

This also allows for multiple implementations without the need to
change user code. I like generic procedures, but your object based
interface ($e) would probably make it easier for end users to
specialize by selecting a different implementation.

I usually name queries like: moduleName::queryName, one example would
be dbis::SelectTableAttributes.

A named query just reflects a SQL view, which is also a named
query...although without parameters.
From: tom.rmadilo on
On May 5, 10:49 am, InetDavid <inetda...(a)gmail.com> wrote:
> On May 5, 1:06 am, "Donal K. Fellows"
>
>
>
>
>
> <donal.k.fell...(a)manchester.ac.uk> wrote:
> > On 4 May, 22:59, "tom.rmadilo" <tom.rmad...(a)gmail.com> wrote:
>
> > > SELECT
> > >  f_name || ' ' || l_name as full_name,
> > >  last_four(ssn)
> > >  from emp
> > > order by
> > >  upper(l_name)
> > > where
> > >  l_name like 'X%';
>
> > > This requires a new type of object/record. This is a simple example,
> > > table joins is another example. The point is that every query which
> > > returns rows creates new object types/record types, so they require a
> > > new mapping. The above SQL query above describes the mapping.
>
> > Quite apart from the fact that your example probably gets the name
> > composition wrong (names, like addresses[*], phone numbers and dates,
> > are trickier than they appear to be at first) the query above does not
> > actually describe a record. It describes an ordered collection of
> > records.
>
> > This means that the above would have to map to some kind of "list
> > objects" operation that is not a method of the instances being
> > returned. It's more like a constructor, except it's not really that
> > either; it's a "list objects" method. :-) Luckily, if we look at how
> > real ORM schemes work, they tend to include space for this sort of
> > thing on the factory/discovery classes. If we were using TclOO, I'd be
> > strongly tempted to make it into a method on the class object, perhaps
> > to be called like this:
>
> >   set emps [db::emp listByLastNameStartingWithX]
>
> > OK, that's an ugly name but even so. Yes, the mapping systems that
> > I've seen, such as EJB entity beans, do it this way. (Mind you, you'd
> > need to fetch some kind of unique ID for each row too; otherwise
> > there's no way to manage the identity of the returned objects.)
>
> > > Select queries also have search parameters, how do you name them and
> > > associate them with a column?
>
> > You can't automatically do all that. ORMs do not expose all the
> > features and power of a relational database automatically. OTOH, the
> > practical ones do let you get control over the mapping so you can
> > write the queries and have them within the system; it's more work than
> > fully automated, but a lot more powerful too. I could certainly
> > imagine that the above query might be generalized to:
>
> >   set emps [db::emp findByLastName -match "X%"]
>
> > Then I could do this:
>
> >   foreach e $emps {
> >      set n   [$e -full_name]
> >      set ssn [$e -last_four_ssn_]; # OK, I remapped the name
> >      puts "$n -> $ssn"
> >   }
>
> > (Damn! That might be a usable API!)
>
> > I'd not expect writes back to the DB to work at all in this case,
> > given that all the (non-ID) columns are the result of expressions.
>
> > Donal.
>
> Good catch, Donal.  That's how ActiveRecord models handle cases like
> that.  Just define methods on the derived class to handle special
> attributes.  A more common method is to retrieve the entire record and
> just create methods to display data in a different way.  For example,
> creating a "full_name" method to concatenate "first_name" and
> "last_name" columns.  This could be done in my TclOO version in the
> derived class like this:
>
> oo::class create Client {
>   superclass ActiveRecord::Base
>
>   # "Glue" to pick up ActiveRecord::Base class methods
>   classmethod unknown args { ::ActiveRecord::Base {*}$args }
>   oo::objdefine State unexport create
>
>   method full_name { {new_name {}} } {
>     if { $new_name != {} } {
>       my first_name [lindex $new_name 0]   ;# Simple version for
> example
>       my last_name [lindex $new_name 1]
>     } else {
>       return [concat [my first_name] " " [my last_name]]
>     }
>   }
>
> }
>
> Then you just retrieve the records as normal and access full_name from
> the object(s):
>
> % set client [Client find_by_first_name_and_last_name First Last]
> oo::Obj6
> % puts [$client full_name]
> First Last
> % $client full_name "New Name"
> New Name
> % puts [$client full_name]
> New Name
> % $client save
>
> This allows you to *set* names by full name (which sets first_name and
> last_name) and you can then *save* the records.
>
> BTW, the "find_by_" method above *does* work now in my
> implementation.  It's a dynamic finder using method unknown and would
> generate "SELECT * FROM `clients` WHERE `states`.`first_name` =
> 'First' AND `states`.`last_name` = 'Last' LIMIT 1".

So how do you know that "full_name" is a list? What happens if
first_name is "Dum Asse"?

% llength [concat {Dum Asse} Jackson]
3

From: Arnold Snarb on
tom.rmadilo wrote:

[ ... among other things ... ]

> If we had real SQL, as originally defined,
> then all views would be updateable

Wait, what?

I've heard griping that SQL doesn't implement
the True Relational Model (as envisioned by Codd,
prophesied by Date, and pounded into the ground
by Pascal); and I've heard grumblings that existing
RDBMSes don't even implement full SQL (as blessed
by ISO); but I have *never* heard that "Real SQL"
(or even that "Real Relational Databases") should
allow unrestricted updates through views.

That's just crazy talk!

In other words: "[citation needed]".


--Arnie
From: Arnold Snarb on
Arnold Snarb just wrote:
> tom.rmadilo wrote:
>> If we had real SQL, as originally defined,
>> then all views would be updateable
> Wait, what?
> [...]
> but I have *never* heard that "Real SQL" should
> allow unrestricted updates through views.
>
> That's just crazy talk!

Well I'll be! That's actually Codd's Rule 6:

"All views that are theoretically updatable must be updatable"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I stand corrected.

My apologies to tom.rmadillo, I failed to read between the lines when
you said that in "real SQL [...] all {theoretically updatable} views
are {actually} updateable". The intersquigglebraced elisions
should have been obvious.

Sorry, won't happen again.


--Arnie