Glorp port changeset and documentation

Nevin Pratt nevin at smalltalkpro.com
Sat Jun 1 01:59:39 UTC 2002


I've spent about a dozen hours playing around with Glorp under Squeak 
(using PostgreSQL).  I've got all of the Glorp SUnit tests passing.

THIS WAS DONE RAPIDLY, and BUGS WILL EXISTS!!  Consider it even earlier 
than an alpha.  But at least the tests all pass.

Attached you will find a MickeySoft Word document ('GlorpPort.doc') that 
contains my "diary" of what I did.  I hope you find it entertaining :-)  

Also attached you will find the 'glorp.cs' changeset with the code.

As you read the "diary", please note that there is workspace code listed 
in the "diary" that needs to be executed after you filein the changeset 
code.  If you don't execute that workspace code, the port won't work.

The diary also documents what image code you need to start with before 
filing in the changeset.

Nevin

-------------- next part --------------
A non-text attachment was scrubbed...
Name: GlorpPort.doc
Type: application/octet-stream
Size: 30720 bytes
Desc: not available
Url : http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20020531/3902ff9c/GlorpPort.obj
-------------- next part --------------
'From Squeak3.2gamma of 15 January 2002 [latest update: #4881] on 31 May 2002 at 7:30:35 pm'!
"Change Set:		glorp
Date:			31 May 2002
Author:			Nevin Pratt

First version of a quick and dirty port of glorp to Squeak"!

ProtoObject subclass: #MessageArchiver
	instanceVariableNames: 'myMessage myReceiver '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
Object subclass: #Address
	instanceVariableNames: 'id street number '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
Address class
	instanceVariableNames: ''!
Object subclass: #Airline
	instanceVariableNames: 'id name '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
Airline class
	instanceVariableNames: ''!
Object subclass: #AirlineMeal
	instanceVariableNames: 'id description ingredients '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
AirlineMeal class
	instanceVariableNames: ''!
Object subclass: #AttributeAccessor
	instanceVariableNames: 'attributeName attributeIndex '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
AttributeAccessor class
	instanceVariableNames: ''!
Object subclass: #BankAccount
	instanceVariableNames: 'id accountNumber accountHolders eventsReceived '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
BankAccount class
	instanceVariableNames: ''!
Object subclass: #BankAccountNumber
	instanceVariableNames: 'bankCode branchNumber accountNumber '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
BankAccountNumber class
	instanceVariableNames: ''!
Object subclass: #BankTransaction
	instanceVariableNames: 'id owner amount serviceCharge '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
BankTransaction class
	instanceVariableNames: ''!
Object subclass: #Cache
	instanceVariableNames: 'items policy mainCache '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!Cache commentStamp: '<historical>' prior: 0!
This is the per-class cache of instances read from the database.

Instance Variables:
	items	<Dictionary from: Object to: Object>	The cached items, keyed by their primary key values
	policy	<CachePolicy>	The settings for this cache.

!

Cache class
	instanceVariableNames: ''!
Object subclass: #CacheManager
	instanceVariableNames: 'subCaches session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!CacheManager commentStamp: '<historical>' prior: 0!
This is the entire cache for a session, consisting of multiple sub-caches, one per class.

Instance Variables:
	session	<Session>	The containing session.
	subCaches	<Dictionary from: Class to: Cache>	The per-class caches.

!

CacheManager class
	instanceVariableNames: ''!
Object subclass: #CachePolicy
	instanceVariableNames: 'size expiryAction '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!CachePolicy commentStamp: '<historical>' prior: 0!
A CachePolicy implements the different possible policies we might use for caching. The superclass implements the trivial policy of keeping all objects forever.

The policy also controls what we store in the cache. In general, it's assumed to be a cache entry of some sort, and the policy is responsible for wrapping and unwrapping objects going to and from the cache. The default policy is that the objects themselves are the cache entry (saving one object per cached object in overhead).

Instance Variables:
	size	<Number>	The minimum cache size we want to use.
	expiryAction <Symbol> What to do when an object has expired. Currently hard-coded as one of #remove, #notify, #refresh, #notifyAndRemove.

!

CachePolicy class
	instanceVariableNames: ''!
Object subclass: #Customer
	instanceVariableNames: 'id name transactions accounts eventsReceived seenPostFetch seenPreWrite seenPostWrite seenExpiry '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
Customer class
	instanceVariableNames: ''!
Object subclass: #DatabaseAccessor
	instanceVariableNames: 'connection currentLogin platform logging '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpDatabase'!
DatabaseAccessor class
	instanceVariableNames: ''!
Object subclass: #DatabaseField
	instanceVariableNames: 'table name isPrimaryKey sequencePolicy type fieldSize position '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
DatabaseField class
	instanceVariableNames: ''!
TestResource subclass: #DatabaseLoginResource
	instanceVariableNames: 'accessor login '
	classVariableNames: 'DefaultLogin '
	poolDictionaries: ''
	category: 'GlorpTest'!
DatabaseLoginResource class
	instanceVariableNames: ''!
Object subclass: #DatabasePlatform
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
DatabasePlatform subclass: #AdabasLikePlatform
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
AdabasLikePlatform subclass: #AdabasDPlatform
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
DatabasePlatform class
	instanceVariableNames: ''!
AdabasLikePlatform class
	instanceVariableNames: ''!
AdabasDPlatform class
	instanceVariableNames: ''!
Object subclass: #DatabaseRow
	instanceVariableNames: 'table contents shouldBeWritten owner '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!DatabaseRow commentStamp: '<historical>' prior: 0!
This represents the data to be written out to a row. Database rows are normally stored in a rowmap, keyed according to their table and the object that did the primary writes to them. We expect that that's only one object, although embedded values are an exception to that.

Instance Variables:

	table	<DatabaseTable>	The table holding the data
	contents	<IdentityDictionary>	Holds the fields with their values, indirectly through FieldValueWrapper instances.
	shouldBeWritten	<Boolean>	Normally true, but can be set false to suppress writing of a particular row. Used with embedded value mappings, where we create their row, unify it with the parent row, and suppress writing of the original row.
	owner	<Object>	The primary object that wrote into this row, would also be the key into the rowmap.!

DatabaseRow class
	instanceVariableNames: ''!
Object subclass: #DatabaseTable
	instanceVariableNames: 'name creator fields primaryKeyFields foreignKeyConstraints parent '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
DatabaseTable class
	instanceVariableNames: ''!
Object subclass: #Descriptor
	instanceVariableNames: 'describedClass tables multipleTableCriteria mappings system mappedFields cachePolicy '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
Descriptor class
	instanceVariableNames: ''!
Object subclass: #DescriptorSystem
	instanceVariableNames: 'descriptors tables session cachePolicy '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
DescriptorSystem class
	instanceVariableNames: ''!
Object subclass: #Dialect
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Extensions'!
Dialect class
	instanceVariableNames: ''!
DatabaseAccessor subclass: #DolphinDatabaseAccessor
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpDatabase'!
DolphinDatabaseAccessor class
	instanceVariableNames: ''!
Object subclass: #ElementBuilder
	instanceVariableNames: 'instance requiresPopulating instanceClass key expression query fieldTranslations '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
ElementBuilder class
	instanceVariableNames: ''!
Object subclass: #EmailAddress
	instanceVariableNames: 'id user host '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
EmailAddress class
	instanceVariableNames: ''!
Object subclass: #Encyclopedia
	instanceVariableNames: 'id entries '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
Encyclopedia class
	instanceVariableNames: ''!
Object subclass: #EncyclopediaEntry
	instanceVariableNames: 'id name text '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
EncyclopediaEntry class
	instanceVariableNames: ''!
Object subclass: #FakeElementBuilder
	instanceVariableNames: 'value '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
FakeElementBuilder class
	instanceVariableNames: ''!
Object subclass: #FieldUnifier
	instanceVariableNames: 'fields fieldsWithRows objects rows rowMap '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
FieldUnifier class
	instanceVariableNames: ''!
Object subclass: #FieldValueWrapper
	instanceVariableNames: 'contents hasValue containedBy '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
FieldValueWrapper class
	instanceVariableNames: ''!
Object subclass: #Flight
	instanceVariableNames: 'id passengersById passengersByName mealsByType '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
Flight class
	instanceVariableNames: ''!
Object subclass: #ForeignKeyConstraint
	instanceVariableNames: 'sourceField targetField '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
ForeignKeyConstraint class
	instanceVariableNames: ''!
DescriptorSystem subclass: #GlorpDemoDescriptorSystem
	instanceVariableNames: ''
	classVariableNames: 'Default '
	poolDictionaries: ''
	category: 'GlorpTest'!
GlorpDemoDescriptorSystem class
	instanceVariableNames: ''!
TestResource subclass: #GlorpDemoTablePopulatorResource
	instanceVariableNames: 'login '
	classVariableNames: 'NeedsSetup '
	poolDictionaries: ''
	category: 'GlorpTest'!
GlorpDemoTablePopulatorResource class
	instanceVariableNames: ''!
DescriptorSystem subclass: #GlorpEncyclopediaDescriptorSystem
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
GlorpEncyclopediaDescriptorSystem class
	instanceVariableNames: ''!
Object subclass: #GlorpExampleSystem
	instanceVariableNames: 'objects '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
GlorpExampleSystem subclass: #GlorpBankExampleSystem
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
GlorpExampleSystem class
	instanceVariableNames: ''!
GlorpBankExampleSystem class
	instanceVariableNames: ''!
Object subclass: #GlorpExpression
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
GlorpExpression subclass: #ConstantExpression
	instanceVariableNames: 'value '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
GlorpExpression subclass: #FieldExpression
	instanceVariableNames: 'field base '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
GlorpExpression class
	instanceVariableNames: ''!
ConstantExpression class
	instanceVariableNames: ''!
FieldExpression class
	instanceVariableNames: ''!
Object subclass: #GlorpHelper
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
GlorpHelper class
	instanceVariableNames: ''!
Object subclass: #GlorpMoney
	instanceVariableNames: 'currency amount '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
GlorpMoney class
	instanceVariableNames: ''!
Object subclass: #Login
	instanceVariableNames: 'database username password connectString '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
Login class
	instanceVariableNames: ''!
Object subclass: #Mapping
	instanceVariableNames: 'descriptor attributeName attributeAccessor '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
Mapping subclass: #ConditionalMapping
	instanceVariableNames: 'conditionalField conditionalMethod cases otherwiseCase '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
Mapping subclass: #ConstantMapping
	instanceVariableNames: 'constantValue valueIsSession '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!ConstantMapping commentStamp: '<historical>' prior: 0!
Sometimes you just want a constant value to be set, either in the row, the object or both. And sometimes you just want a non-mapping (e.g. with a ConditionalMapping where one
of the conditions means "this isn't mapped"). This mapping represents these situations.
It also handles the special case where it's useful to have access to the session inside a
domain object, by allowing you to map it to an instance variable.

So far only the case of mapping to an inst var is implemented.

Instance Variables:
!

Mapping subclass: #DictionaryMapping
	instanceVariableNames: 'keyMapping valueMapping '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!DictionaryMapping commentStamp: '<historical>' prior: 0!
This allows us to map a dictionary into tables. This breaks down into many cases.
String->Object
Object->Object
with representation either like a 1-many or like a many-many with information in the link table. The general idea is that we represent this as a compound mapping that can describe how the key maps and how the values maps. 

Instance Variables:

	keyMapping	<ClassOfVariable>	description of variable's function
	valueMapping	<ClassOfVariable>	description of variable's function!

Mapping subclass: #DirectMapping
	instanceVariableNames: 'field '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
Mapping class
	instanceVariableNames: ''!
ConditionalMapping class
	instanceVariableNames: ''!
ConstantMapping class
	instanceVariableNames: ''!
DictionaryMapping class
	instanceVariableNames: ''!
DirectMapping class
	instanceVariableNames: ''!
MessageArchiver class
	instanceVariableNames: ''!
GlorpExpression subclass: #ObjectExpression
	instanceVariableNames: 'mappingExpressions requiresDistinct tableAliases fieldAliases '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
ObjectExpression subclass: #BaseExpression
	instanceVariableNames: 'descriptor '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
ObjectExpression subclass: #MappingExpression
	instanceVariableNames: 'name base '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
ObjectExpression class
	instanceVariableNames: ''!
BaseExpression class
	instanceVariableNames: ''!
MappingExpression class
	instanceVariableNames: ''!
Object subclass: #ObjectTransaction
	instanceVariableNames: 'undoMap '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!ObjectTransaction commentStamp: '<historical>' prior: 0!
An ObjectTransaction knows how to remember the state of objects and revert them back to that state later on. It does this by making a *shallow* copy of the registered objects and everything connected to them, and then putting that into an identity dictionary keyed by the originals.

If you have to undo, you push the state from the shallow copies back into the originals.

Yes, that works, and it's all you have to do. It even handles collections become:ing different sizes.

Instance Variables:
	undoMap	<IdentityDictionary>	 The dictionary of originals->copies.

!

ObjectTransaction class
	instanceVariableNames: ''!
DatabasePlatform subclass: #OraclePlatform
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
OraclePlatform class
	instanceVariableNames: ''!
GlorpExpression subclass: #ParameterExpression
	instanceVariableNames: 'field base '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
ParameterExpression class
	instanceVariableNames: ''!
Object subclass: #Passenger
	instanceVariableNames: 'id name frequentFlyerMiles airline '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
Passenger class
	instanceVariableNames: ''!
Object subclass: #Person
	instanceVariableNames: 'id name address emailAddresses '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
Person class
	instanceVariableNames: ''!
DatabasePlatform subclass: #PostgreSQLPlatform
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
PostgreSQLPlatform class
	instanceVariableNames: ''!
Object subclass: #PrimaryKeyExpression
	instanceVariableNames: 'sources targets base '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
PrimaryKeyExpression class
	instanceVariableNames: ''!
ProtoObject subclass: #Proxy
	instanceVariableNames: 'session query parameters value isInstantiated '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
Proxy class
	instanceVariableNames: ''!
Object subclass: #Query
	instanceVariableNames: 'session criteria prepared '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
Query subclass: #AbstractReadQuery
	instanceVariableNames: 'resultClass readsOneObject returnProxies shouldRefresh '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
Query subclass: #DeleteQuery
	instanceVariableNames: 'objectToDelete '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
Query class
	instanceVariableNames: ''!
AbstractReadQuery class
	instanceVariableNames: ''!
DeleteQuery class
	instanceVariableNames: ''!
AbstractReadQuery subclass: #QueryStub
	instanceVariableNames: 'result '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
QueryStub class
	instanceVariableNames: ''!
AbstractReadQuery subclass: #ReadQuery
	instanceVariableNames: 'absentBlock '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!ReadQuery commentStamp: '<historical>' prior: 0!
This class has not yet been commented.  The comment should state the purpose of the class, what messages are subclassResponsibility, and the type and purpose of each instance and class variable.  The comment should also explain any unobvious aspects of the implementation.

Instance Variables:

criteria	<ClassOfVariable>	description of variable's function
resultDescription	<ClassOfVariable>	CollectionOfClassOrProjectionOfClass'!

ReadQuery class
	instanceVariableNames: ''!
GlorpExpression subclass: #RelationExpression
	instanceVariableNames: 'relation leftChild rightChild '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
RelationExpression subclass: #CollectionExpression
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!

!CollectionExpression commentStamp: '<historical>' prior: 0!
This represents expressions on collection objects taking a block, which at the moment means just anySatisfy:

We treat this as a relation, but with the special properties that when we convert the right hand side into an expression we assume it's a block and give it a base which is the left-hand side. Also, we don't print this relation when printing SQL, we just print the right hand side.!

RelationExpression class
	instanceVariableNames: ''!
CollectionExpression class
	instanceVariableNames: ''!
Mapping subclass: #RelationshipMapping
	instanceVariableNames: 'referenceClass mappingCriteria shouldProxy '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
RelationshipMapping subclass: #ManyToManyMapping
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
RelationshipMapping subclass: #OneToManyMapping
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
RelationshipMapping subclass: #OneToOneMapping
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
OneToOneMapping subclass: #EmbeddedValueOneToOneMapping
	instanceVariableNames: 'fieldTranslation '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!EmbeddedValueOneToOneMapping commentStamp: '<historical>' prior: 0!
This represents a one-to-one mapping in which the referenced object is stored as part of the same table as the containing object.
'!

RelationshipMapping class
	instanceVariableNames: ''!
ManyToManyMapping class
	instanceVariableNames: ''!
OneToManyMapping class
	instanceVariableNames: ''!
OneToOneMapping class
	instanceVariableNames: ''!
EmbeddedValueOneToOneMapping class
	instanceVariableNames: ''!
Object subclass: #RowMap
	instanceVariableNames: 'rowDictionary '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
RowMap class
	instanceVariableNames: ''!
Object subclass: #RowMapKey
	instanceVariableNames: 'key1 key2 '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!RowMapKey commentStamp: '<historical>' prior: 0!
This class serves as a key for a dictionary containing two sub-keys, where we want to be able to look up based on the identity of both sub-keys paired together. This is used primarily for many-to-many mappings indexing into rowmaps, where we want to key the row by the identity of the object that determines it, but there are two of them.

Instance Variables:

key1	<Object>	One sub-key.
key2	<Object>	The other sub-key.'!

RowMapKey class
	instanceVariableNames: ''!
AdabasLikePlatform subclass: #SAPDBPlatform
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
SAPDBPlatform class
	instanceVariableNames: ''!
DatabasePlatform subclass: #SQLServerPlatform
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
SQLServerPlatform class
	instanceVariableNames: ''!
Object subclass: #SequencePolicy
	instanceVariableNames: 'field '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
SequencePolicy subclass: #InMemorySequencePolicy
	instanceVariableNames: 'count '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
SequencePolicy subclass: #NullSequencePolicy
	instanceVariableNames: ''
	classVariableNames: 'Singleton '
	poolDictionaries: ''
	category: 'Glorp'!
SequencePolicy class
	instanceVariableNames: ''!
InMemorySequencePolicy class
	instanceVariableNames: ''!
NullSequencePolicy class
	instanceVariableNames: ''!
SequencePolicy subclass: #SequenceTablePolicy
	instanceVariableNames: 'sequenceTableName '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
SequenceTablePolicy class
	instanceVariableNames: ''!
Object subclass: #ServiceCharge
	instanceVariableNames: 'description amount '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest-Domain Models'!
ServiceCharge class
	instanceVariableNames: ''!
Object subclass: #Session
	instanceVariableNames: 'lastAccess data cache currentUnitOfWork accessor applicationData system '
	classVariableNames: 'Sessions '
	poolDictionaries: ''
	category: 'Glorp'!

!Session commentStamp: '<historical>' prior: 0!
This class has not yet been commented.  The comment should state the purpose of the class, what messages are subclassResponsibility, and the type and purpose of each instance and class variable.  The comment should also explain any unobvious aspects of the implementation.

Instance Variables:

	system	<ClassOfVariable>	description of variable's function
	currentUnitOfWork	<ClassOfVariable>	description of variable's function
	cache	<ClassOfVariable>	description of variable's function
	accessor	<ClassOfVariable>	description of variable's function
	application	<ClassOfVariable>	application-specific data!

Session class
	instanceVariableNames: ''!
TestResource subclass: #SessionResource
	instanceVariableNames: 'session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
SessionResource class
	instanceVariableNames: ''!
AbstractReadQuery subclass: #SimpleQuery
	instanceVariableNames: 'fields distinctFields traceNodes builders '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!SimpleQuery commentStamp: '<historical>' prior: 0!
This is a query that is directly executable. A single query might be more than we can do in a single database read, so we might have to break it down into simple queries. But at the moment we just break anything down into an equivalent single query.

Instance Variables:
	builders	<OrderedCollection of: ElementBuilder)>	The builders that will assemble the object from the row that this query returns.
	fields	<OrderedCollection of: DatabaseField>	The fields being selected.
	traceNodes	<Collection of: GlorpExpression>	 These describe the graph of the objects to be read, so we can specify customer, customer address and customer account all in one read.


!

SimpleQuery class
	instanceVariableNames: ''!
DatabaseAccessor subclass: #SqueakDatabaseAccessor
	instanceVariableNames: 'driverSession port isInTransaction '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpDatabase'!
ObjectExpression subclass: #TableExpression
	instanceVariableNames: 'table base '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp-Expressions'!
TableExpression class
	instanceVariableNames: ''!
Object subclass: #TableSorter
	instanceVariableNames: 'orderedTables tables visitedTables '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!
TableSorter class
	instanceVariableNames: ''!
TestCase subclass: #BasicMappingTest
	instanceVariableNames: 'mapping person '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
BasicMappingTest class
	instanceVariableNames: ''!
TestCase subclass: #CacheTest
	instanceVariableNames: 'cache '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
CacheTest class
	instanceVariableNames: ''!
TestCase subclass: #CommitOrderTest
	instanceVariableNames: 'system t1 t2 t3 t1id t2id t3id '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
CommitOrderTest class
	instanceVariableNames: ''!
TestCase subclass: #ConstantMappingTest
	instanceVariableNames: 'mappingToClass mappingToRow mappingToSession slot '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ConstantMappingTest class
	instanceVariableNames: ''!
TestCase subclass: #DatabaseBasicTest
	instanceVariableNames: 'system '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
DatabaseBasicTest class
	instanceVariableNames: ''!
TestCase subclass: #DatabaseFieldTest
	instanceVariableNames: 'field negatedField '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
DatabaseFieldTest class
	instanceVariableNames: ''!
TestCase subclass: #DatabaseLoginTest
	instanceVariableNames: 'login accessor '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
DatabaseLoginTest class
	instanceVariableNames: ''!
TestCase subclass: #DatabaseSessionTest
	instanceVariableNames: 'session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
DatabaseSessionTest class
	instanceVariableNames: ''!
TestCase subclass: #DeleteQueryTest
	instanceVariableNames: 'session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
DeleteQueryTest class
	instanceVariableNames: ''!
TestCase subclass: #DescriptorTest
	instanceVariableNames: 'system '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
DescriptorTest class
	instanceVariableNames: ''!
TestCase subclass: #DictionaryMappingTest
	instanceVariableNames: 'system '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
DictionaryMappingTest class
	instanceVariableNames: ''!
TestCase subclass: #DirectMappingTest
	instanceVariableNames: 'system mapping '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
DirectMappingTest class
	instanceVariableNames: ''!
TestCase subclass: #ExpressionBasicPropertiesTest
	instanceVariableNames: 'base '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ExpressionBasicPropertiesTest class
	instanceVariableNames: ''!
TestCase subclass: #ExpressionIterationTest
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ExpressionIterationTest class
	instanceVariableNames: ''!
TestCase subclass: #ExpressionJoiningTest
	instanceVariableNames: 'source target base system '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ExpressionJoiningTest class
	instanceVariableNames: ''!
TestCase subclass: #ExpressionTableAliasingTest
	instanceVariableNames: 'exp system '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ExpressionTableAliasingTest class
	instanceVariableNames: ''!
TestCase subclass: #ExpressionTest
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ExpressionTest class
	instanceVariableNames: ''!
TestCase subclass: #InsertUpdateTest
	instanceVariableNames: 'session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
InsertUpdateTest class
	instanceVariableNames: ''!
TestCase subclass: #MappingTest
	instanceVariableNames: 'system rowMap '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
MappingTest class
	instanceVariableNames: ''!
TestCase subclass: #MessageCollectorTest
	instanceVariableNames: 'collector '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
MessageCollectorTest class
	instanceVariableNames: ''!
TestCase subclass: #ObjectTransactionTest
	instanceVariableNames: 'transaction objects '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ObjectTransactionTest class
	instanceVariableNames: ''!
TestCase subclass: #PrimaryKeyExpressionTest
	instanceVariableNames: 'expression system compoundExpression '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
PrimaryKeyExpressionTest class
	instanceVariableNames: ''!
TestCase subclass: #ProxyTest
	instanceVariableNames: 'session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ProxyTest class
	instanceVariableNames: ''!
TestCase subclass: #QueryTableAliasingTest
	instanceVariableNames: 'query system expression elementBuilder '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
QueryTableAliasingTest class
	instanceVariableNames: ''!
TestCase subclass: #ReadQueryTest
	instanceVariableNames: 'session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ReadQueryTest class
	instanceVariableNames: ''!
TestCase subclass: #ReadingTest
	instanceVariableNames: 'system session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
ReadingTest class
	instanceVariableNames: ''!
TestCase subclass: #RowMapUnificationTest
	instanceVariableNames: 't1 t2 t3 f1 f2 f3 o1 o2 o3 rowMap '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
RowMapUnificationTest class
	instanceVariableNames: ''!
TestCase subclass: #SQLPrintingTest
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
SQLPrintingTest class
	instanceVariableNames: ''!
TestCase subclass: #SessionTest
	instanceVariableNames: 'session system '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
SessionTest class
	instanceVariableNames: ''!
TestCase subclass: #SimpleQueryTest
	instanceVariableNames: 'session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
SimpleQueryTest class
	instanceVariableNames: ''!
TestCase subclass: #TableTest
	instanceVariableNames: 'system descriptors '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
TableTest class
	instanceVariableNames: ''!
CachePolicy subclass: #TimedExpiryCachePolicy
	instanceVariableNames: 'timeout '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!TimedExpiryCachePolicy commentStamp: '<historical>' prior: 0!
This implements a cache that notes that an object is stale after some amount of time since it has been read.

Instance Variables:
	timeout	<Integer>	The time in seconds until we note an object as needing refreshing.

!

TimedExpiryCachePolicy class
	instanceVariableNames: ''!
CacheTest subclass: #TimedExpiryCacheTest
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
TimedExpiryCacheTest class
	instanceVariableNames: ''!
Object subclass: #Tracing
	instanceVariableNames: 'base allTracings '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!Tracing commentStamp: '<historical>' prior: 0!
A tracing is a collection of expressions representing the graph of other objects which
are to be read at the same time as the root object.

Instance Variables:

	base	<Expression>	The base expression representing the root object. Same as the parameter to the query block
	allTracings	<Collection of: Expression>	The expressions representing each of the associated objects. e.g. base accounts, base amount serviceCharge .!

Tracing class
	instanceVariableNames: ''!
TestCase subclass: #TracingTest
	instanceVariableNames: 'tracing '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
TracingTest class
	instanceVariableNames: ''!
Object subclass: #TransformedField
	instanceVariableNames: 'parent transformation stringTransformation '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!TransformedField commentStamp: '<historical>' prior: 0!
This represents a field with a transformation applied to it. This can be used to handle
cases where a foreign key relationship is not based on equality. Specifically, the motivating case is Store's use of (pk * -1) = fk for tw_blob chaining.

Instance Variables:

	parent	<DatabaseField>	description of variable's function
	transformation	<Block>	description of variable's function!

TransformedField class
	instanceVariableNames: ''!
Object subclass: #UnitOfWork
	instanceVariableNames: 'session rowMap commitPlan transaction '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Glorp'!

!UnitOfWork commentStamp: '<historical>' prior: 0!
A UnitOfWork keeps track of objects which might potentially be modified and lets you roll them back or commit the changes into the database.

Instance Variables:
	newObjects	<IdentitySet of: Object>	The objects registered with this unit of work. newObjects is probably a bad name for this.
	session	<Session>	The session in which this is all taking place.
	transaction	<ObjectTransaction>	Keeps track of the original object state so that we can revert it.
	rowMap	<RowMap>	A holder for the rows when we are writing out changes.
	commitPlan	<(OrderedCollection of: DatabaseRow)>	The list of rows to be written, in order. Constructed by topological sorting the contents of the row map.

!

UnitOfWork class
	instanceVariableNames: ''!
TestCase subclass: #UnitOfWorkTest
	instanceVariableNames: 'session '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpTest'!
UnitOfWorkTest class
	instanceVariableNames: ''!
DatabaseAccessor subclass: #VA55DatabaseAccessor
	instanceVariableNames: 'isInTransaction '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpDatabase'!
VA55DatabaseAccessor class
	instanceVariableNames: 'databaseErrorSignal '!
DatabaseAccessor subclass: #VWDatabaseAccessor
	instanceVariableNames: 'driverSession '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GlorpDatabase'!
VWDatabaseAccessor class
	instanceVariableNames: ''!

!MessageArchiver methodsFor: 'expression creation'!
asGlorpExpression
	^self asGlorpExpressionOn: BaseExpression new! !

!MessageArchiver methodsFor: 'expression creation'!
asGlorpExpressionOn: aBaseExpression 
	myReceiver == nil ifTrue: [^aBaseExpression].
	^(myReceiver asGlorpExpressionOn: aBaseExpression) get: myMessage selector
		withArguments: myMessage arguments! !

!MessageArchiver methodsFor: 'doesNotUnderstand'!
= anObject
	"Needed because VA's abtObservableWrapper implements =. Should be portable."
	^MessageArchiver 
		receiver: self
		message: (Message selector: #= arguments: (Array with: anObject)).! !

!MessageArchiver methodsFor: 'doesNotUnderstand'!
basicDoesNotUnderstand: aMessage 
	"Invoke this to avoid infinite recursion in the case of internal errors. We want a dialect-independent way of getting a walkback window, so we'll invoke it against a different object"


	(Array with: self) doesNotUnderstand: aMessage.! !

!MessageArchiver methodsFor: 'doesNotUnderstand'!
doesNotUnderstand: aMessage 
	| sel |
	sel := aMessage selector.
	sel == #doesNotUnderstand: ifTrue: [self basicDoesNotUnderstand: aMessage].
	(sel size >= 8 and: [(sel copyFrom: 1 to: 8) = 'perform:']) 
		ifTrue: 
			[^self get: aMessage arguments first
				withArguments: (aMessage arguments copyFrom: 2 to: aMessage arguments size)].
	^MessageArchiver receiver: self message: aMessage! !

!MessageArchiver methodsFor: 'debugging'!
basicPrintString

	^self printString.! !

!MessageArchiver methodsFor: 'debugging'!
class
	^MessageArchiver! !

!MessageArchiver methodsFor: 'debugging'!
halt
	"Support this so that we can debug inside query blocks. For portability, send it to a different object so that we don't have to care how halt is implemented"

	(Array with: self) halt.! !

!MessageArchiver methodsFor: 'debugging'!
inspect

	"Not exactly the intended semantics, but should be portable"
	(Array with: self) inspect.! !

!MessageArchiver methodsFor: 'debugging'!
inspectorSize
  ^2! !

!MessageArchiver methodsFor: 'debugging'!
printOn: aStream

	aStream nextPutAll: self printString.! !

!MessageArchiver methodsFor: 'debugging'!
printString

	"Hard-code this for maximum dialect portability"
	^'a MessageArchiver'.! !

!MessageArchiver methodsFor: 'expression protocol'!
between: anObject and: anotherObject

	^(self > anObject) & (self < anotherObject).! !

!MessageArchiver methodsFor: 'expression protocol'!
get: aSymbol 
	^MessageArchiver receiver: self
		message: (Message selector: aSymbol arguments: #())! !

!MessageArchiver methodsFor: 'expression protocol'!
get: aSymbol withArguments: anArray 
	^MessageArchiver receiver: self
		message: (Message selector: aSymbol arguments: anArray)! !

!MessageArchiver methodsFor: 'initialize'!
receiver: aMessageCollector message: aMessage

	myReceiver := aMessageCollector.
	myMessage := aMessage.! !

!MessageArchiver methodsFor: 'private/accessing'!
privateGlorpMessage

	^myMessage! !

!MessageArchiver methodsFor: 'private/accessing'!
privateGlorpReceiver

	^myReceiver! !


!Object methodsFor: 'glorp'!
asGlorpExpression

	^ConstantExpression for: self.! !

!Object methodsFor: 'glorp'!
asGlorpExpressionOn: anExpression

	^self asGlorpExpression.! !

!Object methodsFor: 'glorp'!
glorpIsCollection

	^false.! !

!Object methodsFor: 'glorp'!
glorpPostFetch: aSession! !

!Object methodsFor: 'glorp'!
glorpPostWrite: aSession! !

!Object methodsFor: 'glorp'!
glorpPreWrite: aSession! !

!Object methodsFor: 'glorp'!
glorpPrintSQLOn: aStream

	self printOn: aStream.! !

!Object methodsFor: 'glorp' stamp: 'nop 5/31/2002 15:14'!
isSymbol
	^false! !


!Address methodsFor: 'accessing'!
id
	"Private - Answer the value of the receiver's ''id'' instance variable."

	^id! !

!Address methodsFor: 'accessing'!
id: anObject
	"Private - Set the value of the receiver's ''id'' instance variable to the argument, anObject."

	id := anObject! !

!Address methodsFor: 'accessing'!
number
	"Private - Answer the value of the receiver's ''number'' instance variable."

	^number! !

!Address methodsFor: 'accessing'!
number: anObject
	"Private - Set the value of the receiver's ''number'' instance variable to the argument, anObject."

	number := anObject! !

!Address methodsFor: 'accessing'!
street
	"Private - Answer the value of the receiver's ''street'' instance variable."

	^street! !

!Address methodsFor: 'accessing'!
street: anObject
	"Private - Set the value of the receiver's ''street'' instance variable to the argument, anObject."

	street := anObject! !


!Address class methodsFor: 'examples'!
example1

	^self new
		id: 1;
		street: 'West 47th Ave';
		number: '2042'.! !


!AttributeAccessor methodsFor: 'accessing'!
attributeName
	"Private - Answer the value of the receiver's ''attributeName'' instance variable."

	^attributeName! !

!AttributeAccessor methodsFor: 'accessing'!
attributeName: anObject
	"Private - Set the value of the receiver's ''attributeName'' instance variable to the argument, anObject."

	attributeName := anObject! !

!AttributeAccessor methodsFor: 'accessing'!
instVarIndexIn: anObject

	attributeIndex isNil ifTrue: [
		attributeIndex := anObject class allInstVarNames indexOf: attributeName asString].
	^attributeIndex.! !

!AttributeAccessor methodsFor: 'get/set'!
getValueFrom: anObject

	^anObject instVarAt: (self instVarIndexIn: anObject).! !

!AttributeAccessor methodsFor: 'get/set'!
setValueIn: anObject to: aValue

	^anObject instVarAt: (self instVarIndexIn: anObject) put: aValue.! !


!AttributeAccessor class methodsFor: 'instance creation'!
newForAttributeNamed: aSymbol

	^self new attributeName: aSymbol! !


!BankAccount methodsFor: 'accessing'!
accountHolders
	^accountHolders.! !

!BankAccount methodsFor: 'accessing'!
accountNumber
	^accountNumber! !

!BankAccount methodsFor: 'accessing'!
accountNumber: anAccountNumber 
	accountNumber := anAccountNumber! !

!BankAccount methodsFor: 'accessing'!
basicAddHolder: aCustomer

	accountHolders add: aCustomer.! !

!BankAccount methodsFor: 'accessing'!
id
	^id! !

!BankAccount methodsFor: 'accessing'!
id: anObject
	id := anObject! !

!BankAccount methodsFor: 'initialize'!
initialize

	accountHolders := OrderedCollection new.! !

!BankAccount methodsFor: 'printing'!
printOn: aStream

	super printOn: aStream.
	aStream 
		nextPutAll: '(id=';
		print: id;
		nextPut: $).! !


!BankAccount class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!BankAccountNumber methodsFor: 'accessing'!
accountNumber
	^accountNumber! !

!BankAccountNumber methodsFor: 'accessing'!
accountNumber: anObject
	accountNumber := anObject! !

!BankAccountNumber methodsFor: 'accessing'!
bankCode
	^bankCode! !

!BankAccountNumber methodsFor: 'accessing'!
bankCode: anObject
	bankCode := anObject! !

!BankAccountNumber methodsFor: 'accessing'!
branchNumber
	^branchNumber! !

!BankAccountNumber methodsFor: 'accessing'!
branchNumber: anObject
	branchNumber := anObject! !


!BankTransaction methodsFor: 'accessing'!
amount
	^amount! !

!BankTransaction methodsFor: 'accessing'!
amount: aGlorpMoney 
	amount := aGlorpMoney! !

!BankTransaction methodsFor: 'accessing'!
id
	"Private - Answer the value of the receiver's ''id'' instance variable."

	^id! !

!BankTransaction methodsFor: 'accessing'!
id: anObject
	"Private - Set the value of the receiver's ''id'' instance variable to the argument, anObject."

	id := anObject! !

!BankTransaction methodsFor: 'accessing'!
owner
	"Private - Answer the value of the receiver's ''owner'' instance variable."

	^owner! !

!BankTransaction methodsFor: 'accessing'!
owner: aCustomer 
	owner := aCustomer! !

!BankTransaction methodsFor: 'accessing'!
serviceCharge
	^serviceCharge! !

!BankTransaction methodsFor: 'accessing'!
serviceCharge: aServiceCharge 
	serviceCharge := aServiceCharge! !

!BankTransaction methodsFor: 'initialize'!
initialize

	amount := GlorpMoney forAmount: 0.
	serviceCharge := ServiceCharge default.! !


!BankTransaction class methodsFor: 'examples'!
example1

	^self new! !

!BankTransaction class methodsFor: 'examples'!
example2

	^self new! !

!BankTransaction class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!BlockContext methodsFor: 'glorp' stamp: 'nop 5/29/2002 23:13'!
asGlorpExpression

	^self asGlorpExpressionOn: BaseExpression new.! !

!BlockContext methodsFor: 'glorp' stamp: 'nop 5/29/2002 23:13'!
asGlorpExpressionForDescriptor: aDescriptor

	| base |
	base := BaseExpression new.
	base descriptor: aDescriptor.
	^self asGlorpExpressionOn: base.! !

!BlockContext methodsFor: 'glorp' stamp: 'nop 5/29/2002 23:13'!
asGlorpExpressionOn: anExpression 
	^(self value: MessageArchiver new) asGlorpExpressionOn: anExpression! !


!Cache methodsFor: 'accessing'!
cachePolicy: aCachePolicy
	policy := aCachePolicy.! !

!Cache methodsFor: 'accessing'!
mainCache
	^mainCache! !

!Cache methodsFor: 'accessing'!
mainCache: aCacheManager
	mainCache := aCacheManager! !

!Cache methodsFor: 'accessing'!
session
	^mainCache session.! !

!Cache methodsFor: 'initialize'!
initialize

	items := Dictionary new: 20.! !

!Cache methodsFor: 'lookup'!
at: key ifAbsent: aBlock 
	| item value |
	item := self basicAt: key ifAbsent: [^aBlock value].
	value := policy contentsOf: item.
	(policy hasExpired: item) 
		ifTrue: 
			[policy takeExpiryActionForKey: key withValue: value in: self. 
			(items includesKey: key) ifFalse: [^aBlock value]].
	^value.! !

!Cache methodsFor: 'lookup'!
at: key ifAbsentPut: aBlock 
	| item |
	item := self at: key ifAbsent: [nil].
	^item isNil 
		ifTrue: [items at: key put: (policy cacheEntryFor: aBlock value)]
		ifFalse: [item]! !

!Cache methodsFor: 'lookup'!
hasExpired: key
	| item |
	item := self basicAt: key ifAbsent: [^false].
	^policy hasExpired: item.! !

!Cache methodsFor: 'lookup'!
removeKey: key ifAbsent: aBlock

	^items removeKey: key ifAbsent: aBlock.! !

!Cache methodsFor: 'private'!
basicAt: anObject ifAbsent: aBlock

	^items at: anObject ifAbsent: aBlock.! !


!Cache class methodsFor: 'instance creation'!
new

	^super new initialize.! !

!Cache class methodsFor: 'instance creation'!
newFor: aClass in: aCacheManager 
	| newCache descriptor |
	descriptor := aCacheManager system descriptorFor: aClass.
	newCache := Cache new.
	newCache mainCache: aCacheManager.
	descriptor isNil 
		ifTrue: [newCache cachePolicy: CachePolicy default]
		ifFalse: [newCache cachePolicy: descriptor cachePolicy].
	^newCache.! !


!CacheManager methodsFor: 'accessing'!
session

	^session.! !

!CacheManager methodsFor: 'accessing'!
session: aSession 
	session := aSession.! !

!CacheManager methodsFor: 'accessing'!
system

	^self session system.! !

!CacheManager methodsFor: 'adding'!
at: aKey insert: anObject

	| subCache |
	subCache := self cacheForClass: anObject class.
	subCache at: aKey ifAbsentPut: [anObject].! !

!CacheManager methodsFor: 'initialize/release'!
initialize

	subCaches := IdentityDictionary new: 100.! !

!CacheManager methodsFor: 'private/caching'!
cacheFor: anObject
	"Get the cache for a particular object. Since this could conceivably be passed a proxy, check for that. The cache for an uninstantiated proxy is kind of ambiguous, treat it as nil."
	^anObject class == Proxy
		ifTrue: [anObject isInstantiated ifTrue: [self cacheFor: anObject getValue] ifFalse: [nil]]
		ifFalse: [self cacheForClass: anObject class].! !

!CacheManager methodsFor: 'private/caching'!
cacheForClass: aClass

	^subCaches at: aClass ifAbsentPut: [Cache newFor: aClass in: self].! !

!CacheManager methodsFor: 'querying'!
hasExpired: anObject
	| key cache |
	key := (session descriptorFor: anObject) primaryKeyFor: anObject.
	cache := self cacheFor: anObject.
	cache isNil ifTrue: [^false].  "We have an uninstantiated proxy."
	^cache hasExpired: key.! !

!CacheManager methodsFor: 'querying'!
hasObjectExpiredOfClass: aClass withKey: key
	| cache |
	cache := self cacheForClass: aClass.
	^cache hasExpired: key.! !

!CacheManager methodsFor: 'querying'!
lookupClass: aClass key: aKey

	^self lookupClass: aClass key: aKey ifAbsent: [self error: 'cache miss'].! !

!CacheManager methodsFor: 'querying'!
lookupClass: aClass key: aKey ifAbsent: failBlock

	^(self cacheForClass: aClass) at: aKey ifAbsent: failBlock.! !

!CacheManager methodsFor: 'querying'!
removeClass: aClass key: aKey

	^self removeClass: aClass key: aKey ifAbsent: [self error: 'Object not in cache'].! !

!CacheManager methodsFor: 'querying'!
removeClass: aClass key: aKey ifAbsent: aBlock 

	^(self cacheForClass: aClass) removeKey: aKey ifAbsent: aBlock.! !


!CacheManager class methodsFor: 'instance creation'!
forSession: aSession 
	^self new session: aSession.! !

!CacheManager class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!CachePolicy methodsFor: 'wrap/unwrap'!
cacheEntryFor: anObject

	^anObject.! !

!CachePolicy methodsFor: 'wrap/unwrap'!
contentsOf: aCacheEntry

	^aCacheEntry.! !

!CachePolicy methodsFor: 'wrap/unwrap'!
hasExpired: aCacheEntry

	^false.! !

!CachePolicy methodsFor: 'initialize'!
initialize

	size := 100.
	expiryAction := #remove.! !

!CachePolicy methodsFor: 'accessing'!
expiryAction
	^expiryAction! !

!CachePolicy methodsFor: 'accessing'!
expiryAction: aSymbol
	"See class comment for possible values"
	expiryAction := aSymbol! !

!CachePolicy methodsFor: 'expiry'!
notifyOfExpiry: anObject in: aCache 
	anObject glorpNoticeOfExpiryIn: aCache session.! !

!CachePolicy methodsFor: 'expiry'!
takeExpiryActionForKey: key withValue: anObject in: aCache
	expiryAction == #refresh
		ifTrue: [aCache session refreshObject: anObject].
	(#(#notify #notifyAndRemove) includes: expiryAction) ifTrue: [
		self notifyOfExpiry: anObject in: aCache].
	(#(#remove #notifyAndRemove) includes: expiryAction) ifTrue: [
		aCache removeKey: key ifAbsent: []].! !


!CachePolicy class methodsFor: 'instance creation'!
default

	^self new.! !

!CachePolicy class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!Collection methodsFor: 'testing'!
glorpIsCollection

	^true.! !

!Collection methodsFor: 'glorp'!
glorpPrintSQLOn: aStream 
	aStream nextPut: $(.
	GlorpHelper 
		do: [:each | each glorpPrintSQLOn: aStream]
		for: self
		separatedBy: [aStream nextPutAll: ', '].
	aStream nextPut: $)! !

!Collection methodsFor: 'glorp'!
glorpRegisterCollectionInternalsIn: anObjectTransaction 
	"Explicitly register any internal structures (e.g. a VW identity dictionary's valueArray) with the transaction. Assume we can safely register everything inside the collection reflectively. The obvious exceptions would be dependents and sortblocks. This is a cheat, and for peculiar cases you'll need to override this in the subclass"

	| names |
	names := self class allInstVarNames.
	(1 to: names size) do: 
			[:index | 
			(#('dependents' 'sortBlock') includes: (names at: index)) 
				ifFalse: [anObjectTransaction register: (self instVarAt: index)]]! !


!Array methodsFor: 'accessing'!
atIndex: anInteger
	"For compatibility with Dolphin and VA data base rows."
	^self at: anInteger.! !


!Customer methodsFor: 'accessing'!
accounts

	^accounts.! !

!Customer methodsFor: 'accessing'!
addAccount: aBankAccount

	accounts add: aBankAccount.
	aBankAccount basicAddHolder: self.! !

!Customer methodsFor: 'accessing'!
addTransaction: aTransaction

	transactions add: aTransaction.
	aTransaction owner: self.! !

!Customer methodsFor: 'accessing'!
id
	"Private - Answer the value of the receiver's ''id'' instance variable."

	^id! !

!Customer methodsFor: 'accessing'!
id: anObject
	"Private - Set the value of the receiver's ''id'' instance variable to the argument, anObject."

	id := anObject! !

!Customer methodsFor: 'accessing'!
name
	"Private - Answer the value of the receiver's ''name'' instance variable."

	^name! !

!Customer methodsFor: 'accessing'!
name: anObject
	"Private - Set the value of the receiver's ''name'' instance variable to the argument, anObject."

	name := anObject! !

!Customer methodsFor: 'accessing'!
seenPostFetch
	^seenPostFetch! !

!Customer methodsFor: 'accessing'!
seenPostWrite
	^seenPostWrite! !

!Customer methodsFor: 'accessing'!
seenPreWrite
	^seenPreWrite! !

!Customer methodsFor: 'accessing'!
transactions
	"Private - Answer the value of the receiver's ''transactions'' instance variable."

	^transactions! !

!Customer methodsFor: 'accessing'!
transactions: anObject
	"Private - Set the value of the receiver's ''transactions'' instance variable to the argument, anObject."

	transactions := anObject! !

!Customer methodsFor: 'glorp/events'!
glorpNoticeOfExpiryIn: aSession
	seenExpiry := true.! !

!Customer methodsFor: 'glorp/events'!
glorpPostFetch: aSession
	seenPostFetch := true! !

!Customer methodsFor: 'glorp/events'!
glorpPostWrite: aSession
	seenPostWrite := true! !

!Customer methodsFor: 'glorp/events'!
glorpPreWrite: aSession
	seenPreWrite := true! !

!Customer methodsFor: 'initialize/release'!
initialize

	transactions := OrderedCollection new.
	accounts := OrderedCollection new.! !

!Customer methodsFor: 'As yet unclassified'!
seenExpiry
	^seenExpiry! !


!Customer class methodsFor: 'examples'!
example1

	^self new
		name: 'Fred Flintstone';
		addTransaction: BankTransaction example1;
		addTransaction: BankTransaction example2.! !

!Customer class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!DatabaseAccessor methodsFor: 'accessing'!
connection
	^connection! !

!DatabaseAccessor methodsFor: 'accessing'!
connectionClass
	^(self connectionClassForLogin: currentLogin)! !

!DatabaseAccessor methodsFor: 'accessing'!
currentLogin
	^currentLogin! !

!DatabaseAccessor methodsFor: 'accessing'!
currentLogin: aLogin 
	currentLogin := aLogin! !

!DatabaseAccessor methodsFor: 'accessing'!
platform
	^currentLogin database.! !

!DatabaseAccessor methodsFor: 'initializing'!
initialize! !

!DatabaseAccessor methodsFor: 'executing'!
createTable: aGLORBDatabaseTable ifError: aBlock 
	"This method should be used to create a database table from aTable"

	self 
		doCommand: [self executeSQLString: (self platform createTableStatementStringFor: aGLORBDatabaseTable)]
		ifError: aBlock! !

!DatabaseAccessor methodsFor: 'executing'!
doCommand: aBlock

	^self doCommand: aBlock ifError: [:ex | self halt].! !

!DatabaseAccessor methodsFor: 'executing'!
doCommand: aBlock ifError: errorBlock

	^aBlock on: self externalDatabaseErrorSignal do: errorBlock.! !

!DatabaseAccessor methodsFor: 'executing'!
dropConstraint: aConstraint 
	self doCommand: [self executeSQLString: aConstraint dropString]
		ifError: [:ex | Transcript show: ex messageText]! !

!DatabaseAccessor methodsFor: 'executing'!
dropTable: aTable ifAbsent: aBlock 
	self doCommand: [aTable dropFromAccessor: self]
		ifError: [:ex | Transcript show: ex messageText]! !

!DatabaseAccessor methodsFor: 'executing'!
dropTableNamed: aString 
	self executeSQLString: 'DROP TABLE ' , aString! !

!DatabaseAccessor methodsFor: 'executing'!
dropTableNamed: aString ifAbsent: aBlock 
	self doCommand: [self executeSQLString: 'DROP TABLE ' , aString]
		ifError: aBlock! !

!DatabaseAccessor methodsFor: 'executing'!
dropTables: anArray 
"PostgreSQL drops foreign key constraints implicitly."
	anArray do: [:each | each dropForeignKeyConstraintsFromAccessor: self].
	anArray do: [:each | self dropTable: each ifAbsent: [ :ex | Transcript show: ex messageText]]! !

!DatabaseAccessor methodsFor: 'executing'!
externalDatabaseErrorSignal

	self subclassResponsibility.! !

!DatabaseAccessor methodsFor: 'logging'!
log: aString

	Transcript cr; show: aString! !

!DatabaseAccessor methodsFor: 'logging'!
logError: anErrorObject

	self log: anErrorObject printString! !

!DatabaseAccessor methodsFor: 'login'!
login
	
	| warning |
	self loginIfError: [:ex | 
		warning := 'Unable to log in. Check login information in DatabaseLoginResource class methods'.
		Transcript show: warning; cr.
		self showDialog: warning.
		self halt].! !

!DatabaseAccessor methodsFor: 'login'!
loginIfError: aBlock

	self subclassResponsibility.! !

!DatabaseAccessor methodsFor: 'login'!
showDialog: aString

	self subclassResponsibility.! !


!DatabaseAccessor class methodsFor: 'instance creation' stamp: 'nop 5/29/2002 15:08'!
classForThisPlatform
	Dialect isVisualWorks
		ifTrue: [^ Smalltalk at: #VWDatabaseAccessor].
	Dialect isVisualAge
		ifTrue: [^ Smalltalk at: #VA55DatabaseAccessor].
	Dialect isDolphin
		ifTrue: [^ Smalltalk at: #DolphinDatabaseAccessor].
	Dialect isSqueak
		ifTrue: [^ Smalltalk at: #SqueakDatabaseAccessor].
	self error: 'unknown dialect'! !

!DatabaseAccessor class methodsFor: 'instance creation'!
forLogin: aLogin 
	^self classForThisPlatform new currentLogin: aLogin! !

!DatabaseAccessor class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!DatabaseField methodsFor: 'printing'!
asConstraintReferenceString

	^table name, ' (', self name, ')'.! !

!DatabaseField methodsFor: 'printing'!
printCreationStringFor: aDatabaseAccessor on: aStream

	aStream  nextPutAll: name.
	aStream nextPutAll: ' '.
	aStream nextPutAll: (self typeStringFor: aDatabaseAccessor).
	self isNullable ifFalse: [aStream nextPutAll: ' NOT NULL'].! !

!DatabaseField methodsFor: 'printing'!
printForConstraintNameOn: aStream maxLength: maxLength 
	| constraintName |
	constraintName := table name , '_' , name.
	constraintName size > maxLength 
		ifTrue: [constraintName := constraintName copyFrom: 1 to: maxLength].
	aStream nextPutAll: constraintName! !

!DatabaseField methodsFor: 'printing'!
printNameOn: aStream withParameters: anArray 
	aStream nextPutAll: self name! !

!DatabaseField methodsFor: 'printing'!
printOn: aStream
	
	super printOn: aStream.
	aStream 
		nextPutAll: '(';
		nextPutAll: (table isNil ifTrue: [''] ifFalse: [table name]);
		nextPutAll: '.';
		nextPutAll: name;
		nextPutAll: ')'.! !

!DatabaseField methodsFor: 'printing'!
printQualifiedSQLOn: aStream withParameters: aDictionary 
	aStream nextPutAll: self qualifiedName "self name"! !

!DatabaseField methodsFor: 'printing'!
printSQLOn: aStream withParameters: anArray 
	aStream nextPutAll: self qualifiedName "self name"! !

!DatabaseField methodsFor: 'printing'!
printUnqualifiedSQLOn: aStream withParameters: anArray 
	aStream nextPutAll: self name.! !

!DatabaseField methodsFor: 'printing'!
typeStringFor: aDatabaseAccessor

	^aDatabaseAccessor platform typeStringFor: self type ofSize: self fieldSize.! !

!DatabaseField methodsFor: 'configuring'!
beNumeric

	type := #number.
	fieldSize := 38.! !

!DatabaseField methodsFor: 'configuring'!
bePrimaryKey

	isPrimaryKey := true.
	self table isNil ifFalse: [self table addAsPrimaryKeyField: self].! !

!DatabaseField methodsFor: 'configuring'!
useSequenceTable

	sequencePolicy := SequenceTablePolicy default.! !

!DatabaseField methodsFor: 'configuring'!
useSequencingInMemory
	sequencePolicy := InMemorySequencePolicy default.! !

!DatabaseField methodsFor: 'database'!
convertToDatabaseForm: anObject

	anObject isSymbol ifTrue: [^anObject asString].
	^anObject.! !

!DatabaseField methodsFor: 'database'!
transform: anObject

	^anObject.! !

!DatabaseField methodsFor: 'accessing'!
fieldSize
	^fieldSize! !

!DatabaseField methodsFor: 'accessing'!
name
	"Private - Answer the value of the receiver's ''name'' instance variable."

	^name! !

!DatabaseField methodsFor: 'accessing'!
name: anObject
	"Private - Set the value of the receiver's ''name'' instance variable to the argument, anObject."

	name := anObject! !

!DatabaseField methodsFor: 'accessing'!
position
	^position! !

!DatabaseField methodsFor: 'accessing'!
position: anObject
	position := anObject! !

!DatabaseField methodsFor: 'accessing'!
table
	"Private - Answer the value of the receiver's ''table'' instance variable."

	^table! !

!DatabaseField methodsFor: 'accessing'!
table: anObject
	"Private - Set the value of the receiver's ''table'' instance variable to the argument, anObject."

	table := anObject! !

!DatabaseField methodsFor: 'accessing'!
type

	^type! !

!DatabaseField methodsFor: 'initializing'!
initialize

	isPrimaryKey := false.
	sequencePolicy := NullSequencePolicy default.
	type := #string.
	fieldSize := 255.! !

!DatabaseField methodsFor: 'hacks'!
inverted

	^TransformedField new parent: self; transformation: [:each | each * -1].! !

!DatabaseField methodsFor: 'hacks'!
rootField

	^self.! !

!DatabaseField methodsFor: 'testing'!
isGenerated

	^sequencePolicy ~= NullSequencePolicy default! !

!DatabaseField methodsFor: 'testing'!
isNullable
	
	^self isPrimaryKey not.! !

!DatabaseField methodsFor: 'testing'!
isPrimaryKey
	"Private - Answer the value of the receiver's ''isPrimaryKey'' instance variable."

	^isPrimaryKey! !

!DatabaseField methodsFor: 'querying'!
possibleTypes

	^#(string number).! !

!DatabaseField methodsFor: 'querying'!
qualifiedName

	^table isNil 
		ifTrue: [self name]
		ifFalse: [self table qualifiedName, '.', self name].! !

!DatabaseField methodsFor: 'querying'!
sequencePolicy

	^sequencePolicy.! !

!DatabaseField methodsFor: 'converting'!
asGlorpExpression

	^ParameterExpression forField: self basedOn: nil.! !

!DatabaseField methodsFor: 'converting'!
asGlorpExpressionOn: anExpression

	^ParameterExpression forField: self basedOn: anExpression.! !


!DatabaseField class methodsFor: 'instance creation'!
named: aString

	^self new name: aString.! !

!DatabaseField class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!DatabaseLoginResource methodsFor: 'accessing'!
accessor
	^accessor! !

!DatabaseLoginResource methodsFor: 'accessing'!
accessor: anObject
	accessor := anObject! !

!DatabaseLoginResource methodsFor: 'accessing'!
login
	^login! !

!DatabaseLoginResource methodsFor: 'accessing'!
login: anObject
	login := anObject! !

!DatabaseLoginResource methodsFor: 'initialize/release'!
setUp
	Transcript show: self class name asString, ' setUp'; cr.
	super setUp.
	login := self class defaultLogin.
	accessor := DatabaseAccessor forLogin: login.
	accessor login.! !

!DatabaseLoginResource methodsFor: 'initialize/release'!
tearDown

	Transcript show: self class name asString, ' tearDown'; cr.
	accessor notNil ifTrue: [accessor logout].! !


!DatabaseLoginResource class methodsFor: 'accessing'!
defaultDolphinODBCLogin
	"To set the default database login for Dolphin, execute the following statement."

	"DefaultLogin := self defaultDolphinODBCLogin."

	^(Login new)
		database: SQLServerPlatform new;
		username: 'glorptest';
		password: 'password';
		connectString: 'glorptest'! !

!DatabaseLoginResource class methodsFor: 'accessing'!
defaultLogin
	"Return the default Login."

	DefaultLogin isNil ifTrue: [^DefaultLogin := self defaultOracleLiteLogin].
	^DefaultLogin! !

!DatabaseLoginResource class methodsFor: 'accessing'!
defaultLogin: aLogin

	DefaultLogin := aLogin.! !

!DatabaseLoginResource class methodsFor: 'accessing'!
defaultMysqlLogin
	"To set the default database login to MySQL, execute the following statement."
	"DefaultLogin := self defaultMysqlLogin."

	^(Login new)
		database: #MySQL;
		username: 'System';
		password: 'password';
		connectString: 'glorp'.! !

!DatabaseLoginResource class methodsFor: 'accessing'!
defaultOracleLiteLogin
	"To set the default database login to Oracle, execute the following statement."
	"DefaultLogin := self defaultOracleLiteLogin."

	^(Login new)
		database: OraclePlatform new;
		username: 'System';
		password: 'password';
		connectString: 'odbc:polite'! !

!DatabaseLoginResource class methodsFor: 'accessing'!
defaultOracleLogin
	"To set the default database login to Oracle, execute the following statement."
	"DefaultLogin := self defaultOracleLogin."

	^(Login new)
		database: OraclePlatform new;
		username: 'System';
		password: 'manager';
		connectString: ''! !

!DatabaseLoginResource class methodsFor: 'accessing'!
defaultPersonalOracleLogin
	"To set the default database login to Oracle, execute the following statement."
	"DefaultLogin := self defaultPersonalOracleLogin."

	^(Login new)
		database: OraclePlatform new;
		username: 'system';
		password: 'manager';
		connectString: ''! !

!DatabaseLoginResource class methodsFor: 'accessing'!
defaultPostgreSQLLogin
	"To set the default database login to PostgreSQL, execute the following statement."
	"DefaultLogin := self defaultPostgreSQLLogin."

	^(Login new)
		database: PostgreSQLPlatform new;
		username: 'alan';
		password: '';
		connectString: 'www.cincomsmalltalk.com:5432_glorptest'.! !


!DatabasePlatform methodsFor: 'constants'!
batchWriteStatementTerminatorString
	"^<String> This statement return the string to be used to devide several statement during batch write ..."

	^';'! !

!DatabasePlatform methodsFor: 'constants'!
capitalWritingOfColumnName
	"^<Boolean> This method returns true, if the dbms wants to have column 
	names written in capital letters"

	^true! !

!DatabasePlatform methodsFor: 'constants'!
capitalWritingOfCreatorName
	"^<Boolean> This method returns true, if the dbms wants to have column 
	names written in capital letters"

	^true! !

!DatabasePlatform methodsFor: 'constants'!
capitalWritingOfDatabaseName
	"^<Boolean>"

	^true! !

!DatabasePlatform methodsFor: 'constants'!
capitalWritingOfSQLCommands
	"^<Boolean>"

	^true! !

!DatabasePlatform methodsFor: 'constants'!
capitalWritingOfTableName
	"^<Boolean>"

	^true! !

!DatabasePlatform methodsFor: 'constants'!
columnNameSeparatorString
	"^<String> This statement return the string to be used to devide several columns ..."

	^','! !

!DatabasePlatform methodsFor: 'constants'!
deleteViewWithTableSyntax

	^false! !

!DatabasePlatform methodsFor: 'constants'!
hasSubtransaction
	"^<Boolean> This method returns true, if the used dbms is able to execute multiple sql-statements
	transferred via a command line transmitted from client to server - otherwise I return false"

	^true! !

!DatabasePlatform methodsFor: 'constants'!
maxLengthOfColumnName
	"^<Integer> I return the max. length of a column name"

	^18! !

!DatabasePlatform methodsFor: 'constants'!
maxLengthOfDatabaseName
	"^<Integer>I return the max. length of a database name"

	^8! !

!DatabasePlatform methodsFor: 'constants'!
maxLengthOfTableName
	"^<Integer> I return the max. length of a table name"

	^18! !

!DatabasePlatform methodsFor: 'constants'!
maxSQLBufferLength
	"^<Integer> I return the maximum length of a sql command stream"

	^8192! !

!DatabasePlatform methodsFor: 'constants'!
postfixTableNameBeforeDeleting

	^false! !

!DatabasePlatform methodsFor: 'constants'!
prefixQualifierBeforeCreatingAndDeleting

	^true! !

!DatabasePlatform methodsFor: 'constants'!
prefixQualifierSeparatorString
	"^<String> This statement return the string to be used to separate the qualifier and the table/column name"

	^'.'! !

!DatabasePlatform methodsFor: 'constants'!
prefixTableNameBeforeDeleting

	^false! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForBeginTransaction
	"comment"

	^'BEGIN'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForCharAttributeType: length
	"^<String>"

	^'CHAR(',length asString,')'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForDateAttributeType
	"^<String>"

	^'DATE'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForDecimalAttributeType: length post: postLength
	"^<String>"

	^'DECIMAL(',length asString,',',postLength asString,')'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForDoubleAttributeType: length
	"^<String>"

	^'FLOAT'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForDoubleLongIntegerAttributeType: length
	"^<String>"

	^''! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForFloatAttributeType: length
	"^<String>"

	^'FLOAT'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForIntegerAttributeType: length
	"^<String>"

	^'SMALLINT'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForLongIntegerAttributeType: length
	"^<String>"

	^'INTEGER'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForNOTNULLAttributeConstraint
	"^<String>"

	^'NOT NULL'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForNOTNULLWithDefaultAttributeConstraint
	"^<String>"

	^'NOT NULL WITH DEFAULT'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForNULLAttributeConstraint
	"^<String>"

	^'NULL'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForTextAttributeType: length
	"^<String>"

	^'LONG'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForTimeAttributeType
	"^<String>"

	^'TIME'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForTimestampAttributeType
	"^<String>"

	^'TIMESTAMP'! !

!DatabasePlatform methodsFor: 'constants'!
sqlTextForVariableCharAttributeType: length
	"^<String>"

	^'VARCHAR(',length asString,')'! !

!DatabasePlatform methodsFor: 'constants'!
sqlWildcardForMultipleCharacters
	"^<String> This method returns the used wildcard string for multiple characters"
	
	^'%'! !

!DatabasePlatform methodsFor: 'constants'!
sqlWildcardForSingleCharacter
	"^<String> This method returns the used wildcard string for single characters"
	
	^'_'! !

!DatabasePlatform methodsFor: 'general services'!
predefinedKeywords
	"
		^<OrderdCollection of: String> This method returns a list of preserved keyword, which should
		not be used in database-, table or column names or any othe names in the platform system
	"

	^OrderedCollection new! !

!DatabasePlatform methodsFor: 'printing'!
typeStringFor: type ofSize: size

	type == #string ifTrue: [		^self sqlTextForVariableCharAttributeType: size ].
	type == #number ifTrue: [	^self sqlTextForLongIntegerAttributeType: size ].

   "
   type == #fixedString ifTrue:[  ^self sqlTextForCharAttributeType: size ].
   type == #date ifTrue:[  ^self sqlTextForDateAttributeType ].
   type == #time ifTrue:[  ^self sqlTextForTimeAttributeType ].
   type == #timestamp ifTrue:[ ^self sqlTextForTimestampAttributeType ].
   type == #decimal ifTrue:[  ^self sqlTextForDecimalAttributeType: size post: 0  ].
   type == #float ifTrue:[ ^self sqlTextForFloatAttributeType: size  ].
   type == #double ifTrue:[  ^self sqlTextForDoubleAttributeType: size  ].
   type == #8bitInt ifTrue:[^self sqlTextForLongIntegerAttributeType: size  ].
   type == #16bitint ifTrue:[ ^self sqlTextForLongIntegerAttributeType: 8  ].
   type == #32bitint ifTrue:[ ^self sqlTextForLongIntegerAttributeType: 8  ].
   type == #64bitint ifTrue:[ ^self sqlTextForLongIntegerAttributeType: 8  ].
  "
	



	self error: 'invalid field type'.! !

!DatabasePlatform methodsFor: 'testing'!
isODBCPlatform

	^false! !

!DatabasePlatform methodsFor: 'testing'!
isOraclePlatform

	^false! !

!DatabasePlatform methodsFor: 'testing'!
isPostgreSQLPlatform

	^false! !

!DatabasePlatform methodsFor: 'services tables'!
createTableStatementStringFor: aGLORPDatabaseTable 
	"^<String> This method returns a string which can be used to create a database table ..."

	| sqlStatementStream tmpString |
	tmpString := 'create table'.
	sqlStatementStream := WriteStream on: String new.
	sqlStatementStream
		nextPutAll: (self capitalWritingOfSQLCommands 
					ifTrue: [tmpString asUppercase]
					ifFalse: [tmpString]);
		space.
	self printDDLTableNameFor: aGLORPDatabaseTable on: sqlStatementStream.

	"Now print the columns specification for each field in the table ..."
	self printColumnsSpecificationFor: aGLORPDatabaseTable
		on: sqlStatementStream.
	self supportsConstraints 
		ifTrue: 
			[aGLORPDatabaseTable hasPrimaryKeyConstraints 
				ifTrue: 
					[sqlStatementStream nextPutAll: ', '.
					self printPrimaryKeyConstraintsOn: sqlStatementStream
						for: aGLORPDatabaseTable].
			aGLORPDatabaseTable hasForeignKeyConstraints 
				ifTrue: 
					[sqlStatementStream nextPutAll: ', '.
					self printForeignKeyConstraintsOn: sqlStatementStream
						for: aGLORPDatabaseTable]].

	sqlStatementStream
		nextPut: $).
	^sqlStatementStream contents! !

!DatabasePlatform methodsFor: 'services tables'!
dropTableStatementStringFor: aGLORPDatabaseTable 
	"^<String> This method returns a string which can be used to drop a database table ..."

	| sqlStatementStream tmpString |
	tmpString := 'drop table'.
	sqlStatementStream := WriteStream on: String new.
	sqlStatementStream
		nextPutAll: (self capitalWritingOfSQLCommands 
					ifTrue: [tmpString asUppercase]
					ifFalse: [tmpString]);
		space.
	self printDDLTableNameFor: aGLORPDatabaseTable on: sqlStatementStream.
	^sqlStatementStream contents! !

!DatabasePlatform methodsFor: 'services tables'!
printDDLTableNameFor: aGLORBDatabaseTable on: sqlStatementStream
	"This method just writes the name of a table to a stream"
	
	(aGLORBDatabaseTable creator asString isEmpty not 
		and: [ self prefixQualifierBeforeCreatingAndDeleting ]) ifTrue:[
			sqlStatementStream
				nextPutAll: (self capitalWritingOfCreatorName 
										ifTrue:[  aGLORBDatabaseTable creator asUppercase ] 
										ifFalse:[aGLORBDatabaseTable creator ]) ;
				nextPutAll: self prefixQualifierSeparatorString.
	].
	sqlStatementStream
		nextPutAll: (self capitalWritingOfTableName 
								ifTrue:[ aGLORBDatabaseTable name asUppercase ] 
								ifFalse:[ aGLORBDatabaseTable name ]).! !

!DatabasePlatform methodsFor: 'services tables'!
printForeignKeyConstraintsOn: sqlStatementStream for: aGLORBDatabaseTable
	"This method print the constraint specification on sqlStatementStream"
 
	| sepFlag |

	sepFlag := false.
	aGLORBDatabaseTable foreignKeyConstraints do: [ :eachKeyField |
		sepFlag ifTrue:[ sqlStatementStream nextPutAll: ',' ].
		sqlStatementStream nextPutAll: eachKeyField creationString.
		sepFlag := true
	].! !

!DatabasePlatform methodsFor: 'services tables'!
printPrimaryKeyConstraintsOn: sqlStatementStream for: aTable 
	"This method print the constraint specification on sqlStatementStream"

	| sepFlag |
	aTable primaryKeyFields isEmpty ifTrue: [^self].
	sqlStatementStream
		nextPutAll: 'CONSTRAINT ';
		nextPutAll: aTable primaryKeyConstraintName;
		nextPutAll: ' PRIMARY KEY  ('.
	sepFlag := false.
	aTable primaryKeyFields do: 
			[:eachPrimaryKeyField | 
			sepFlag ifTrue: [sqlStatementStream nextPutAll: ','].
			sqlStatementStream nextPutAll: eachPrimaryKeyField name.
			sepFlag := true].
	sqlStatementStream nextPut: $).
	sqlStatementStream
		nextPutAll: ',';
		cr;
		nextPutAll: 'CONSTRAINT ';
		nextPutAll: aTable primaryKeyUniqueConstraintName;
		nextPutAll: ' UNIQUE  ('.
	sepFlag := false.
	aTable primaryKeyFields do: 
			[:eachPrimaryKeyField | 
			sepFlag ifTrue: [sqlStatementStream nextPutAll: ','].
			sqlStatementStream nextPutAll: eachPrimaryKeyField name.
			sepFlag := true].
	sqlStatementStream nextPut: $)! !

!DatabasePlatform methodsFor: 'services tables'!
validateTableName: tableNameString
	" <Boolean> I return true, if the choosen tableNameString is valid for the platform"

 	^( tableNameString size <= self maxLengthOfTableName ) 
			and: [ (self predefinedKeywords includes: tableNameString asLowercase) not ]! !

!DatabasePlatform methodsFor: 'services columns'!
printColumnsSpecificationFor: aGLORBDatabaseTable on: sqlStatementStream

	aGLORBDatabaseTable fields isEmpty not ifTrue:[
		| sepFlag |

		sqlStatementStream
			space ;
			nextPut: $(.

	   sepFlag := false.
	   aGLORBDatabaseTable fields
	     do: [ :eachGLORBDatabaseField | 

			sepFlag ifTrue:[ sqlStatementStream nextPutAll: self columnNameSeparatorString ].
			
			sqlStatementStream  
				nextPutAll: (self capitalWritingOfColumnName 
										ifTrue:[ eachGLORBDatabaseField name asUppercase ] 
										ifFalse:[ eachGLORBDatabaseField name ]) ;
				space ;
				nextPutAll: (self typeStringFor: eachGLORBDatabaseField type ofSize: eachGLORBDatabaseField fieldSize ) ;
				space  ;
				nextPutAll: (eachGLORBDatabaseField isNullable 
										ifTrue:[ 	self sqlTextForNULLAttributeConstraint]
										ifFalse: [  self sqlTextForNOTNULLAttributeConstraint]).

			sepFlag := true.
		].


	]! !


!AdabasLikePlatform methodsFor: 'SQL'!
supportsConstraints
	"^<Boolean> This method returns true, if the database support a contraint concept"

	^false! !


!DatabaseRow methodsFor: 'accessing'!
at: aField

	^self at: aField ifAbsent: [self error: 'missing field'].! !

!DatabaseRow methodsFor: 'accessing'!
at: aField ifAbsent: absentBlock

	^(self wrapperAt: aField ifAbsent: absentBlock) contents.! !

!DatabaseRow methodsFor: 'accessing'!
at: aField put: aValue 
	
	"For generated fields, we expect the real value to be provided later by the database, so don't write a nil value"

	
	| wrapper dbValue |
	dbValue := aField convertToDatabaseForm: aValue.
	aField table == self table ifFalse: [self error: 'Invalid table'].
	wrapper := contents at: aField ifAbsentPut: [FieldValueWrapper new].
	(aValue isNil and: [aField isGenerated]) ifFalse: [wrapper contents: dbValue].
	^wrapper.! !

!DatabaseRow methodsFor: 'accessing'!
atName: aString put: anObject 
	self at: (table fieldNamed: aString) put: anObject! !

!DatabaseRow methodsFor: 'accessing'!
fields
	^table fields! !

!DatabaseRow methodsFor: 'accessing'!
owner

	^owner.! !

!DatabaseRow methodsFor: 'accessing'!
owner: anObject

	owner := anObject.! !

!DatabaseRow methodsFor: 'accessing'!
primaryKey

	| |
	self table primaryKeyFields isEmpty ifTrue: [^nil].
	^self table hasCompositePrimaryKey 
		ifTrue: [
			self table primaryKeyFields 
				collect: [:each | 	self at: each]]
		ifFalse: [self at: self table primaryKeyFields first].! !

!DatabaseRow methodsFor: 'accessing'!
table
	"Private - Answer the value of the receiver's ''table'' instance variable."

	^table! !

!DatabaseRow methodsFor: 'accessing'!
wrapperAt: aField

	^self wrapperAt: aField ifAbsent: [self error: 'Field not found'].! !

!DatabaseRow methodsFor: 'accessing'!
wrapperAt: aField ifAbsent: aBlock

	^contents at: aField ifAbsent: aBlock.! !

!DatabaseRow methodsFor: 'accessing'!
wrapperAt: aField put: aWrapper

	contents at: aField put: aWrapper.
	aWrapper isNowContainedBy: self and: aField.! !

!DatabaseRow methodsFor: 'printing'!
equalityStringForField: aDatabaseField

	| stream |
	stream := WriteStream on: (String new: 50).
	self printEqualityStringForField: aDatabaseField on: stream.
	^stream contents.! !

!DatabaseRow methodsFor: 'printing'!
printEqualityStringForField: aDatabaseField on: aStream 
	"Get around PostgreSQL bug.  Qualified names cannot appear in SET expression."
	aDatabaseField printNameOn: aStream withParameters: #().
	aStream nextPutAll: ' = '.
	self printValueOfField: aDatabaseField on: aStream! !

!DatabaseRow methodsFor: 'printing'!
printFieldValuesOn: aWriteStream 
	GlorpHelper 
		print: [:each | self sqlFormOfValue: (self at: each)]
		on: aWriteStream
		for: self table fields
		separatedBy: ','.! !

!DatabaseRow methodsFor: 'printing'!
printOn: aStream

	super printOn: aStream.
	aStream nextPutAll: '(' , (table name isNil ifTrue: [''] ifFalse: [table name]),  ')'.
	aStream cr.
	contents keysAndValuesDo: [:eachField :eachWrapper |
		aStream nextPutAll: '    '.
		eachField printOn: aStream.
		aStream nextPutAll: '->'.
		eachWrapper printOn: aStream.
		aStream cr.].! !

!DatabaseRow methodsFor: 'printing'!
printPrimaryKeyStringOn: aStream 
	
	GlorpHelper
		do:  [:eachField |
			self printEqualityStringForField: eachField on: aStream]
		for: table primaryKeyFields
		separatedBy: [aStream nextPutAll: ' AND '].! !

!DatabaseRow methodsFor: 'printing'!
printValueOfField: aDatabaseField on: aWriteStream 
	aWriteStream nextPutAll: (self sqlFormOfValue: (self at: aDatabaseField))! !

!DatabaseRow methodsFor: 'printing'!
sqlFormOfValue: anObject 
	"Print a primitive value in the form SQL wants it"

	anObject == nil ifTrue: [^'NULL'].
	^anObject printString! !

!DatabaseRow methodsFor: 'enumerating'!
fieldValuesDo: aBlock

	table fields do: [:each |
		aBlock value: (self at: each)].! !

!DatabaseRow methodsFor: 'enumerating'!
keysAndValuesDo: aBlock

	contents keysAndValuesDo: [:eachKey :eachValue |
		aBlock value: eachKey value: eachValue contents].! !

!DatabaseRow methodsFor: 'querying'!
hasValueFor: aField

	^(self wrapperAt: aField ifAbsent: [^false]) hasValue.! !

!DatabaseRow methodsFor: 'querying'!
shouldBeWritten
	^shouldBeWritten! !

!DatabaseRow methodsFor: 'initializing'!
initialize

	contents := IdentityDictionary new.
	shouldBeWritten := true.! !

!DatabaseRow methodsFor: 'sequencing'!
postWriteAssignSequences

	self table fields do: [:each |
		(self hasValueFor: self) ifFalse: [
			each sequencePolicy postWriteAssignSequenceValueFor: each in: self]].! !

!DatabaseRow methodsFor: 'sequencing'!
preWriteAssignSequences

	self table fields do: [:each |
		(self hasValueFor: each) ifFalse: [
			each sequencePolicy preWriteAssignSequenceValueFor: each in: self]].! !

!DatabaseRow methodsFor: 'configuring'!
shouldBeWritten: aBoolean 
	shouldBeWritten := aBoolean! !

!DatabaseRow methodsFor: 'configuring'!
table: anObject
	"Private - Set the value of the receiver's ''table'' instance variable to the argument, anObject."

	table := anObject! !


!DatabaseRow class methodsFor: 'instance creation'!
new

	^super new initialize.! !

!DatabaseRow class methodsFor: 'instance creation'!
newForTable: aTable

	^self new table: aTable.! !

!DatabaseRow class methodsFor: 'instance creation'!
newForTable: aTable withOwner: anObject

	^self new table: aTable; owner: anObject.! !


!DatabaseTable methodsFor: 'fields'!
addField: aField

	fields add: aField.
	aField isPrimaryKey ifTrue: [
		self addAsPrimaryKeyField: aField].
	aField table: self.
	aField position: fields size.
	^aField.! !

!DatabaseTable methodsFor: 'fields'!
addForeignKeyFrom: sourceField to: targetField

	foreignKeyConstraints add: (ForeignKeyConstraint sourceField:  sourceField targetField: targetField).! !

!DatabaseTable methodsFor: 'fields'!
fieldNamed: aString

	^fields detect: [:each | each name = aString] ifNone: [self addField: (DatabaseField named: aString)].! !

!DatabaseTable methodsFor: 'fields'!
newFieldNamed: aString

	^self fieldNamed: aString.! !

!DatabaseTable methodsFor: 'create/delete in db'!
creationStringFor: aDatabaseAccessor 
	| creationStream |
	creationStream := WriteStream on: (String new: 1000).
	creationStream
		nextPutAll: 'CREATE TABLE ';
		nextPutAll: self name;
		nextPutAll: ' ( ';
		cr.
	self printFieldsOn: creationStream for: aDatabaseAccessor.
	aDatabaseAccessor platform supportsConstraints ifTrue: [
		self hasPrimaryKeyConstraints 
			ifTrue: [self printDelimiterOn: creationStream].
		self printPrimaryKeyConstraintsOn: creationStream for: aDatabaseAccessor.
		self hasForeignKeyConstraints ifTrue: [self printDelimiterOn: creationStream].
		self printForeignKeyConstraintsOn: creationStream for: aDatabaseAccessor].
	creationStream nextPutAll: ')'.
	^creationStream contents! !

!DatabaseTable methodsFor: 'create/delete in db'!
dropForeignKeyConstraintsFromAccessor: aDatabaseAccessor 
	self foreignKeyConstraints 
		do: [:each | aDatabaseAccessor dropConstraint: each]! !

!DatabaseTable methodsFor: 'create/delete in db'!
dropFromAccessor: aDatabaseAccessor 
	self primaryKeyFields isEmpty 
		ifFalse: 
			[aDatabaseAccessor doCommand: 
					[aDatabaseAccessor 
						executeSQLString: 'ALTER TABLE ' , self name , ' DROP CONSTRAINT ' 
								, self primaryKeyUniqueConstraintName]
				ifError: [:ex | Transcript show: ex messageText].
			aDatabaseAccessor doCommand: 
					[aDatabaseAccessor 
						executeSQLString: 'ALTER TABLE ' , self name , ' DROP CONSTRAINT ' 
								, self primaryKeyConstraintName]
				ifError: [:ex | Transcript show: ex messageText]].
	aDatabaseAccessor dropTableNamed: self name! !

!DatabaseTable methodsFor: 'create/delete in db'!
primaryKeyConstraintName

	^self name, '_PK'.! !

!DatabaseTable methodsFor: 'create/delete in db'!
primaryKeyUniqueConstraintName

	^self name, '_UNIQ'.! !

!DatabaseTable methodsFor: 'create/delete in db'!
printDelimiterOn: aStream
	
	aStream
		nextPut: $,;
		cr! !

!DatabaseTable methodsFor: 'create/delete in db'!
printFieldsOn: creationStream for: aDatabaseAccessor 
	GlorpHelper 
		do: [:each | each printCreationStringFor: aDatabaseAccessor on: creationStream]
		for: fields
		separatedBy: [self printDelimiterOn: creationStream]! !

!DatabaseTable methodsFor: 'create/delete in db'!
printForeignKeyConstraintsOn: creationStream for: anObject 

	GlorpHelper 
		print: [:each | each creationString]
		on: creationStream
		for: foreignKeyConstraints
		separatedBy: ','! !

!DatabaseTable methodsFor: 'create/delete in db'!
printPrimaryKeyConstraintsOn: aStream for: aDatabaseAccessor 
	self primaryKeyFields isEmpty ifTrue: [^self].
	aStream nextPutAll: 'CONSTRAINT '.
	aStream nextPutAll: self primaryKeyConstraintName.
	aStream nextPutAll: ' PRIMARY KEY  ('.
	GlorpHelper 
		print: [:each | each name]
		on: aStream
		for: self primaryKeyFields
		separatedBy: ','.
	aStream nextPut: $).

	aStream
		nextPutAll: ',';
		cr.

	aStream nextPutAll: 'CONSTRAINT '.
	aStream nextPutAll: self primaryKeyUniqueConstraintName.
	aStream nextPutAll: ' UNIQUE  ('.
	GlorpHelper 
		print: [:each | each name]
		on: aStream
		for: self primaryKeyFields
		separatedBy: ','.
	aStream nextPut: $)! !

!DatabaseTable methodsFor: 'accessing'!
creator
	"Private - Answer the value of the receiver's ''creator'' instance variable."

	^creator! !

!DatabaseTable methodsFor: 'accessing'!
creator: anObject
	"Private - Set the value of the receiver's ''creator'' instance variable to the argument, anObject."

	creator := anObject! !

!DatabaseTable methodsFor: 'accessing'!
fields

	^fields! !

!DatabaseTable methodsFor: 'accessing'!
foreignKeyConstraints
	"Private - Answer the value of the receiver's ''foreignKeyConstraints'' instance variable."

	^foreignKeyConstraints! !

!DatabaseTable methodsFor: 'accessing'!
name
	"Private - Answer the value of the receiver's ''name'' instance variable."

	^name! !

!DatabaseTable methodsFor: 'accessing'!
name: anObject
	"Private - Set the value of the receiver's ''name'' instance variable to the argument, anObject."

	name := anObject! !

!DatabaseTable methodsFor: 'accessing'!
parent

	^parent.! !

!DatabaseTable methodsFor: 'accessing'!
parent: aDatabaseTable

	parent := aDatabaseTable.! !

!DatabaseTable methodsFor: 'accessing'!
primaryKeyFields

	^primaryKeyFields.! !

!DatabaseTable methodsFor: 'accessing'!
qualifiedName

	^self name.! !

!DatabaseTable methodsFor: 'accessing'!
sqlTableName
	"Our name, as appropriate for the list of tables in a SQL statement. Take into account aliasing"
	^parent isNil 
		ifTrue: [self name]
		ifFalse: [parent sqlTableName, ' ', self name].! !

!DatabaseTable methodsFor: 'testing'!
hasCompositePrimaryKey

	^primaryKeyFields size > 1.! !

!DatabaseTable methodsFor: 'testing'!
hasConstraints
	^self hasForeignKeyConstraints or: [self hasPrimaryKeyConstraints]! !

!DatabaseTable methodsFor: 'testing'!
hasForeignKeyConstraints
	^foreignKeyConstraints isEmpty not! !

!DatabaseTable methodsFor: 'testing'!
hasPrimaryKeyConstraints
	^self primaryKeyFields isEmpty not.! !

!DatabaseTable methodsFor: 'initialize'!
initialize

	creator := ''.
	fields := OrderedCollection new.
	primaryKeyFields := #().
	foreignKeyConstraints := OrderedCollection new: 4.! !

!DatabaseTable methodsFor: 'printing'!
printOn: aStream

	super printOn: aStream.
	aStream 
		nextPutAll: '(';
		nextPutAll: (name isNil ifTrue: [''] ifFalse: [name]);
		nextPutAll: ')'.! !

!DatabaseTable methodsFor: 'printing'!
printSQLOn: aWriteStream withParameters: aDictionary
	aWriteStream nextPutAll: self name! !

!DatabaseTable methodsFor: 'printing'!
sqlString

	^name.! !

!DatabaseTable methodsFor: 'private/fields'!
addAsPrimaryKeyField: aField

	primaryKeyFields := primaryKeyFields, (Array with: aField)! !


!DatabaseTable class methodsFor: 'instance creation'!
named: aString

	^self new name: aString.! !

!DatabaseTable class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!Date methodsFor: 'printing'!
glorpPrintSQLOn: aStream
	"Print the date in ISO format. 'yyyy-mm-dd'  Don't rely on any dialect-specific formatting or padding mechanisms"
	| monthString dayString |
	aStream 
		nextPut: $';
		print: self year;
		nextPut: $-.
	monthString := self monthIndex printString.
	monthString size = 1 ifTrue: [aStream nextPut: $0 ].
	aStream nextPutAll: monthString.
	aStream nextPut: $-.
	dayString := self dayOfMonth printString.
	dayString size = 1 ifTrue: [aStream nextPut: $0 ].
	aStream nextPutAll: dayString.
	aStream nextPut: $'.! !


!Descriptor methodsFor: 'accessing'!
addMapping: aMapping

	mappings add: aMapping.
	aMapping descriptor: self.
	mappedFields := nil.! !

!Descriptor methodsFor: 'accessing'!
addMultipleTableCriteria: anExpression 
	self multipleTableCriteria add: anExpression! !

!Descriptor methodsFor: 'accessing'!
addTable: aDatabaseTable 
	tables add: aDatabaseTable.! !

!Descriptor methodsFor: 'accessing'!
allMappingsForField: aField 
	"Return all of the mappings that use this field"
	^mappings select: [:each | each mappedFields includes: aField]! !

!Descriptor methodsFor: 'accessing'!
cachePolicy
	cachePolicy isNil ifTrue: [^system cachePolicy].
	^cachePolicy! !

!Descriptor methodsFor: 'accessing'!
cachePolicy: aCachePolicy
	cachePolicy:= aCachePolicy! !

!Descriptor methodsFor: 'accessing'!
describedClass
	"Private - Answer the value of the receiver's ''describedClass'' instance variable."

	^describedClass! !

!Descriptor methodsFor: 'accessing'!
describedClass: anObject
	"Private - Set the value of the receiver's ''describedClass'' instance variable to the argument, anObject."

	describedClass := anObject! !

!Descriptor methodsFor: 'accessing'!
directMappingForField: aField 
	"Return a single, direct mapping for this field. There may conceivably be more than one, but they all have to agree, so it shouldn't matter as far as the value. There may also be none."

	^mappings 
		detect: [:each | each isRelationship not and: [each mappedFields includes: aField]]
		ifNone: [nil]! !

!Descriptor methodsFor: 'accessing'!
fieldsForSelectStatement
	"Return all the fields that are mapped, in the order that they occur in the table, as appropriate for the set of fields being selected"
	| fieldsForSelect fieldSet |
	fieldSet := IdentitySet new: mappings size.
	mappings do: [:each | fieldSet addAll: each fieldsForSelectStatement].
	fieldsForSelect := self mappedFields select: [:each | fieldSet includes: each].
	^fieldsForSelect.! !

!Descriptor methodsFor: 'accessing'!
initialize

	mappings := OrderedCollection new.
	tables := OrderedCollection new: 1.! !

!Descriptor methodsFor: 'accessing'!
mappedFields
	"Return all the fields that are mapped, in the order that they occur in the table."
	mappedFields isNil ifTrue: [
		| fieldSet |
		fieldSet := IdentitySet new: mappings size.
		mappings do: [:each | fieldSet addAll: each mappedFields].
		mappedFields := OrderedCollection new.
		tables do: [:each |
			each fields do: [:eachField | (fieldSet includes: eachField) ifTrue: [mappedFields add: eachField]]]].
	^mappedFields.! !

!Descriptor methodsFor: 'accessing'!
mappingForAttributeNamed: aSymbol

	^mappings detect: [:each | each attributeName == aSymbol] ifNone: [nil]! !

!Descriptor methodsFor: 'accessing'!
multipleTableCriteria
	multipleTableCriteria isNil
		ifTrue: [multipleTableCriteria := OrderedCollection new: 1].
	^multipleTableCriteria.! !

!Descriptor methodsFor: 'accessing'!
primaryTable
	^tables first.! !

!Descriptor methodsFor: 'accessing'!
session

	^system session.! !

!Descriptor methodsFor: 'accessing'!
system
	^system! !

!Descriptor methodsFor: 'accessing'!
system: anObject
	system := anObject! !

!Descriptor methodsFor: 'accessing'!
table
	^tables first.! !

!Descriptor methodsFor: 'accessing'!
table: aDatabaseTable

	tables add: aDatabaseTable.! !

!Descriptor methodsFor: 'accessing'!
tables
	^tables! !

!Descriptor methodsFor: 'mapping'!
createRowsFor: anObject in: aRowMap

	|  |
	anObject class == self describedClass ifFalse: [self error: 'wrong descriptor for this object'].
	mappings do: [:each |
		each mapFromObject: anObject intoRowsIn: aRowMap].
	self multipleTableCriteria do: [:each |
		each mapFromSource: anObject andTarget: anObject intoRowsIn: aRowMap].! !

!Descriptor methodsFor: 'mapping'!
mappings
	^ReadStream on: mappings! !

!Descriptor methodsFor: 'mapping'!
populateObject: anObject fromRow: anArray inBuilder: anElementBuilder 
	"Answer an object using the values for the specified fields."

	mappings do: 
			[:each | 
			each 
				mapFromRow: anArray
				intoObject: anObject
				inElementBuilder: anElementBuilder]! !

!Descriptor methodsFor: 'mapping'!
primaryKeyExpressionFor: anObject
	| expression |
	anObject class == describedClass ifFalse: [self error: 'Wrong descriptor for this object'].
	expression := nil.
	self primaryKeyMappings do: [:each | 
		| clause |
		clause := each expressionFor: anObject.
		expression := clause exAnd: expression].
	^expression.! !

!Descriptor methodsFor: 'mapping'!
primaryKeyFor: anObject
	| result |
	anObject class == describedClass ifFalse: [self error: 'Wrong descriptor for this object'].
	result := self primaryKeyMappings collect: [:each | 
		each getValueFrom: anObject].
	^result size = 1 ifTrue: [result at: 1] ifFalse: [result].! !

!Descriptor methodsFor: 'mapping'!
primaryKeyMappings
	^self primaryTable primaryKeyFields 
		collect: [:each | self directMappingForField: each]! !

!Descriptor methodsFor: 'tracing'!
defaultTracing
	^Tracing new.! !

!Descriptor methodsFor: 'tracing'!
setupTracing: aTracing
	"Find all the other objects that need to be read when this one is read"

	self trace: aTracing context: aTracing base.! !

!Descriptor methodsFor: 'tracing'!
trace: aTracing context: anExpression
	"For each mapping, check if the relationship is involved in the set of things
to be read"
	mappings do: [:each |
		each trace: aTracing context: anExpression].! !

!Descriptor methodsFor: 'testing'!
mapsPrimaryKeys

	| primaryKeyFields  |
	primaryKeyFields := self primaryTable primaryKeyFields.
	primaryKeyFields isEmpty ifTrue: [^false].
	primaryKeyFields do: [:each | (self mappedFields includes: each) ifFalse: [^false]].
	^true.! !

!Descriptor methodsFor: 'printing'!
printOn: aStream

	super printOn: aStream.
	aStream nextPutAll: '('.
	describedClass printOn: aStream.
	aStream nextPutAll: ')'.! !

!Descriptor methodsFor: 'internal'!
readBackNewRowInformationFor: anObject! !

!Descriptor methodsFor: 'internal'!
referencedIndependentObjectsFrom: anObject do: aBlock

	mappings do: [:each |
		(each referencedIndependentObjectsFrom: anObject) do: [:eachReferencedObject |
			aBlock value: eachReferencedObject]].! !


!Descriptor class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!DescriptorSystem methodsFor: 'accessing'!
allClasses

	^self allClassNames collect: [:each | Smalltalk at: each].! !

!DescriptorSystem methodsFor: 'accessing'!
allDescriptors

	^self allClasses collect: [:each | 
		self descriptorFor: each].! !

!DescriptorSystem methodsFor: 'accessing'!
allTables
	^self allTableNames collect: [:each | 
		self tableNamed: each].! !

!DescriptorSystem methodsFor: 'accessing'!
cachePolicy
	"Return the default cache policy that will be used for descriptors that don't specify their own policy"
	cachePolicy isNil ifTrue: [cachePolicy := CachePolicy new].
	^cachePolicy.! !

!DescriptorSystem methodsFor: 'accessing'!
cachePolicy: aCachePolicy
	cachePolicy := aCachePolicy.! !

!DescriptorSystem methodsFor: 'accessing'!
defaultTracing

	^Tracing new! !

!DescriptorSystem methodsFor: 'accessing'!
session
	^session! !

!DescriptorSystem methodsFor: 'accessing'!
session: anObject
	session := anObject! !

!DescriptorSystem methodsFor: 'private'!
initialize

	descriptors := Dictionary new.
	tables := Dictionary new.! !

!DescriptorSystem methodsFor: 'private'!
newDescriptorFor: aClass

	| newDescriptor selector |
	(self allClassNames includes: aClass name) ifFalse: [^nil].
	newDescriptor := Descriptor new.
	newDescriptor system: self.
	newDescriptor describedClass: aClass.
	selector := ('descriptorFor', aClass name, ':') asSymbol.
	self perform:  selector with: newDescriptor.
	^newDescriptor.! !

!DescriptorSystem methodsFor: 'private'!
newTableNamed: aString
	| newTable str |
	newTable := DatabaseTable new.
	newTable name: aString.
	str := aString copyWithout: $_.
	self perform: ('tableFor', str, ':') asSymbol with: newTable.
	^newTable.! !

!DescriptorSystem methodsFor: 'api'!
descriptorFor: aClassOrObject 

	| theClass |
	theClass := aClassOrObject isBehavior 
				ifTrue: [aClassOrObject]
				ifFalse: [aClassOrObject class].
	^descriptors at: theClass ifAbsentPut: [self newDescriptorFor: theClass]! !

!DescriptorSystem methodsFor: 'api'!
existingTableNamed: aString

	^tables at: aString ifAbsent: [self error: 'missing table'].! !

!DescriptorSystem methodsFor: 'api'!
hasDescriptorFor: aClassOrObject 

	^(self descriptorFor: aClassOrObject) notNil.! !

!DescriptorSystem methodsFor: 'api'!
tableNamed: aString

	^tables at: aString ifAbsentPut: [self newTableNamed: aString].! !


!DescriptorSystem class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!Dialect class methodsFor: 'identifying' stamp: 'nop 5/29/2002 10:36'!
dialectName
	self isVisualWorks
		ifTrue: [^ 'VisualWorks'].
	self isVisualAge
		ifTrue: [^ 'VisualAge'].
	self isDolphin
		ifTrue: [^ 'Dolphin'].
	self isSqueak
		ifTrue: [^ 'Squeak'].
	self error: 'I don''t know'! !

!Dialect class methodsFor: 'identifying'!
isDolphin
	Smalltalk at: #DolphinSplash ifAbsent: [^false].
	^true! !

!Dialect class methodsFor: 'identifying' stamp: 'nop 5/29/2002 10:36'!
isSqueak
	Smalltalk
				at: #SqueakPage
				ifAbsent: [^ false].

	^true! !

!Dialect class methodsFor: 'identifying'!
isVisualAge
	| sys |
	sys := Smalltalk at: #System ifAbsent: [^false].
	(sys respondsTo: #vmType) ifFalse: [^false].
	^sys vmType = 'ES'! !

!Dialect class methodsFor: 'identifying'!
isVisualWorks
	Smalltalk class selectors do: [ :s | 
		(s == #versionName and: [ (Smalltalk versionName copyFrom: 1 to: 11) = 'VisualWorks']) 
		    ifTrue: [^true]].
	^false! !


!DolphinDatabaseAccessor methodsFor: 'accessing'!
isLoggedIn

	^connection notNil.! !

!DolphinDatabaseAccessor methodsFor: 'executing'!
executeSQLString: aString

	logging ifTrue: [Transcript show: aString; cr].
	^connection query: aString! !

!DolphinDatabaseAccessor methodsFor: 'executing'!
externalDatabaseErrorSignal

	^Smalltalk at: #DBError! !

!DolphinDatabaseAccessor methodsFor: 'initialize'!
initialize

	super initialize.
	logging := true.! !

!DolphinDatabaseAccessor methodsFor: 'login'!
loginIfError: aBlock

	self
		doCommand:
			[connection := ((Smalltalk at: #DBConnection) new)
				dsn: currentLogin connectString;
				uid: currentLogin username;
				pwd: currentLogin password;
				connect.]
		ifError: aBlock! !

!DolphinDatabaseAccessor methodsFor: 'login'!
logout

	connection notNil
		ifTrue:
			[[connection disconnect.] ensure: [connection := nil]].! !

!DolphinDatabaseAccessor methodsFor: 'login'!
showDialog: aString

	(Smalltalk at: #MessageBox) warning: aString.! !

!DolphinDatabaseAccessor methodsFor: 'transactions'!
beginTransaction

	connection beginRWTxn.! !

!DolphinDatabaseAccessor methodsFor: 'transactions'!
commitTransaction

	logging ifTrue: 
		[Transcript show: 'Commit Transaction'; cr].
	connection commitTxn.! !

!DolphinDatabaseAccessor methodsFor: 'transactions'!
isInTransaction

	^connection isInTransaction! !

!DolphinDatabaseAccessor methodsFor: 'transactions'!
rollbackTransaction
	logging ifTrue: 
		[Transcript show: 'Rollback Transaction'; cr].
	connection rollbackTxn.! !


!ElementBuilder methodsFor: 'building objects'!
buildObjectFrom: anArray 
	self requiresPopulating 
		ifTrue: 
			[self populateInstanceFromRow: anArray inBuilder: self.
			self registerObjectInUnitOfWork]! !

!ElementBuilder methodsFor: 'building objects'!
buildProxyFrom: anArray 
	| parameters |
	parameters := IdentityDictionary new.
	self descriptor primaryTable primaryKeyFields do: [:eachField |
		parameters at: eachField put: (self valueOf: eachField in: anArray)].
	instance := (self newProxy) 
		session: self session; 
		parameters: parameters.
	^self! !

!ElementBuilder methodsFor: 'building objects'!
canCache

	^self descriptor mapsPrimaryKeys! !

!ElementBuilder methodsFor: 'building objects'!
findInstanceForRow: row useProxy: useProxies 
	self lookupCachedObjectForRow: row.
	instance isNil ifFalse: [
		requiresPopulating := query shouldRefresh. ^self].
	useProxies 
		ifTrue: [self buildProxyFrom: row]
		ifFalse: 
			[requiresPopulating := true.
			instance := expression descriptor describedClass basicNew.
			self canCache ifTrue: [self session cacheAt: key put: instance]]! !

!ElementBuilder methodsFor: 'building objects'!
lookupCachedObjectForRow: row 
	self canCache 
		ifTrue: 
			[key := self primaryKeyFromRow: row.
			instance := self session cacheLookupForClass: expression descriptor describedClass key: key]! !

!ElementBuilder methodsFor: 'building objects'!
newProxy
	"Create a proxy with a primary key query in which the parameters are the primary key fields"
	| whereExpression |
	whereExpression := PrimaryKeyExpression new.
	self descriptor primaryTable primaryKeyFields do: [:eachField | 
		whereExpression addSource: eachField target: eachField].
	^Proxy 
		returningOneOf: query resultClass 
		where: whereExpression.! !

!ElementBuilder methodsFor: 'building objects'!
populateInstanceFromRow: row inBuilder: anElementBuilder 
	expression descriptor 
		populateObject: instance
		fromRow: row
		inBuilder: anElementBuilder! !

!ElementBuilder methodsFor: 'building objects'!
primaryKeyFromRow: aRow
	
	key := self descriptor table primaryKeyFields collect: [:each |
		aRow atIndex: (each position)].
	key size = 0 ifTrue: [self error: 'Missing primary key'].
	key size = 1 ifTrue: [^key first].
	^key.! !

!ElementBuilder methodsFor: 'building objects'!
registerObjectInUnitOfWork
	"If there is a current unit of work, then we must register in it, after population because that way the state is already in place. The nil checks are mostly for safety during unit tests, as those conditions should never occur in real use"
	query isNil ifTrue: [^self].
	query session isNil ifTrue: [^self].
	query session register: instance.! !

!ElementBuilder methodsFor: 'accessing'!
descriptor
	
	^expression descriptor.! !

!ElementBuilder methodsFor: 'accessing'!
expression
	^expression! !

!ElementBuilder methodsFor: 'accessing'!
expression: anExpression
	expression := anExpression.! !

!ElementBuilder methodsFor: 'accessing'!
fieldTranslations
	^fieldTranslations! !

!ElementBuilder methodsFor: 'accessing'!
fieldTranslations: aDictionary 
	fieldTranslations := aDictionary.! !

!ElementBuilder methodsFor: 'accessing'!
instance
	^instance! !

!ElementBuilder methodsFor: 'accessing'!
instance: anObject
	instance := anObject! !

!ElementBuilder methodsFor: 'accessing'!
instanceClass
	^instanceClass! !

!ElementBuilder methodsFor: 'accessing'!
instanceClass: anObject
	instanceClass := anObject! !

!ElementBuilder methodsFor: 'accessing'!
query
	^query! !

!ElementBuilder methodsFor: 'accessing'!
query: aQuery 
	query := aQuery! !

!ElementBuilder methodsFor: 'accessing'!
requiresDistinct

	^expression requiresDistinct.! !

!ElementBuilder methodsFor: 'accessing'!
requiresPopulating
	^requiresPopulating! !

!ElementBuilder methodsFor: 'accessing'!
requiresPopulating: anObject
	requiresPopulating := anObject! !

!ElementBuilder methodsFor: 'accessing'!
session
	^expression descriptor session.! !

!ElementBuilder methodsFor: 'selecting fields'!
fieldsForSelectStatement
	^self fieldsFromThePerspectiveOfTheMainSelect: (self fieldsFromMyPerspective).! !

!ElementBuilder methodsFor: 'selecting fields'!
fieldsFromMyPerspective
	^query returnProxies 
		ifTrue: [self descriptor table primaryKeyFields]
		ifFalse: [self descriptor mappedFields]! !

!ElementBuilder methodsFor: 'selecting fields'!
fieldsFromThePerspectiveOfTheMainSelect: aCollection
	^expression translateFields: aCollection.! !

!ElementBuilder methodsFor: 'initializing'!
initialize

	requiresPopulating := false.! !

!ElementBuilder methodsFor: 'translating fields'!
translateFieldPosition: aDatabaseField 
	fieldTranslations isNil ifTrue: [^aDatabaseField position].
	^fieldTranslations at: aDatabaseField.! !

!ElementBuilder methodsFor: 'translating fields'!
valueOf: aField in: anArray

	^anArray atIndex: (self translateFieldPosition: aField rootField).! !

!ElementBuilder methodsFor: 'executing'!
hasFieldTranslations
	^self fieldTranslations notNil! !


!ElementBuilder class methodsFor: 'instance creation'!
for: anExpression

	^self new
		expression: anExpression.! !

!ElementBuilder class methodsFor: 'instance creation'!
for: anExpression in: aQuery

	^self new
		expression: anExpression;
		query: aQuery.! !

!ElementBuilder class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!EmailAddress methodsFor: 'accessing'!
host
	^host! !

!EmailAddress methodsFor: 'accessing'!
host: anObject
	host := anObject! !

!EmailAddress methodsFor: 'accessing'!
id
	^ id! !

!EmailAddress methodsFor: 'accessing'!
user
	^user! !

!EmailAddress methodsFor: 'accessing'!
user: anObject
	user := anObject! !


!Encyclopedia methodsFor: 'accessories'!
entries
	^entries.! !

!Encyclopedia methodsFor: 'initialize'!
initialize

	entries := Dictionary new.! !


!Encyclopedia class methodsFor: 'instance creation'!
new

	^super new initialize.! !

!Encyclopedia class methodsFor: 'examples'!
example1
	| result |
	result := Encyclopedia new.
	result entries at: 'one' put: EncyclopediaEntry example1.
	result entries at: 'two' put: EncyclopediaEntry example2.
	^result.! !


!EncyclopediaEntry methodsFor: 'accessing'!
id: aSmallInteger 
	id := aSmallInteger.! !

!EncyclopediaEntry methodsFor: 'accessing'!
name: aString 
	name := aString! !

!EncyclopediaEntry methodsFor: 'accessing'!
text: aString 
	text := aString.! !


!EncyclopediaEntry class methodsFor: 'examples'!
example1
	^self new
		id: 1;
		name: 'One';
		text: 'The first number (not counting zero)'.! !

!EncyclopediaEntry class methodsFor: 'examples'!
example2
	^self new
		id: 2;
		name: 'Two';
		text: 'The second number (comes after 1)'.! !


!FakeElementBuilder methodsFor: 'accessing'!
value: anObject
	value := anObject.! !

!FakeElementBuilder methodsFor: 'element builder protocol'!
valueOf: aField in: anArray

	^value.! !


!FieldUnifier methodsFor: 'unifying'!
calculateRows

	rows := OrderedCollection new: fields size.
	fieldsWithRows := OrderedCollection new: fields size.
	fields with: objects do: [:eachField :eachObject |
		eachObject isNil 
			ifFalse: [
				fieldsWithRows add: eachField.
				rows add: (rowMap findOrAddRowForTable: eachField table withKey: eachObject)]].! !

!FieldUnifier methodsFor: 'unifying'!
findExistingWrappersIfNone: aBlock 

	| allWrappers |
	allWrappers := IdentitySet new: 5.
	fieldsWithRows with: rows do: [:eachField :eachRow | 
		| wrapper |
		wrapper := eachRow wrapperAt: eachField ifAbsent: [nil].
		wrapper isNil ifFalse: [allWrappers add: wrapper]].
	^allWrappers isEmpty
		ifTrue: [aBlock value]
		ifFalse: [allWrappers asArray].! !

!FieldUnifier methodsFor: 'unifying'!
findWrapperToUseFrom: aWrapperCollection 
	| wrappersWithValues winner |
	wrappersWithValues := aWrapperCollection select: [:each | each hasValue].
	wrappersWithValues size > 1 
		ifTrue: [self error: 'Conflicting values in rows'].
	winner := wrappersWithValues size = 1 
				ifTrue: [wrappersWithValues at: 1]
				ifFalse: [aWrapperCollection first].
	^winner! !

!FieldUnifier methodsFor: 'unifying'!
unify
	|  wrappers |
	self calculateRows.
	wrappers := self findExistingWrappersIfNone: [Array with: FieldValueWrapper new].
	self unifyWrappers: wrappers.! !

!FieldUnifier methodsFor: 'unifying'!
unifyWrappers: aWrapperCollection 
	| winner allRows allFields |
	winner := self findWrapperToUseFrom: aWrapperCollection.
	allRows := OrderedCollection new.
	allFields := OrderedCollection new.
	aWrapperCollection do: 
			[:eachWrapper | 
			eachWrapper containedBy do: 
					[:eachRowAndField | 
					allRows add: eachRowAndField first.
					allFields add: eachRowAndField last]].
	allRows addAll: rows.
	allFields addAll: fieldsWithRows.
	allFields with: allRows
		do: [:eachField :eachRow | eachRow wrapperAt: eachField put: winner]! !

!FieldUnifier methodsFor: 'accessing'!
fields
	^fields! !

!FieldUnifier methodsFor: 'accessing'!
fields: anObject
	fields := anObject! !

!FieldUnifier methodsFor: 'accessing'!
objects
	^objects! !

!FieldUnifier methodsFor: 'accessing'!
objects: anObject
	objects := anObject! !

!FieldUnifier methodsFor: 'accessing'!
rowMap
	^rowMap! !

!FieldUnifier methodsFor: 'accessing'!
rowMap: anObject
	rowMap := anObject! !


!FieldUnifier class methodsFor: 'instance creation'!
unifyFields: fields correspondingTo: objects in: aRowMap 

	self new
		fields: fields;
		objects: objects;
		rowMap: aRowMap;
		unify.! !


!FieldValueWrapper methodsFor: 'public'!
containedBy

	^containedBy.! !

!FieldValueWrapper methodsFor: 'public'!
contents

	^contents.! !

!FieldValueWrapper methodsFor: 'public'!
contents: anObject

	(hasValue and: [contents ~= anObject]) ifTrue: [self error: 'Inconsistent values in field'].
	contents := anObject.
	hasValue := true.! !

!FieldValueWrapper methodsFor: 'public'!
hasValue

	^hasValue.! !

!FieldValueWrapper methodsFor: 'public'!
initialize

	hasValue := false.
	containedBy := OrderedCollection new: 5.! !

!FieldValueWrapper methodsFor: 'public'!
printOn: aStream

	aStream 
		nextPutAll: '<<'.
	self hasValue ifTrue: [aStream print: contents].
	aStream
		nextPutAll: '>>'.! !

!FieldValueWrapper methodsFor: 'containing'!
isNowContainedBy: aRow and: aField

	| existingEntry |
	existingEntry := containedBy detect: [:each | each first == aRow and: [each last == aField]] ifNone: [nil].
	existingEntry notNil ifTrue: [^self].
	containedBy add: (Array with: aRow with: aField).! !


!FieldValueWrapper class methodsFor: 'public'!
new

	^super new initialize.! !


!ForeignKeyConstraint methodsFor: 'printing'!
creationString
	
	^'CONSTRAINT ', self name, ' FOREIGN KEY (', sourceField name, ') REFERENCES ', targetField asConstraintReferenceString.! !

!ForeignKeyConstraint methodsFor: 'printing'!
dropString
	
	^'ALTER TABLE ', sourceField table sqlString, ' DROP CONSTRAINT ', self name.! !

!ForeignKeyConstraint methodsFor: 'printing'!
name
	| stream |
	stream := WriteStream on: (String new: 100).
	sourceField printForConstraintNameOn: stream maxLength: 11.
	stream nextPutAll: '_TO_'.
	targetField printForConstraintNameOn: stream maxLength: 11.
	stream nextPutAll: '_REF'.
	^stream contents! !

!ForeignKeyConstraint methodsFor: 'accessing'!
sourceField
	^sourceField! !

!ForeignKeyConstraint methodsFor: 'accessing'!
sourceField: aDatabaseField 
	sourceField := aDatabaseField! !

!ForeignKeyConstraint methodsFor: 'accessing'!
targetField
	^targetField! !

!ForeignKeyConstraint methodsFor: 'accessing'!
targetField: aDatabaseField 
	targetField := aDatabaseField! !

!ForeignKeyConstraint methodsFor: 'initializing'!
sourceField: aDatabaseField targetField: anotherDatabaseField

	sourceField := aDatabaseField.
	targetField := anotherDatabaseField.! !


!ForeignKeyConstraint class methodsFor: 'instance creation'!
sourceField: aDatabaseField targetField: anotherDatabaseField

	^self new
		sourceField: aDatabaseField
		targetField: anotherDatabaseField.! !


!GlorpDemoDescriptorSystem methodsFor: 'other'!
allClassNames

	^#(Person Address Customer BankTransaction BankAccount GlorpMoney ServiceCharge BankAccountNumber EmailAddress Passenger Airline).! !

!GlorpDemoDescriptorSystem methodsFor: 'other'!
allTableNames

	^#('GR_ADDRESS' 'PERSON' 'GR_CUSTOMER' 'BANK_TRANS' 'BANK_ACCT' 'CUSTOMER_ACCT_LINK' 'EMAIL_ADDRESS' 'STUFF' 'PASSENGER' 'AIRLINE' 'FREQUENT_FLYER').! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleAccountRow1

	| accountTable row |
	accountTable := self tableNamed: 'BANK_ACCT'.
	row := DatabaseRow newForTable: accountTable.
	row at: (accountTable fieldNamed: 'ID') put: 9874.
	row at: (accountTable fieldNamed: 'BANK_CODE') put: 1.
	row at: (accountTable fieldNamed: 'BRANCH_NO') put: 2.
	row at: (accountTable fieldNamed: 'ACCT_NO') put: 3.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleAccountRow2

	| accountTable row |
	accountTable := self tableNamed: 'BANK_ACCT'.
	row := DatabaseRow newForTable: accountTable.
	row at: (accountTable fieldNamed: 'ID') put: 6.
	row at: (accountTable fieldNamed: 'BANK_CODE') put: 2.
	row at: (accountTable fieldNamed: 'BRANCH_NO') put: 3.
	row at: (accountTable fieldNamed: 'ACCT_NO') put: 4.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleAddressRow
	| addressTable row |
	addressTable := self  tableNamed: 'GR_ADDRESS'.
	row := DatabaseRow newForTable: addressTable.
	row at: (addressTable fieldNamed: 'ID') put: 123.
	row at: (addressTable fieldNamed: 'STREET') put: 'Paseo Montril'.
	row at: (addressTable fieldNamed: 'HOUSE_NUM') put: '10185'.
	^row! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleBankTransactionRow
	| table row |
	table := self tableNamed: 'BANK_TRANS'.
	row := DatabaseRow newForTable: table.

	row atName: 'ID' put: 1.
	row atName: 'OWNER_ID' put: nil.
	row atName: 'AMT_CURR' put: 'CDN'.
	row atName: 'AMT_AMT' put: 7.
	row atName: 'SRVC_DESC' put: 'additional overcharge'.
	row atName: 'SRVC_AMT_CURR' put: 'USD'.
	row atName: 'SRVC_AMT_AMT' put: 2.

	^row! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleCALinkRow1

	| linkTable row |
	linkTable := self tableNamed: 'CUSTOMER_ACCT_LINK'.
	row := DatabaseRow newForTable: linkTable.
	row at: (linkTable fieldNamed: 'ACCT_ID') put: 9874.
	row at: (linkTable fieldNamed: 'CUSTOMER_ID') put: 27.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleCALinkRow2

	| linkTable row |
	linkTable := self tableNamed: 'CUSTOMER_ACCT_LINK'.
	row := DatabaseRow newForTable: linkTable.
	row at: (linkTable fieldNamed: 'ACCT_ID') put: 6.
	row at: (linkTable fieldNamed: 'CUSTOMER_ID') put: 27.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleCustomerRow1

	| customerTable row |
	customerTable := self tableNamed: 'GR_CUSTOMER'.
	row := DatabaseRow newForTable: customerTable.
	row at: (customerTable fieldNamed: 'ID') put: 27.
	row at: (customerTable fieldNamed: 'NAME') put: 'aCustomer'.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleEmailAddressRow1

	| personTable row |
	personTable := self tableNamed: 'EMAIL_ADDRESS'.
	row := DatabaseRow newForTable: personTable.
	row at: (personTable fieldNamed: 'ID') put: 42.
	row at: (personTable fieldNamed: 'USER_NAME') put: 'alan'.
	row at: (personTable fieldNamed: 'HOST_NAME') put: 'objectpeople.com'.
	row at: (personTable fieldNamed: 'PERSON_ID') put: 3.

	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleEmailAddressRow2

	| personTable row |
	personTable := self tableNamed: 'EMAIL_ADDRESS'.
	row := DatabaseRow newForTable: personTable.
	row at: (personTable fieldNamed: 'ID') put: 54321.
	row at: (personTable fieldNamed: 'USER_NAME') put: 'johnson'.
	row at: (personTable fieldNamed: 'HOST_NAME') put: 'cs.uiuc.edu'.
	row at: (personTable fieldNamed: 'PERSON_ID') put: 3.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleFrequentFlyerRow
	| ffTable row |
	ffTable := self tableNamed: 'FREQUENT_FLYER'.
	row := DatabaseRow newForTable: ffTable.
	row at: (ffTable fieldNamed: 'ID') put: 1.
	row at: (ffTable fieldNamed: 'POINTS') put: 10000.
	row at: (ffTable fieldNamed: 'AIRLINE_ID') put: nil.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
exampleModifiedAddressRow
	| addressTable row |
	addressTable := self  tableNamed: 'GR_ADDRESS'.
	row := DatabaseRow newForTable: addressTable.
	row at: (addressTable fieldNamed: 'ID') put: 123.
	row at: (addressTable fieldNamed: 'STREET') put: 'Something Else'.
	row at: (addressTable fieldNamed: 'HOUSE_NUM') put: '10185'.
	^row! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
examplePassengerRow
	| passengerTable row |
	passengerTable := self tableNamed: 'PASSENGER'.
	row := DatabaseRow newForTable: passengerTable.
	row at: (passengerTable fieldNamed: 'ID') put: 1.
	row at: (passengerTable fieldNamed: 'NAME') put: 'Some Passenger'.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
examplePersonRow1

	| personTable row |
	personTable := self tableNamed: 'PERSON'.
	row := DatabaseRow newForTable: personTable.
	row at: (personTable fieldNamed: 'ID') put: 3.
	row at: (personTable fieldNamed: 'NAME') put: 'aPerson'.
	row at: (personTable fieldNamed: 'ADDRESS_ID') put: 123.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'examples'!
examplePersonRow2

	| personTable row |
	personTable := self tableNamed: 'PERSON'.
	row := DatabaseRow newForTable: personTable.
	row at: (personTable fieldNamed: 'ID') put: 4.
	row at: (personTable fieldNamed: 'NAME') put: 'aPerson'.
	row at: (personTable fieldNamed: 'ADDRESS_ID') put: nil.
	^row.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables'!
tableForBANKACCT: aTable

	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.
	aTable newFieldNamed: 'BANK_CODE'.
	aTable newFieldNamed: 'BRANCH_NO'.
	aTable newFieldNamed: 'ACCT_NO'.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables'!
tableForBANKTRANS: aTable 
	| ownerId |
	(aTable newFieldNamed: 'ID')
		beNumeric;
		bePrimaryKey;
		useSequencingInMemory.
	ownerId := (aTable newFieldNamed: 'OWNER_ID') beNumeric.
	aTable addForeignKeyFrom: ownerId
		to: ((self tableNamed: 'GR_CUSTOMER') fieldNamed: 'ID').
	aTable newFieldNamed: 'AMT_CURR'.
	aTable newFieldNamed: 'AMT_AMT'.
	aTable newFieldNamed: 'SRVC_DESC'.
	aTable newFieldNamed: 'SRVC_AMT_CURR'.
	aTable newFieldNamed: 'SRVC_AMT_AMT'.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables'!
tableForCUSTOMERACCTLINK: aTable

	| customerId accountId |
	customerId := (aTable newFieldNamed: 'CUSTOMER_ID') beNumeric.
	aTable addForeignKeyFrom: customerId to: ((self tableNamed: 'GR_CUSTOMER') fieldNamed: 'ID').
	accountId := (aTable newFieldNamed: 'ACCT_ID') beNumeric.
	aTable addForeignKeyFrom: accountId to: ((self tableNamed: 'BANK_ACCT') fieldNamed: 'ID').! !

!GlorpDemoDescriptorSystem methodsFor: 'tables'!
tableForEMAILADDRESS: aTable

	| personId |
	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.
	aTable newFieldNamed: 'USER_NAME'.
	aTable newFieldNamed: 'HOST_NAME'.
	personId := (aTable newFieldNamed: 'PERSON_ID') beNumeric.
	aTable addForeignKeyFrom: personId to: ((self tableNamed: 'PERSON') fieldNamed: 'ID').! !

!GlorpDemoDescriptorSystem methodsFor: 'tables'!
tableForGRADDRESS: aTable 

	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.
	aTable newFieldNamed: 'STREET'.
	aTable newFieldNamed: 'HOUSE_NUM'.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables'!
tableForGRCUSTOMER: aTable 
	(aTable newFieldNamed: 'ID')
		beNumeric;
		bePrimaryKey;
		useSequencingInMemory.
	aTable newFieldNamed: 'NAME'.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables'!
tableForMONEYIMAGINARYTABLE: aTable
	| |
	aTable newFieldNamed: 'CURRENCY'.
	(aTable newFieldNamed: 'AMOUNT') beNumeric.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables'!
tableForPERSON: aTable
	| addrId|
	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.
	aTable newFieldNamed: 'NAME'.
	addrId := (aTable newFieldNamed: 'ADDRESS_ID') beNumeric.
	aTable addForeignKeyFrom: addrId to: ((self tableNamed: 'GR_ADDRESS') fieldNamed: 'ID').! !

!GlorpDemoDescriptorSystem methodsFor: 'tables'!
tableForSTUFF: aTable 
	| |
	(aTable newFieldNamed: 'ID')
		beNumeric;
		bePrimaryKey.
	aTable newFieldNamed: 'THING'.! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/bank'!
descriptorForAddress: aDescriptor

	| table |
	table := self tableNamed: 'GR_ADDRESS'.
	aDescriptor table: table.
	aDescriptor addMapping: (	
		DirectMapping from: #id to: (table fieldNamed: 'ID')).
	aDescriptor addMapping: (	
		DirectMapping from: #street to: (table fieldNamed: 'STREET')).
	aDescriptor addMapping: (	
		DirectMapping from: #number to: (table fieldNamed: 'HOUSE_NUM')).
	^aDescriptor.! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/bank'!
descriptorForBankAccount: aDescriptor 

	| table |
	table := self tableNamed: 'BANK_ACCT'.
	aDescriptor table: table.
	aDescriptor addMapping: (DirectMapping from: #id
				to: (table fieldNamed: 'ID')).
	aDescriptor addMapping: ((ManyToManyMapping new)
				attributeName: #accountHolders;
				referenceClass: Customer;
				mappingCriteria: (PrimaryKeyExpression 
							from: (table fieldNamed: 'ID')
							to: ((self tableNamed: 'CUSTOMER_ACCT_LINK') fieldNamed: 'ACCT_ID'))).
	aDescriptor addMapping: ((EmbeddedValueOneToOneMapping new)
				attributeName: #accountNumber;
				referenceClass: BankAccountNumber).
	^aDescriptor! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/bank'!
descriptorForBankAccountNumber: aDescriptor

	| table |
	table := self tableNamed: 'BANK_ACCT'.
	aDescriptor table: table.
	aDescriptor addMapping: (	
		DirectMapping from: #bankCode to: (table fieldNamed: 'BANK_CODE')).
	aDescriptor addMapping: (	
		DirectMapping from: #branchNumber to: (table fieldNamed: 'BRANCH_NO')).
	aDescriptor addMapping: (	
		DirectMapping from: #accountNumber to: (table fieldNamed: 'ACCT_NO')).
	^aDescriptor.! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/bank'!
descriptorForBankTransaction: aDescriptor
	| table | 
	table := self tableNamed: 'BANK_TRANS'.
	aDescriptor table: table.
	aDescriptor addMapping: (DirectMapping from: #id
				to: (table fieldNamed: 'ID')).
	aDescriptor addMapping: ((OneToOneMapping new)
				attributeName: #owner;
				referenceClass: Customer;
				mappingCriteria: (PrimaryKeyExpression 
							from: (table fieldNamed: 'OWNER_ID')
							to: ((self tableNamed: 'GR_CUSTOMER') fieldNamed: 'ID'))).
	aDescriptor addMapping: ((EmbeddedValueOneToOneMapping new)
				attributeName: #amount;
				referenceClass: GlorpMoney;
				fieldTranslation: ((PrimaryKeyExpression new)
							addSource: (table fieldNamed: 'AMT_AMT')
								target: ((self tableNamed: 'MONEY_IMAGINARY_TABLE') fieldNamed: 'AMOUNT');
							addSource: (table fieldNamed: 'AMT_CURR')
								target: ((self tableNamed: 'MONEY_IMAGINARY_TABLE') fieldNamed: 'CURRENCY');
							yourself)).
	aDescriptor addMapping: ((EmbeddedValueOneToOneMapping new)
				attributeName: #serviceCharge;
				referenceClass: ServiceCharge).
	^aDescriptor! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/bank'!
descriptorForCustomer: aDescriptor

	| table |
	table := self tableNamed: 'GR_CUSTOMER'.
	aDescriptor table: table.
	aDescriptor addMapping: (	
		DirectMapping from: #id to: (table fieldNamed: 'ID')).
	aDescriptor addMapping: (	
		DirectMapping from: #name to: (table fieldNamed: 'NAME')).
	aDescriptor addMapping: (	
		OneToManyMapping new
			attributeName: #transactions;
			referenceClass: BankTransaction;
			mappingCriteria: (PrimaryKeyExpression 
				from: (table fieldNamed: 'ID')
				to: ((self tableNamed: 'BANK_TRANS') fieldNamed: 'OWNER_ID'))).
	aDescriptor addMapping: (	
		ManyToManyMapping new
			attributeName: #accounts;
			referenceClass: BankAccount;
			mappingCriteria: (PrimaryKeyExpression 
				from: (table fieldNamed: 'ID')
				to: ((self tableNamed: 'CUSTOMER_ACCT_LINK') fieldNamed: 'CUSTOMER_ID'))).

	^aDescriptor.! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/bank'!
descriptorForEmailAddress: aDescriptor

	| table |
	table := self tableNamed: 'EMAIL_ADDRESS'.
	aDescriptor table: table.
	aDescriptor addMapping: (	
		DirectMapping from: #id to: (table fieldNamed: 'ID')).
	aDescriptor addMapping: (	
		DirectMapping from: #user to: (table fieldNamed: 'USER_NAME')).
	aDescriptor addMapping: (	
		DirectMapping from: #host to: (table fieldNamed: 'HOST_NAME')).
	^aDescriptor.! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/bank'!
descriptorForGlorpMoney: aDescriptor

	| table |
	table := self tableNamed: 'MONEY_IMAGINARY_TABLE'.
	aDescriptor table: table.
	aDescriptor addMapping: (	
		DirectMapping from: #currency to: (table fieldNamed: 'CURRENCY')).
	aDescriptor addMapping: (	
		DirectMapping from: #amount to: (table fieldNamed: 'AMOUNT')).
	^aDescriptor.! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/bank'!
descriptorForPerson: aDescriptor

	| table |
	table := self tableNamed: 'PERSON'.
	aDescriptor table: table.
	aDescriptor addMapping: (	
		DirectMapping from: #id to: (table fieldNamed: 'ID')).
	aDescriptor addMapping: (	
		DirectMapping from: #name to: (table fieldNamed: 'NAME')).
	aDescriptor addMapping: (	 
		OneToOneMapping new
			attributeName: #address;
			referenceClass: Address;
			mappingCriteria: (PrimaryKeyExpression 
				from: (table fieldNamed: 'ADDRESS_ID')
				to: ((self tableNamed: 'GR_ADDRESS') fieldNamed: 'ID'))).
	aDescriptor addMapping: (
		OneToManyMapping new
			attributeName: #emailAddresses;
			referenceClass: EmailAddress;
			mappingCriteria: (PrimaryKeyExpression 
				from: (table fieldNamed: 'ID')
				to: ((self tableNamed: 'EMAIL_ADDRESS') fieldNamed: 'PERSON_ID'))).
	^aDescriptor.! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/bank'!
descriptorForServiceCharge: aDescriptor 
	
	| table |
	table := self tableNamed: 'BANK_TRANS'.
	aDescriptor table: table.
	aDescriptor addMapping: (DirectMapping from: #description
				to: (table fieldNamed: 'SRVC_DESC')).
	aDescriptor addMapping: ((EmbeddedValueOneToOneMapping new)
				attributeName: #amount;
				referenceClass: GlorpMoney;
				fieldTranslation: ((PrimaryKeyExpression new)
							addSource: (table fieldNamed: 'SRVC_AMT_AMT')
								target: ((self tableNamed: 'MONEY_IMAGINARY_TABLE') fieldNamed: 'AMOUNT');
							addSource: (table fieldNamed: 'SRVC_AMT_CURR')
								target: ((self tableNamed: 'MONEY_IMAGINARY_TABLE') fieldNamed: 'CURRENCY');
							yourself)).
	^aDescriptor! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/airline'!
descriptorForAirline: aDescriptor 
	| table |
	table := self tableNamed: 'AIRLINE'.
	aDescriptor table: (self tableNamed: 'AIRLINE').
	aDescriptor addMapping: (DirectMapping from: #id to: (table fieldNamed: 'ID')).
	aDescriptor addMapping: (DirectMapping from: #name to: (table fieldNamed: 'NAME')).
	^aDescriptor! !

!GlorpDemoDescriptorSystem methodsFor: 'descriptors/airline'!
descriptorForPassenger: aDescriptor 
	| passTable ffTable |
	passTable := self tableNamed: 'PASSENGER'.
	ffTable := self tableNamed: 'FREQUENT_FLYER'.
	aDescriptor table: passTable.
	aDescriptor addTable: ffTable.
	aDescriptor addMultipleTableCriteria: (	
		PrimaryKeyExpression 
			from: (passTable fieldNamed: 'ID')
			to: (ffTable fieldNamed: 'ID')).
	aDescriptor addMapping: (DirectMapping from: #id to: (passTable fieldNamed: 'ID')).
	aDescriptor addMapping: (DirectMapping from: #name to: (passTable fieldNamed: 'NAME')).
	aDescriptor addMapping: (DirectMapping from: #frequentFlyerMiles to: (ffTable fieldNamed: 'POINTS')).
	aDescriptor addMapping: (	
		OneToOneMapping new
			attributeName: #airline;
			referenceClass: Airline;
			mappingCriteria: (PrimaryKeyExpression 
				from: (ffTable fieldNamed: 'AIRLINE_ID')
				to: ((self tableNamed: 'AIRLINE') fieldNamed: 'ID'))).


	^aDescriptor! !

!GlorpDemoDescriptorSystem methodsFor: 'tables/airline'!
tableForAIRLINE: aTable
	| |
	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.
	aTable newFieldNamed: 'NAME'.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables/airline'!
tableForAIRLINEMEAL: aTable
	| |
	(aTable newFieldNamed: 'ID') beNumeric.
	(aTable fieldNamed: 'DESCR').
	(aTable fieldNamed: 'FLIGHT_ID') beNumeric.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables/airline'!
tableForFLIGHT: aTable
	| |
	aTable name: 'FLIGHT'.
	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.
	aTable newFieldNamed: 'FLIGHT_NUM'.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables/airline'!
tableForFLIGHTPASS: aTable
	| |
	aTable name: 'FLIGHT_PASS'.
	(aTable newFieldNamed: 'FLIGHT_ID') beNumeric.
	(aTable fieldNamed: 'PASS_ID') beNumeric.
	(aTable fieldNamed: 'AIRLINE_ID') beNumeric.! !

!GlorpDemoDescriptorSystem methodsFor: 'tables/airline'!
tableForFREQUENTFLYER: aTable 
	| airlineId |
	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.
	(aTable newFieldNamed: 'POINTS') beNumeric.
	airlineId := (aTable newFieldNamed: 'AIRLINE_ID') beNumeric.
	aTable addForeignKeyFrom: airlineId to: ((self tableNamed: 'AIRLINE') fieldNamed: 'ID').! !

!GlorpDemoDescriptorSystem methodsFor: 'tables/airline'!
tableForPASSENGER: aTable
	| |
	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.
	aTable newFieldNamed: 'NAME'.! !


!GlorpDemoDescriptorSystem class methodsFor: 'accessing'!
default
	Default isNil ifTrue: [Default := self new].
	^Default! !


!GlorpDemoTablePopulatorResource methodsFor: 'setup'!
populateStuffTable
 
	login accessor executeSQLString: 'INSERT INTO STUFF VALUES (12,''abc'')'.
	login accessor executeSQLString: 'INSERT INTO STUFF VALUES (13, ''hey nonny nonny'')'.
	login accessor executeSQLString: 'INSERT INTO STUFF VALUES (42, ''yabba dabba doo'')'.
	login accessor executeSQLString: 'INSERT INTO STUFF VALUES (9625, ''and the band played Waltzing Matilda'')'.
	login accessor executeSQLString: 'INSERT INTO STUFF VALUES (113141, ''Smalltalk'')'.! !

!GlorpDemoTablePopulatorResource methodsFor: 'setup'!
setUp
	| system | 
	super setUp.
	login := DatabaseLoginResource current.
	system := GlorpDemoDescriptorSystem new.
	self class needsSetup ifFalse: [^self].
	login accessor dropTables: system allTables.
	system allTables do: 
			[:each | 
			login accessor createTable: each
				ifError: 
					[:ex | 
					Transcript
						show: ex description;
						cr]].
	self populateStuffTable.
	self class needsSetup: false! !


!GlorpDemoTablePopulatorResource class methodsFor: 'setup'!
invalidateSetup
	"GlorpDemoTablePopulatorResource invalidateSetup"

	NeedsSetup := true! !

!GlorpDemoTablePopulatorResource class methodsFor: 'setup'!
needsSetup
	NeedsSetup isNil ifTrue: [NeedsSetup := true].
	^NeedsSetup! !

!GlorpDemoTablePopulatorResource class methodsFor: 'setup'!
needsSetup: aBoolean

	NeedsSetup := aBoolean.! !

!GlorpDemoTablePopulatorResource class methodsFor: 'setup'!
resources

	^Array with: DatabaseLoginResource.! !


!GlorpEncyclopediaDescriptorSystem methodsFor: 'other'!
allClassNames

	^#(Encyclopedia EncyclopediaEntry).! !

!GlorpEncyclopediaDescriptorSystem methodsFor: 'other'!
allTableNames

	^#('ENCYC' 'ENCYC_ENTRY').! !

!GlorpEncyclopediaDescriptorSystem methodsFor: 'descriptors'!
descriptorForEncyclopedia: aDescriptor 
	| table keyMapping valueMapping entryTable |
	table := self tableNamed: 'ENCYC'.
	entryTable := self tableNamed: 'ENCYC_ENTRY'.
	aDescriptor table: table.

	keyMapping := DirectMapping new field: (entryTable fieldNamed: 'NAME').
	valueMapping := OneToManyMapping new
			referenceClass: EncyclopediaEntry;
			mappingCriteria: (PrimaryKeyExpression 
				from: (table fieldNamed: 'ID')
				to: (entryTable fieldNamed: 'OWNER_ID')).
	aDescriptor addMapping: (DictionaryMapping 
		attributeName: #entries
		keyMapping: keyMapping
		valueMapping: valueMapping).

	^aDescriptor! !

!GlorpEncyclopediaDescriptorSystem methodsFor: 'descriptors'!
descriptorForEncyclopediaEntry: aDescriptor 
	| entryTable |
	entryTable := self tableNamed: 'ENCYC_ENTRY'.
	aDescriptor table: entryTable.
	aDescriptor addMapping: (DirectMapping from: #id to: (entryTable fieldNamed: 'ID')).
	aDescriptor addMapping: (DirectMapping from: #name to: (entryTable fieldNamed: 'NAME')).
	aDescriptor addMapping: (DirectMapping from: #text to: (entryTable fieldNamed: 'ENTRY_TEXT')).

	^aDescriptor.! !

!GlorpEncyclopediaDescriptorSystem methodsFor: 'tables'!
tableForENCYC: aTable 

	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.! !

!GlorpEncyclopediaDescriptorSystem methodsFor: 'tables'!
tableForENCYCENTRY: aTable 

	(aTable newFieldNamed: 'ID') beNumeric; bePrimaryKey.
	aTable newFieldNamed: 'NAME'.
	aTable newFieldNamed: 'ENTRY_TEXT'.! !


!GlorpExampleSystem methodsFor: 'initialize'!
initialize

	objects := Dictionary new.! !

!GlorpExampleSystem methodsFor: 'misc'!
lookupObject: aNumber ofClass: aClass ifAbsentPut: absentBlock

	^(objects at: aClass ifAbsentPut: [Dictionary new]) at: aNumber ifAbsentPut: absentBlock.! !

!GlorpExampleSystem methodsFor: 'api'!
objectNumber: aNumber ofClass: aClass

	| symbol instance|
	instance := self lookupObject: aNumber ofClass: aClass ifAbsentPut: [aClass new].
	symbol := ('example', aClass name, 'Number', aNumber printString, ':') asSymbol.
	self perform: symbol with: instance.
	^instance.! !


!GlorpBankExampleSystem methodsFor: 'misc'!
allClasses

	^#(Customer Account BankTransaction) collect: [:each | Smalltalk at: each].! !

!GlorpBankExampleSystem methodsFor: 'examples'!
exampleAddressNumber1: anAddress
	anAddress id: 12.
	anAddress street: 'Paseo Montril'.
	anAddress number: '10185'.! !

!GlorpBankExampleSystem methodsFor: 'examples'!
exampleBankAccountNumber1: anAccount

	anAccount id: 1.
	anAccount accountNumber: (self objectNumber: 1 ofClass: BankAccountNumber).! !

!GlorpBankExampleSystem methodsFor: 'examples'!
exampleBankAccountNumber2: anAccount

	anAccount id: 2.
	anAccount accountNumber: (self objectNumber: 2 ofClass: BankAccountNumber).! !

!GlorpBankExampleSystem methodsFor: 'examples'!
exampleBankAccountNumberNumber1: aBankAccountNumber

	aBankAccountNumber bankCode: '004'.
	aBankAccountNumber branchNumber: '0342'.
	aBankAccountNumber accountNumber: '12345'.! !

!GlorpBankExampleSystem methodsFor: 'examples'!
exampleBankAccountNumberNumber2: aBankAccountNumber

	aBankAccountNumber bankCode: '004'.
	aBankAccountNumber branchNumber: '0342'.
	aBankAccountNumber accountNumber: '01010'.! !

!GlorpBankExampleSystem methodsFor: 'examples'!
exampleBankTransactionNumber1: aTrans

	"Nothing to initialize"! !

!GlorpBankExampleSystem methodsFor: 'examples'!
exampleBankTransactionNumber2: aTrans

	"Nothing to initialize"! !

!GlorpBankExampleSystem methodsFor: 'examples'!
exampleCustomerNumber1: aCustomer

	aCustomer id: 1.
	aCustomer name: 'Fred Flintstone'.
	aCustomer addTransaction: (self objectNumber: 1 ofClass: BankTransaction).
	aCustomer addTransaction: (self objectNumber: 2 ofClass: BankTransaction).
	aCustomer addAccount: (self objectNumber: 1 ofClass: BankAccount).
	aCustomer addAccount: (self objectNumber: 2 ofClass: BankAccount).! !

!GlorpBankExampleSystem methodsFor: 'examples'!
exampleEmailAddressNumber1: anEmailAddress 
	anEmailAddress id: 2.
	anEmailAddress user: 'foo'.
	anEmailAddress host: 'bar.com'! !

!GlorpBankExampleSystem methodsFor: 'examples'!
examplePersonNumber1: aPerson 
	aPerson id: 1.
	aPerson name: 'Barney Rubble'.
	aPerson address: (self objectNumber: 1 ofClass: Address).	
	aPerson emailAddress: (self objectNumber: 1 ofClass: EmailAddress).! !


!GlorpBankExampleSystem class methodsFor: 'instance creation'!
new

	^super new initialize! !


!GlorpExpression methodsFor: 'preparing'!
additionalExpressions

	^#().! !

!GlorpExpression methodsFor: 'preparing'!
additionalExpressionsIn: aQuery 
	"Return the collection of additional expressions (representing joins) that this expression tree requires"

	^self 
		inject: OrderedCollection new 
		into: [:sum :each | 
			sum addAll: each additionalExpressions.
			sum].! !

!GlorpExpression methodsFor: 'preparing'!
allTables

	^self inject: Set new into: [:sum :each | 
		sum addAll: each tables. sum].! !

!GlorpExpression methodsFor: 'preparing'!
allTablesToPrint

	^self inject: Set new into: [:sum :each | 
		sum addAll: each tablesToPrint. sum].! !

!GlorpExpression methodsFor: 'preparing'!
asExpressionJoiningSource: source toTarget: target
	"Create a general expression which represents this relationship where the values of the targets (which are normally parameters) are supplied out of the context provided by 'target' and the source fields are referencing things out of the context of source. Produces something suitable for ANDing into an expression when doing a join
   Example: If we had CUSTOMER.ADDRESS_ID = ADDRESS.ID as a parameter, and we want to AND this into an expression [:customer | customer address street = 'Main'] then we have customer as a base, and we get 
	(customer.ADDRESS.STREET = 'Main') AND (customer.CUSTOMER.ADDRESS_ID = customer.ADDRESS.ID)
	The primary key expression for the relationship has been translated into field references into the customer and address tables in a particular context."

	self subclassResponsibility.! !

!GlorpExpression methodsFor: 'preparing'!
assignTableAliasesStartingAt: anInteger

	^anInteger.! !

!GlorpExpression methodsFor: 'preparing'!
prepareIn: aQuery 
	| newExpression |
	newExpression := self.
	(self additionalExpressionsIn: aQuery) 
		do: [:each | newExpression := newExpression exAnd: each].
	^newExpression! !

!GlorpExpression methodsFor: 'preparing'!
tables

	^#().! !

!GlorpExpression methodsFor: 'preparing'!
tablesToPrint

	^#().! !

!GlorpExpression methodsFor: 'api'!
asGlorpExpression

	^self.! !

!GlorpExpression methodsFor: 'api'!
base

	self subclassResponsibility.! !

!GlorpExpression methodsFor: 'api'!
equals: anExpression

	^RelationExpression named: #= basedOn: self withArguments: (Array with: anExpression).! !

!GlorpExpression methodsFor: 'api'!
exAnd: anExpression

	anExpression isNil ifTrue: [^self].
	^RelationExpression named: #AND basedOn: self withArguments: (Array with: anExpression).! !

!GlorpExpression methodsFor: 'api'!
get: aSymbol withArguments: anArray

	self subclassResponsibility.! !

!GlorpExpression methodsFor: 'converting'!
asGlorpExpressionForDescriptor: aDescriptor

	self ultimateBaseExpression descriptor: aDescriptor.! !

!GlorpExpression methodsFor: 'accessing'!
hasDescriptor

	^false.! !

!GlorpExpression methodsFor: 'accessing'!
printsTable

	^false.! !

!GlorpExpression methodsFor: 'accessing'!
valueIn: aDictionary
	"Return the value associated with this expression given the parameters in aDictionary. Only meaningful for ParameterExpressions"

	^self.! !

!GlorpExpression methodsFor: 'initialize'!
initialize! !

!GlorpExpression methodsFor: 'iterating'!
collect: aBlock

	| newCollection |
	newCollection := OrderedCollection new.
	self do: [:each | newCollection add: (aBlock value: each)].
	^newCollection.! !

!GlorpExpression methodsFor: 'iterating'!
do: aBlock
	"Iterate over the expression tree"

	self do: aBlock skipping: IdentitySet new.! !

!GlorpExpression methodsFor: 'iterating'!
do: aBlock skipping: aSet
	"Iterate over the expression tree. Keep track of who has already been visited, so we don't get trapped in cycles or visit nodes twice."

	(aSet includes: self) ifTrue: [^self].
	aSet add: self.
	aBlock value: self.! !

!GlorpExpression methodsFor: 'iterating'!
inject: anObject into: aBlock

	| sum |
	sum := anObject.
	self do: [:each | sum := aBlock value: sum value: each].
	^sum! !

!GlorpExpression methodsFor: 'printing'!
className

	^self class name.! !

!GlorpExpression methodsFor: 'printing'!
displayString

	| stream |
	stream := String new writeStream.
	self printOnlySelfOn: stream.
	^stream contents.! !

!GlorpExpression methodsFor: 'printing'!
printOn: aStream 
	self printTreeOn: aStream! !

!GlorpExpression methodsFor: 'printing'!
printOnlySelfOn: aStream
	self subclassResponsibility.! !

!GlorpExpression methodsFor: 'printing'!
printTreeOn: aStream

	self subclassResponsibility.! !

!GlorpExpression methodsFor: 'inspecting'!
inspectorHierarchies
	| hierarchy |
	hierarchy := ((Smalltalk at: #Tools ifAbsent: [^#()])
		at: #Trippy ifAbsent: [^#()])
		at: #Hierarchy ifAbsent: [^#()].
	^Array with: (hierarchy
			id: #expression
			label: 'Expression Tree'
			parentBlock: [:each | nil]
			childrenBlock: [:each | each inspectorChildren])! !


!ConstantExpression methodsFor: 'accessing'!
value
	^value! !

!ConstantExpression methodsFor: 'accessing'!
value: anObject
	value := anObject! !

!ConstantExpression methodsFor: 'accessing'!
valueIn: aDictionary
	^value! !

!ConstantExpression methodsFor: 'preparing'!
asExpressionJoiningSource: source toTarget: target
	"Create a general expression which represents this relationship where the values of the targets (which are normally parameters) are supplied out of the context provided by 'target' and the source fields are referencing things out of the context of source. Produces something suitable for ANDing into an expression when doing a join
   Example: If we had CUSTOMER.ADDRESS_ID = ADDRESS.ID as a parameter, and we want to AND this into an expression [:customer | customer address street = 'Main'] then we have customer as a base, and we get 
	(customer.ADDRESS.STREET = 'Main') AND (customer.CUSTOMER.ADDRESS_ID = customer.ADDRESS.ID)
	The primary key expression for the relationship has been translated into field references into the customer and address tables in a particular context."

	^self.! !

!ConstantExpression methodsFor: 'printing'!
printOnlySelfOn: aStream

	aStream print: value! !

!ConstantExpression methodsFor: 'printing'!
printTreeOn: aStream 
	aStream print: value! !

!ConstantExpression methodsFor: 'printing SQL'!
printSQLOn: aStream withParameters: aDictionary
	self value glorpPrintSQLOn: aStream.! !


!FieldExpression methodsFor: 'initializing'!
field: aField base: anObjectExpression

	field := aField.
	base := anObjectExpression.! !

!FieldExpression methodsFor: 'printing SQL'!
printSQLOn: aStream withParameters: aDictionary
	self field printSQLOn: aStream withParameters:aDictionary.! !

!FieldExpression methodsFor: 'preparing'!
asExpressionJoiningSource: source toTarget: target
	"Create a general expression which represents this relationship where the values of the targets (which are normally parameters) are supplied out of the context provided by 'target' and the source fields are referencing things out of the context of source. Produces something suitable for ANDing into an expression when doing a join
   Example: If we had CUSTOMER.ADDRESS_ID = ADDRESS.ID as a parameter, and we want to AND this into an expression [:customer | customer address street = 'Main'] then we have customer as a base, and we get 
	(customer.ADDRESS.STREET = 'Main') AND (customer.CUSTOMER.ADDRESS_ID = customer.ADDRESS.ID)
	The primary key expression for the relationship has been translated into field references into the customer and address tables in a particular context."
	| newTarget |
	newTarget := (target tables includes: field table) 
		ifTrue: [target]
		ifFalse: [base asExpressionJoiningSource: source toTarget: target].
	^newTarget getField: field.! !

!FieldExpression methodsFor: 'preparing'!
tables

	^base tables.! !

!FieldExpression methodsFor: 'preparing'!
tablesToPrint

	^#().! !

!FieldExpression methodsFor: 'navigating'!
ultimateBaseExpression
	^base ultimateBaseExpression.! !

!FieldExpression methodsFor: 'accessing'!
base

	^base.! !

!FieldExpression methodsFor: 'accessing'!
field
	^base translateField: field! !

!FieldExpression methodsFor: 'iterating'!
do: aBlock skipping: aSet
	"Iterate over the expression tree"

	(aSet includes: self) ifTrue: [^self].
	aSet add: self.
	base do: aBlock skipping: aSet.
	aBlock value: self.! !

!FieldExpression methodsFor: 'printing'!
printOnlySelfOn: aStream 
	base printsTable
		ifTrue: [field printUnqualifiedSQLOn: aStream withParameters: #()]
		ifFalse: [field printSQLOn: aStream withParameters: #()]! !

!FieldExpression methodsFor: 'printing'!
printTreeOn: aStream 
	base printOn: aStream.
	aStream nextPut: $..
	base printsTable 
		ifTrue: [field printUnqualifiedSQLOn: aStream withParameters: #()]
		ifFalse: [field printSQLOn: aStream withParameters: #()]! !

!FieldExpression methodsFor: 'api'!
get: aSymbol withArguments: anArray

	^anArray isEmpty 
		ifTrue: [self error: 'Field expressions do not have attributes']
		ifFalse: [RelationExpression named: aSymbol basedOn: self withArguments: anArray].! !


!GlorpExpression class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!ConstantExpression class methodsFor: 'instance creation'!
for: anObject

	^self new value: anObject.! !


!FieldExpression class methodsFor: 'instance creation'!
forField: aField basedOn: anObjectExpression

	^self new field: aField base: anObjectExpression; yourself! !


!GlorpHelper class methodsFor: 'helpers'!
do: aBlock for: aCollection separatedBy: separatorBlock

	| array |
	array := aCollection asArray.
	1 to: array size do: [:i |
		| each |
		each := array at: i.
		aBlock value: each.
		i = array size ifFalse: [
			separatorBlock value]].! !

!GlorpHelper class methodsFor: 'helpers'!
print: printBlock on: stream for: aCollection separatedBy: separatorString

	| array |
	array := aCollection asArray.
	1 to: array size do: [:index |
		stream nextPutAll: (printBlock value: (array at: index)).
		index == array size ifFalse: [
			stream nextPutAll: separatorString]].
	^stream contents.! !

!GlorpHelper class methodsFor: 'helpers'!
separate: aCollection by: aOneArgumentBlock

	^aCollection inject: Dictionary new into: [:dict :each |
		| val |
		val := aOneArgumentBlock value: each.
		(dict at: val ifAbsentPut: [OrderedCollection new]) add: each].! !


!GlorpMoney methodsFor: 'accessing'!
amount
	^amount! !

!GlorpMoney methodsFor: 'accessing'!
amount: anObject
	amount := anObject! !

!GlorpMoney methodsFor: 'accessing'!
currency
	^currency! !

!GlorpMoney methodsFor: 'accessing'!
currency: anObject
	currency := anObject! !


!GlorpMoney class methodsFor: 'instance creation'!
currency: aSymbol amount: aNumber

	^self new
		currency: aSymbol;
		amount: aNumber.! !

!GlorpMoney class methodsFor: 'instance creation'!
defaultCurrency

	^#CDN.! !

!GlorpMoney class methodsFor: 'instance creation'!
forAmount: anAmount

	^self currency: self defaultCurrency amount: anAmount.! !


!Login methodsFor: 'accessing'!
connectString
	^connectString! !

!Login methodsFor: 'accessing'!
connectString: aString 
	connectString := aString! !

!Login methodsFor: 'accessing'!
database
	^database! !

!Login methodsFor: 'accessing'!
database: aSymbol 
	database := aSymbol! !

!Login methodsFor: 'accessing'!
password
	^password! !

!Login methodsFor: 'accessing'!
password: aString 
	password := aString! !

!Login methodsFor: 'accessing'!
username
	^username! !

!Login methodsFor: 'accessing'!
username: aString 
	username := aString! !

!Login methodsFor: 'printing'!
printOn: aStream

	aStream nextPutAll: 'a Login('.
	database printOn: aStream.
	aStream nextPutAll: ', '.
	username printOn: aStream.
	aStream nextPutAll: ', '.
	password printOn: aStream.
	aStream nextPutAll: ', '.
	connectString printOn: aStream.
	aStream nextPutAll: ')'.! !


!Mapping methodsFor: 'accessing'!
allTables

	self subclassResponsibility.! !

!Mapping methodsFor: 'accessing'!
attributeName
	"Private - Answer the value of the receiver's ''attributeName'' instance variable."

	^attributeName! !

!Mapping methodsFor: 'accessing'!
attributeName: anObject
	"Private - Set the value of the receiver's ''attributeName'' instance variable to the argument, anObject."

	attributeName := anObject.
	self initializeAccessor.! !

!Mapping methodsFor: 'accessing'!
descriptor
	"Private - Answer the value of the receiver's ''descriptor'' instance variable."

	^descriptor! !

!Mapping methodsFor: 'accessing'!
descriptor: anObject
	"Private - Set the value of the receiver's ''descriptor'' instance variable to the argument, anObject."

	descriptor := anObject! !

!Mapping methodsFor: 'accessing'!
fieldsForSelectStatement
	"Return a collection of fields that this mapping will read from a row"

	^self mappedFields! !

!Mapping methodsFor: 'accessing'!
initializeAccessor

	attributeAccessor := AttributeAccessor newForAttributeNamed: attributeName.! !

!Mapping methodsFor: 'accessing'!
mappedFields
	"Return a collection of fields that this mapping will write into any of the containing object's rows"

	^Array with: self field.! !

!Mapping methodsFor: 'accessing'!
session
	
	^self descriptor session.! !

!Mapping methodsFor: 'accessing'!
system
	
	^self descriptor system.! !

!Mapping methodsFor: 'public'!
getValueFrom: anObject

	^attributeAccessor getValueFrom: anObject.! !

!Mapping methodsFor: 'public'!
printOn: aStream

	super printOn: aStream.
	aStream 
		nextPutAll: '(';
		nextPutAll: (attributeName isNil ifTrue: [''] ifFalse: [attributeName]) ;
		nextPutAll: ')'.! !

!Mapping methodsFor: 'public'!
setValueIn: anObject to: aValue

	attributeAccessor setValueIn: anObject to: aValue.! !

!Mapping methodsFor: 'testing'!
controlsTables
	"Return true if this type of mapping 'owns' the tables it's associated with, and expression nodes using this mapping should alias those tables where necessary"

	self subclassResponsibility! !

!Mapping methodsFor: 'testing'!
includesSubFieldsInSelectStatement
	^false! !

!Mapping methodsFor: 'testing'!
isIndependentRelationship
	"True when the mapping associates different tables."

	^self subclassResponsibility! !

!Mapping methodsFor: 'testing'!
isOneToOne

	^false! !

!Mapping methodsFor: 'testing'!
isRelationship
	"True when the mapping associates different classes."

	^self subclassResponsibility! !

!Mapping methodsFor: 'preparing'!
joinExpressionFor: anExpression

	^nil.! !

!Mapping methodsFor: 'mapping'!
expressionFor: anObject
	"Return our expression using the object's values. e.g. if this was a direct mapping from id->ID and the object had id: 3, then return TABLE.ID=3"

	self subclassResponsibility.! !

!Mapping methodsFor: 'mapping'!
mapFromObject: anObject intoRowsIn: aRowMap

	self subclassResponsibility.! !

!Mapping methodsFor: 'mapping'!
mapFromRow: valueCollection intoObject: anObject inElementBuilder: anObject1 
	self subclassResponsibility! !

!Mapping methodsFor: 'mapping'!
referencedIndependentObjectsFrom: anObject

	self subclassResponsibility.! !

!Mapping methodsFor: 'mapping'!
trace: aTracing context: anExpression

	self subclassResponsibility.! !

!Mapping methodsFor: 'initialize/release'!
initialize! !


!ConditionalMapping methodsFor: 'mapping'!
applicableMappingFor: anArray in: anElementBuilder 
	| rowValue |
	rowValue := anElementBuilder valueOf: conditionalField in: anArray.
	cases do: [:each | (self descriptor system perform: each key with: rowValue)
			ifTrue: [^each value]].
	^otherwiseCase! !

!ConditionalMapping methodsFor: 'mapping'!
mapFromRow: valueCollection intoObject: anObject inElementBuilder: anElementBuilder 
	(self applicableMappingFor: valueCollection in: anElementBuilder)
		mapFromRow: valueCollection
		intoObject: anObject
		inElementBuilder: anElementBuilder! !

!ConditionalMapping methodsFor: 'accessing'!
conditionalField: aField

	conditionalField := aField.! !

!ConditionalMapping methodsFor: 'accessing'!
conditionalMethod: aSymbol

	conditionalMethod := aSymbol.! !

!ConditionalMapping methodsFor: 'accessing'!
descriptor: aDescriptor

	super descriptor: aDescriptor.
	cases do: [:each | each value descriptor: aDescriptor].
	otherwiseCase descriptor: aDescriptor.! !

!ConditionalMapping methodsFor: 'accessing'!
mappedFields

	| all |
	all := OrderedCollection new.
	all add: conditionalField.
	cases do: [:each |
		all addAll: each value mappedFields].
	^all.! !

!ConditionalMapping methodsFor: 'conditions'!
if: conditionSelector then: aMapping

	cases add: (Association key: conditionSelector value: aMapping).! !

!ConditionalMapping methodsFor: 'conditions'!
otherwise: aMapping

	otherwiseCase := aMapping.! !

!ConditionalMapping methodsFor: 'conditions'!
trace: aTracing context: anExpression
	"To make a join, we need to look at all of our possible cases"

	cases do: [:each |
		each value trace: aTracing context: anExpression].! !

!ConditionalMapping methodsFor: 'testing'!
controlsTables
	self error: 'What should we do here?'! !

!ConditionalMapping methodsFor: 'testing'!
isRelationship

	self error: 'What should we do here?'.! !

!ConditionalMapping methodsFor: 'initialize/release'!
initialize
	
	super initialize.
	cases := OrderedCollection new.! !


!ConstantMapping methodsFor: 'accessing'!
constantValue
	^constantValue.! !

!ConstantMapping methodsFor: 'accessing'!
constantValue: anObject

	constantValue := anObject.! !

!ConstantMapping methodsFor: 'accessing'!
constantValueIn: aSession

	^valueIsSession
		ifTrue: [aSession]
		ifFalse: [constantValue].! !

!ConstantMapping methodsFor: 'accessing'!
constantValueIsSession

	valueIsSession := true.! !

!ConstantMapping methodsFor: 'accessing'!
initializeAccessor

	attributeName == nil 
		ifFalse: [super initializeAccessor].! !

!ConstantMapping methodsFor: 'accessing'!
mappedFields
	"Return a collection of fields that this mapping will write into any of the containing object's rows"

	^#().! !

!ConstantMapping methodsFor: 'mapping'!
mapFromObject: anObject intoRowsIn: aRowMap! !

!ConstantMapping methodsFor: 'mapping'!
mapFromRow: valueCollection intoObject: anObject inElementBuilder: anElementBuilder 
	| value |
	value := anElementBuilder isNil
				ifTrue: [constantValue]
				ifFalse: [self constantValueIn: anElementBuilder session].
	self setValueIn: anObject to: value! !

!ConstantMapping methodsFor: 'mapping'!
referencedIndependentObjectsFrom: anObject

	^#().! !

!ConstantMapping methodsFor: 'mapping'!
trace: aTracing context: anExpression

	^self.! !

!ConstantMapping methodsFor: 'testing'!
controlsTables
	"Return true if this type of method 'owns' the tables it's associated with, and expression nodes using this mapping should alias those tables where necessary"

	^false! !

!ConstantMapping methodsFor: 'api'!
getValueFrom: anObject

	^constantValue.! !

!ConstantMapping methodsFor: 'initialize/release'!
initialize

	super initialize.
	valueIsSession := false.! !


!DictionaryMapping methodsFor: 'accessing'!
keyMapping: aMapping 
	keyMapping := aMapping.! !

!DictionaryMapping methodsFor: 'accessing'!
valueMapping: aMapping 
	valueMapping := aMapping.! !


!DirectMapping methodsFor: 'accessing'!
field
	"Private - Answer the value of the receiver's ''field'' instance variable."

	^field! !

!DirectMapping methodsFor: 'accessing'!
field: anObject
	"Private - Set the value of the receiver's ''field'' instance variable to the argument, anObject."

	field := anObject! !

!DirectMapping methodsFor: 'testing'!
controlsTables
	"Return true if this type of method 'owns' the tables it's associated with, and expression nodes using this mapping should alias those tables where necessary"

	^false! !

!DirectMapping methodsFor: 'testing'!
isIndependentRelationship
	"True when the mapping associates different tables."

	^false! !

!DirectMapping methodsFor: 'testing'!
isRelationship
	"True when the mapping associates different classes."

	^false! !

!DirectMapping methodsFor: 'enumerating'!
referencedIndependentObjectsFrom: anObject

	^#().! !

!DirectMapping methodsFor: 'mapping'!
convertValueToDatabaseForm: aValue

	aValue isSymbol ifTrue: [^aValue asString].
	^aValue.! !

!DirectMapping methodsFor: 'mapping'!
expressionFor: anObject
	"Return our expression using the object's values. e.g. if this was a direct mapping from id->ID and the object had id: 3, then return TABLE.ID=3"

	| value |
	value := attributeAccessor getValueFrom: anObject. 
	^(BaseExpression new getField: field) get: #= withArguments: (Array with: value).! !

!DirectMapping methodsFor: 'mapping'!
mapFromObject: anObject intoRowsIn: aRowMap

	| value row |
	value := attributeAccessor getValueFrom: anObject.
	row := aRowMap findOrAddRowForTable: self field table withKey: anObject.
	row at: field put: value.! !

!DirectMapping methodsFor: 'mapping'!
mapFromRow: anArray intoObject: anObject inElementBuilder: anElementBuilder 
	self setValueIn: anObject to: (anElementBuilder valueOf: field in: anArray).! !

!DirectMapping methodsFor: 'mapping'!
trace: aTracing 
	^self.! !

!DirectMapping methodsFor: 'mapping'!
trace: aTracing context: anExpression
	^self.! !


!Mapping class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!DictionaryMapping class methodsFor: 'instance creation'!
attributeName: aSymbol keyMapping: keyMapping valueMapping: valueMapping

	^self new
		attributeName: aSymbol;
		keyMapping: keyMapping;
		valueMapping: valueMapping.! !


!DirectMapping class methodsFor: 'instance creation'!
from: attributeName to: field

	^self new
		attributeName: attributeName;
		field: field.! !


!MessageArchiver class methodsFor: 'instance creation'!
receiver: aMessageCollector message: aMessage

	^self new
		receiver: aMessageCollector
		message: aMessage.! !


!ObjectExpression methodsFor: 'api'!
get: aSymbol 
	"Return the mapping expression corresponding to the named attribute"

	| reallyASymbol |
	reallyASymbol := aSymbol asSymbol.
	^mappingExpressions at: reallyASymbol
		ifAbsentPut: [MappingExpression named: reallyASymbol basedOn: self]! !

!ObjectExpression methodsFor: 'api'!
get: aSymbol withArguments: anArray
	"Return the mapping expression corresponding to the named attribute"

	^anArray isEmpty 
		ifTrue: [self get: aSymbol]
		ifFalse: [RelationExpression named: aSymbol basedOn: self withArguments: anArray].! !

!ObjectExpression methodsFor: 'api'!
getField: aField
	
	^mappingExpressions at: aField ifAbsentPut: [self newFieldExpressionFor: aField].! !

!ObjectExpression methodsFor: 'api'!
getTable: aTable
	aTable isString ifTrue: [self error: 'This method takes a table object'].
	^mappingExpressions at: aTable ifAbsentPut: [TableExpression forTable: aTable basedOn: self].! !

!ObjectExpression methodsFor: 'fields'!
aliasTable: aDatabaseTable to: aString 
	| newTable |
	newTable := aDatabaseTable copy.
	newTable name: aString.
	newTable parent: aDatabaseTable.
	self tableAliases at: aDatabaseTable put: newTable! !

!ObjectExpression methodsFor: 'fields'!
aliasedTableFor: aDatabaseTable 
	tableAliases isNil ifTrue: [^aDatabaseTable].
	^tableAliases at: aDatabaseTable.! !

!ObjectExpression methodsFor: 'fields'!
controlsTables

	self subclassResponsibility.! !

!ObjectExpression methodsFor: 'fields'!
newFieldExpressionFor: aField

	^FieldExpression forField: aField basedOn: self! !

!ObjectExpression methodsFor: 'fields'!
translateField: aDatabaseField 
	| newTable |
	newTable := self aliasedTableFor: aDatabaseField table.
	newTable == aDatabaseField table ifTrue: [^aDatabaseField].
	^self fieldAliases 
		at: aDatabaseField
		ifAbsentPut: 
			[| newField |
			newField := aDatabaseField copy.
			newField table: newTable]! !

!ObjectExpression methodsFor: 'fields'!
translateFields: anOrderedCollection 
	^anOrderedCollection collect: [:each | self translateField: each]! !

!ObjectExpression methodsFor: 'accessing'!
fieldAliases

	fieldAliases isNil ifTrue: [fieldAliases := IdentityDictionary new].
	^fieldAliases.! !

!ObjectExpression methodsFor: 'accessing'!
requiresDistinct
	^requiresDistinct! !

!ObjectExpression methodsFor: 'accessing'!
requiresDistinct: aBoolean 
	requiresDistinct := aBoolean! !

!ObjectExpression methodsFor: 'accessing'!
tableAliases
	tableAliases isNil ifTrue: [
		tableAliases := IdentityDictionary new: 3].
	^tableAliases! !

!ObjectExpression methodsFor: 'initialize'!
initialize

	super initialize.
	mappingExpressions := IdentityDictionary new.
	requiresDistinct := false.! !

!ObjectExpression methodsFor: 'tests'!
hasTableAliases
	^tableAliases notNil! !

!ObjectExpression methodsFor: 'preparing'!
assignTableAliasesStartingAt: anInteger 
	| tableNumber |
	self controlsTables ifFalse: [^anInteger].
	tableNumber := anInteger.
	self tables do: [:each |
		self aliasTable: each to: 't', tableNumber printString.
		tableNumber := tableNumber + 1].
	^tableNumber! !

!ObjectExpression methodsFor: 'printing'!
printTableAliasesOn: aStream 
	self hasTableAliases 
		ifTrue: 
			[aStream nextPutAll: ' '.
			tableAliases keysAndValuesDo: [:eachKey :eachValue | 
				aStream nextPutAll: eachKey name, '->', eachValue name , ' ']]! !

!ObjectExpression methodsFor: 'private/accessing'!
removeMappingExpression: anExpression
	"Private. Normally you would never do this, but in the case of an anySatisfy: or allSatisfy: we want to have each of them as distinct joins, so we will remove the entry from the mappingExpression of the base, making sure that relationship will not be used for anything else. Since any/allSatisfy: is the only valid use of a collection relationship, we don't have to worry about whether it was used for something else earlier."

	mappingExpressions removeKey: anExpression name.! !


!BaseExpression methodsFor: 'api'!
base

	^nil.! !

!BaseExpression methodsFor: 'api'!
getParameter: aDatabaseField 
	^ParameterExpression forField: aDatabaseField basedOn: self.! !

!BaseExpression methodsFor: 'accessing'!
descriptor

	^descriptor! !

!BaseExpression methodsFor: 'accessing'!
descriptor: aDescriptor
	descriptor := aDescriptor! !

!BaseExpression methodsFor: 'accessing'!
hasDescriptor

	^descriptor notNil.! !

!BaseExpression methodsFor: 'accessing'!
system
	^descriptor system.! !

!BaseExpression methodsFor: 'accessing'!
targetDescriptor
	self halt! !

!BaseExpression methodsFor: 'navigating'!
ultimateBaseExpression

	^self! !

!BaseExpression methodsFor: 'printing'!
className

	^'Base'.! !

!BaseExpression methodsFor: 'printing'!
printOn: aStream 
	aStream
		nextPutAll: self className;
		nextPut: $(.
	self printTreeOn: aStream.
	aStream nextPut: $)! !

!BaseExpression methodsFor: 'printing'!
printOnlySelfOn: aStream 
	descriptor isNil ifTrue: [aStream nextPutAll: 'Empty Base'. ^self].
	aStream print: descriptor describedClass.
	self printTableAliasesOn: aStream! !

!BaseExpression methodsFor: 'printing'!
printTreeOn: aStream 
	aStream 
		print: (descriptor isNil ifTrue: [nil] ifFalse: [descriptor describedClass])! !

!BaseExpression methodsFor: 'preparing'!
additionalExpressions

	^descriptor multipleTableCriteria 
		collect: [:each | each asExpressionJoiningSource: self toTarget: self]! !

!BaseExpression methodsFor: 'preparing'!
asExpressionJoiningSource: source toTarget: target
	"Create a general expression which represents this relationship where the values of the targets (which are normally parameters) are supplied out of the context provided by 'target' and the source fields are referencing things out of the context of source. Produces something suitable for ANDing into an expression when doing a join
   Example: If we had CUSTOMER.ADDRESS_ID = ADDRESS.ID as a parameter, and we want to AND this into an expression [:customer | customer address street = 'Main'] then we have customer as a base, and we get 
	(customer.ADDRESS.STREET = 'Main') AND (customer.CUSTOMER.ADDRESS_ID = customer.ADDRESS.ID)
	The primary key expression for the relationship has been translated into field references into the customer and address tables in a particular context."

	^source.! !

!BaseExpression methodsFor: 'preparing'!
controlsTables

	^true.! !

!BaseExpression methodsFor: 'preparing'!
fieldsForSelectStatement
	^descriptor mappedFields.! !

!BaseExpression methodsFor: 'preparing'!
rebuildOn: aBaseExpression 
	^aBaseExpression.! !

!BaseExpression methodsFor: 'preparing'!
tables

	^descriptor tables.! !

!BaseExpression methodsFor: 'preparing'!
tablesToPrint
	"We derive the base's tables from the fields that are being selected, so don't add them here."
	^#().! !


!MappingExpression methodsFor: 'api'!
base

	^base.! !

!MappingExpression methodsFor: 'api'!
get: aSymbol withArguments: anArray

	^aSymbol == #anySatisfy:
		ifTrue: [
			self base requiresDistinct: true.
			self base removeMappingExpression: self.
			CollectionExpression 
				named: aSymbol 
				basedOn: self 
				withArguments: anArray]
		ifFalse: [super get: aSymbol withArguments: anArray].! !

!MappingExpression methodsFor: 'accessing'!
descriptor
	^self system descriptorFor: self mapping referenceClass.! !

!MappingExpression methodsFor: 'accessing'!
field

	^base translateField: self mapping field.! !

!MappingExpression methodsFor: 'accessing'!
hasDescriptor
	"Does the object that we describe have its own descriptor"
	^self mapping isRelationship! !

!MappingExpression methodsFor: 'accessing'!
name

	^name.! !

!MappingExpression methodsFor: 'accessing'!
sourceDescriptor
	^base descriptor.! !

!MappingExpression methodsFor: 'accessing'!
system
	^base system.! !

!MappingExpression methodsFor: 'accessing'!
tables

	| set |
	self controlsTables ifFalse: [^#()].
	set := self descriptor tables asSet.
	^set.! !

!MappingExpression methodsFor: 'printing SQL'!
printSQLOn: aStream withParameters: aDictionary 
	self field printSQLOn: aStream withParameters:aDictionary.! !

!MappingExpression methodsFor: 'navigating'!
ultimateBaseExpression

	^base ultimateBaseExpression.! !

!MappingExpression methodsFor: 'preparing'!
additionalExpressions
	| exp |
	exp := self mapping joinExpressionFor: self.
	^exp isNil ifTrue: [#()] ifFalse: [Array with: exp].! !

!MappingExpression methodsFor: 'preparing'!
asExpressionJoiningSource: source toTarget: target
	"Create a general expression which represents this relationship where the values of the targets (which are normally parameters) are supplied out of the context provided by 'target' and the source fields are referencing things out of the context of source. Produces something suitable for ANDing into an expression when doing a join
   Example: If we had CUSTOMER.ADDRESS_ID = ADDRESS.ID as a parameter, and we want to AND this into an expression [:customer | customer address street = 'Main'] then we have customer as a base, and we get 
	(customer.ADDRESS.STREET = 'Main') AND (customer.CUSTOMER.ADDRESS_ID = customer.ADDRESS.ID)
	The primary key expression for the relationship has been translated into field references into the customer and address tables in a particular context."

	| newBase |
	newBase := base asExpressionJoiningSource: source toTarget: target.
	^self class named: name basedOn: newBase.! !

!MappingExpression methodsFor: 'preparing'!
fieldsForSelectStatement
	
	^self mapping fieldsForSelectStatement.! !

!MappingExpression methodsFor: 'preparing'!
rebuildOn: aBaseExpression 
	^(base rebuildOn: aBaseExpression) get: name.! !

!MappingExpression methodsFor: 'preparing'!
tablesToPrint

	self hasDescriptor ifFalse: [^#()].
	^self tables collect: [:each |
		self aliasedTableFor: each].! !

!MappingExpression methodsFor: 'preparing'!
translateFields: anOrderedCollection 
	"Ugh. Unify these mechnisms"
	^super translateFields: (self mapping translateFields: anOrderedCollection).! !

!MappingExpression methodsFor: 'iterating'!
do: aBlock skipping: aSet
	"Iterate over the expression tree"

	(aSet includes: self) ifTrue: [^self].
	aSet add: self.
	base do: aBlock skipping: aSet.
	aBlock value: self.! !

!MappingExpression methodsFor: 'printing'!
printOnlySelfOn: aStream

	aStream nextPutAll: name.
	self printTableAliasesOn: aStream.! !

!MappingExpression methodsFor: 'printing'!
printTreeOn: aStream 
	aStream
		print: base;
		nextPut: $.;
		nextPutAll: name! !

!MappingExpression methodsFor: 'fields'!
aliasedTableFor: aDatabaseTable 
	^self controlsTables 
		ifTrue: [super aliasedTableFor: aDatabaseTable]
		ifFalse: [base aliasedTableFor: aDatabaseTable]! !

!MappingExpression methodsFor: 'fields'!
controlsTables
	| mapping |
	mapping := self mapping.
	mapping isNil ifTrue: [^false].
	^mapping controlsTables! !

!MappingExpression methodsFor: 'internal'!
mapping
	| descriptor |
	descriptor := self sourceDescriptor.
	descriptor isNil ifTrue: [^nil].
	^descriptor mappingForAttributeNamed: name.! !

!MappingExpression methodsFor: 'private/initialization'!
named: aSymbol basedOn: anExpression

	name := aSymbol.
	base := anExpression.! !


!MappingExpression class methodsFor: 'instance creation'!
named: aSymbol basedOn: anExpression

	^self new
		named: aSymbol
		basedOn: anExpression.! !


!ObjectTransaction methodsFor: 'begin/commit/abort'!
abort
	undoMap keysAndValuesDo: [:original :copy | self restoreStateOf: original toThatOf: copy]! !

!ObjectTransaction methodsFor: 'begin/commit/abort'!
begin
	self initializeUndoMap! !

!ObjectTransaction methodsFor: 'begin/commit/abort'!
commit
	self initializeUndoMap! !

!ObjectTransaction methodsFor: 'initializing'!
initialize
	self initializeUndoMap! !

!ObjectTransaction methodsFor: 'initializing'!
initializeUndoMap

	undoMap := IdentityDictionary new.! !

!ObjectTransaction methodsFor: 'private/registering'!
instanceVariablesOf: anObject do: aBlock

	(1 to: anObject class instSize) do: [:index | aBlock value: (anObject instVarAt: index)].
	(1 to: anObject basicSize) do: [:index | aBlock value: (anObject basicAt: index)]! !

!ObjectTransaction methodsFor: 'private/registering'!
shallowCopyOf: anObject ifNotNeeded: aBlock

	| copy |
	copy := anObject shallowCopy.
	^copy == anObject 
		ifTrue: [aBlock value]
		ifFalse: [copy]! !

!ObjectTransaction methodsFor: 'private/restoring'!
isShapeOf: original differentThanThatOf: copy

	^original class ~~ copy class or: [original basicSize ~= copy basicSize]! !

!ObjectTransaction methodsFor: 'private/restoring'!
restoreIndexedInstanceVariablesOf: original toThoseOf: copy

	1 to: copy basicSize do: [:index |
		original basicAt: index put: (copy basicAt: index)]! !

!ObjectTransaction methodsFor: 'private/restoring'!
restoreNamedInstanceVariablesOf: original toThoseOf: copy

	1 to: copy class instSize do: [:index |
		original instVarAt: index put: (copy instVarAt: index)]! !

!ObjectTransaction methodsFor: 'private/restoring'!
restoreShapeOf: original toThatOf: copy

	| newOriginal |
	(copy class isBits or: [copy class isVariable])
		ifTrue: [newOriginal := copy class basicNew: copy basicSize]
		ifFalse: [newOriginal := copy class basicNew].
	original become: newOriginal.! !

!ObjectTransaction methodsFor: 'private/restoring'!
restoreStateOf: original toThatOf: copy 

	(self isShapeOf: original differentThanThatOf: copy) 
		ifTrue: [self restoreShapeOf: original toThatOf: copy].
	self restoreNamedInstanceVariablesOf: original toThoseOf: copy.
	self restoreIndexedInstanceVariablesOf: original toThoseOf: copy! !

!ObjectTransaction methodsFor: 'registering'!
isRegistered: anObject 
	"Note: We can never have a situation where a proxy is registered but its contents aren't, so we don't have to worry about that ambiguous case."
	| realObject |
	realObject := self realObjectFor: anObject.
	realObject isNil ifTrue: [^false].
	^undoMap includesKey: realObject.! !

!ObjectTransaction methodsFor: 'registering'!
realObjectFor: anObject 
	"If this is a proxy, return the contents (if available). Otherwise, return the object itself."
	^anObject class == Proxy 
		ifTrue: [anObject isInstantiated ifTrue: [anObject getValue] ifFalse: [nil]]
		ifFalse: [anObject]! !

!ObjectTransaction methodsFor: 'registering'!
register: anObject 
	"Make anObject be a member of the current transaction. Return the object if registered, or nil otherwise"

	| copy realObject |
	(self requiresRegistrationFor: anObject) ifFalse: [^nil].
	realObject := self realObjectFor: anObject.
	copy := self shallowCopyOf: realObject ifNotNeeded: [^nil].
	undoMap at: realObject put: copy.
	self registerTransientInternalsOfCollection: realObject.
	^realObject! !

!ObjectTransaction methodsFor: 'registering'!
registerTransientInternalsOfCollection: aCollection

	"If this is a collection, then we may need to register any internal structures it has, e.g. an internal array. This is implementation dependent for the collection. We will also explicitly exclude strings"

	aCollection glorpIsCollection ifFalse: [^self].
	aCollection isString ifTrue: [^self].
	aCollection glorpRegisterCollectionInternalsIn: self.! !

!ObjectTransaction methodsFor: 'registering'!
registeredObjectsDo: aBlock
	"Iterate over all our objects. Note that this will include objects without descriptors"
	undoMap keysDo: aBlock.! !

!ObjectTransaction methodsFor: 'registering'!
requiresRegistrationFor: anObject

	| realObject |
	realObject := self realObjectFor: anObject.
	realObject isNil ifTrue: [^false].
	^(self isRegistered: realObject) not.! !


!ObjectTransaction class methodsFor: 'instance creation'!
new
	^super new initialize! !


!OraclePlatform methodsFor: 'table creation'!
printNumericFieldOfSize: anInteger on: aStream

	aStream 
		nextPutAll: 'number('.
	anInteger printOn: aStream.
	aStream nextPutAll: ')'.! !

!OraclePlatform methodsFor: 'table creation'!
printStringFieldOfSize: anInteger on: aStream

	aStream 
		nextPutAll: 'varchar2('.
	anInteger printOn: aStream.
	aStream nextPutAll: ')'.! !

!OraclePlatform methodsFor: 'SQL'!
isOraclePlatform

	^true! !

!OraclePlatform methodsFor: 'SQL'!
sqlTextForVariableCharAttributeType: length
	"^<String>"

	^'VARCHAR2(',length asString,')'! !

!OraclePlatform methodsFor: 'SQL'!
supportsConstraints

	^true.! !

!OraclePlatform methodsFor: 'SQL'!
typeStringFor: type ofSize: size

	type == #string ifTrue: [^'varchar2(', size printString, ')'].
	type == #number ifTrue: [^'number(', size printString, ')'].
	self error: 'invalid field type'.! !


!PGAsciiRow methodsFor: 'as yet unclassified' stamp: 'nop 5/31/2002 18:04'!
description
	^ description! !


!PGColumnDescription methodsFor: 'as yet unclassified' stamp: 'nop 5/31/2002 18:10'!
isNumber
	^ typeOid = 23! !


!PGConnection methodsFor: 'events' stamp: 'nop 5/31/2002 15:11'!
execute: queryString 
	| pkt |
	resultSet errorResponse: nil.
	resultSet rowDescription: nil.
	resultSet rows reset.
	(pkt _ self receivePacket) eventName = #ReadyForQuery
		ifFalse: [self error: 'Failed to receive ReadyForQuery packet'].
	self sendQuery: queryString.
	pkt _ self receivePacket.
	pkt eventName = #ErrorResponse
		ifTrue: [resultSet errorResponse: pkt.
				Transcript show: pkt value;
				 cr]
		ifFalse: [pkt eventName = #CompletedResponse
				ifFalse: [pkt eventName = #CursorResponse
						ifTrue: [pkt _ self receivePacket.
							pkt eventName = #RowDescription
								ifTrue: [resultSet rowDescription: pkt.
									[(pkt _ self receivePacket) eventName = #CompletedResponse]
										whileFalse: [resultSet rows add: pkt]]
								ifFalse: [pkt eventName = #CompletedResponse
										ifFalse: [self error: 'Failed to receive RowDescription packet']]]]].
	^ resultSet! !


!ParameterExpression methodsFor: 'accessing'!
base

	^base! !

!ParameterExpression methodsFor: 'accessing'!
field
	^field! !

!ParameterExpression methodsFor: 'navigating'!
ultimateBaseExpression
	^base ultimateBaseExpression.! !

!ParameterExpression methodsFor: 'iterating'!
do: aBlock skipping: aSet

	(aSet includes: self) ifTrue: [^self].
	aSet add: self.
	base do: aBlock skipping: aSet.
	aBlock value: self.! !

!ParameterExpression methodsFor: 'preparing'!
asExpressionJoiningSource: source toTarget: target
	"Create a general expression which represents this relationship where the values of the targets (which are normally parameters) are supplied out of the context provided by 'target' and the source fields are referencing things out of the context of source. Produces something suitable for ANDing into an expression when doing a join
   Example: If we had CUSTOMER.ADDRESS_ID = ADDRESS.ID as a parameter, and we want to AND this into an expression [:customer | customer address street = 'Main'] then we have customer as a base, and we get 
	(customer.ADDRESS.STREET = 'Main') AND (customer.CUSTOMER.ADDRESS_ID = customer.ADDRESS.ID)
	The primary key expression for the relationship has been translated into field references into the customer and address tables in a particular context."

	^source getField: field.! !

!ParameterExpression methodsFor: 'printing'!
printOn: aStream 
	aStream nextPutAll: 'Parameter('.
	self printTreeOn: aStream.
	aStream nextPut: $)! !

!ParameterExpression methodsFor: 'printing'!
printOnlySelfOn: aStream

	field printSQLOn: aStream withParameters: #()! !

!ParameterExpression methodsFor: 'printing'!
printSQLOn: aStream withParameters: aDictionary
	(self valueIn: aDictionary) glorpPrintSQLOn: aStream.! !

!ParameterExpression methodsFor: 'printing'!
printTreeOn: aStream 
	field printSQLOn: aStream withParameters: #()! !

!ParameterExpression methodsFor: 'printing'!
valueIn: aDictionary 
	^aDictionary at: field! !

!ParameterExpression methodsFor: 'initialize/release'!
field: aDatabaseField base: aBaseExpression 
	field := aDatabaseField.
	base := aBaseExpression! !


!ParameterExpression class methodsFor: 'instance creation'!
forField: aField basedOn: anObjectExpression

	^self new field: aField base: anObjectExpression; yourself! !


!Passenger methodsFor: 'accessing'!
frequentFlyerPoints
	^frequentFlyerMiles.! !

!Passenger methodsFor: 'accessing'!
frequentFlyerPoints: aSmallInteger 
	frequentFlyerMiles := aSmallInteger.! !

!Passenger methodsFor: 'accessing'!
id
	^id! !

!Passenger methodsFor: 'accessing'!
id: aSmallInteger 
	id := aSmallInteger.! !

!Passenger methodsFor: 'accessing'!
name
	^name.! !

!Passenger methodsFor: 'accessing'!
name: aString 
	name := aString.! !


!Passenger class methodsFor: 'examples'!
example1

	^self new
		id: 1;
		name: 'Some Passenger';
		frequentFlyerPoints: 10000.! !


!Person methodsFor: 'accessing'!
address
	"Private - Answer the value of the receiver's ''address'' instance variable."

	^address! !

!Person methodsFor: 'accessing'!
address: anObject
	"Private - Set the value of the receiver's ''address'' instance variable to the argument, anObject."

	address := anObject! !

!Person methodsFor: 'accessing'!
emailAddresses
	^emailAddresses! !

!Person methodsFor: 'accessing'!
id
	"Private - Answer the value of the receiver's ''id'' instance variable."

	^id! !

!Person methodsFor: 'accessing'!
id: anObject
	"Private - Set the value of the receiver's ''id'' instance variable to the argument, anObject."

	id := anObject! !

!Person methodsFor: 'accessing'!
name
	"Private - Answer the value of the receiver's ''name'' instance variable."

	^name! !

!Person methodsFor: 'accessing'!
name: anObject
	"Private - Set the value of the receiver's ''name'' instance variable to the argument, anObject."

	name := anObject! !


!Person class methodsFor: 'example1'!
example1

	^self new
		id: 1;
		name: 'Zaphod Beeblebrox';
		address: Address example1.! !


!PositionableStream methodsFor: 'positioning' stamp: 'nop 5/31/2002 18:36'!
skipToAll: aCollection
	^self match: aCollection! !


!PostgreSQLPlatform methodsFor: 'SQL'!
isPostgreSQLPlatform

	^true! !

!PostgreSQLPlatform methodsFor: 'SQL'!
supportsConstraints

	^false.! !

!PostgreSQLPlatform methodsFor: 'SQL'!
typeStringFor: type ofSize: size 

	type ==#number ifTrue: [^'integer'].
	type == #string ifTrue: [^'varchar(', size printString, ')'].
	self error: 'invalid field type'.! !


!PrimaryKeyExpression methodsFor: 'accessing'!
allSourceFields

	^sources! !

!PrimaryKeyExpression methodsFor: 'accessing'!
allTables

	^(targets collect: [:each | each table]) asSet.! !

!PrimaryKeyExpression methodsFor: 'accessing'!
hasDescriptor

	^false.! !

!PrimaryKeyExpression methodsFor: 'accessing'!
numberOfParameters
	^sources size! !

!PrimaryKeyExpression methodsFor: 'accessing'!
targetKeys

	^targets.! !

!PrimaryKeyExpression methodsFor: 'accessing'!
ultimateBaseExpression

	^base.! !

!PrimaryKeyExpression methodsFor: 'converting'!
asExpression
	"Convert this to a 'normal' expression representing the same information"

	| main clause |
	main := nil.
	sources with: targets do: [:eachSource :eachTarget |
		| srcExp targetExp |
		srcExp := base getParameter: eachSource.
		targetExp := (base getTable: eachTarget table) getField: eachTarget.
		"Reversing the order is important because the source is the parameter, and sql won't accept '27 = FOO' "
		clause := targetExp equals: srcExp.
		main := main == nil 
			ifTrue: [clause]
			ifFalse: [main exAnd: clause]].
	^main.! !

!PrimaryKeyExpression methodsFor: 'converting'!
asExpressionJoiningSource: source toTarget: target
	"Create a general expression which represents this relationship where the values of the targets (which are normally parameters) are supplied out of the context provided by 'target' and the source fields are referencing things out of the context of source. Produces something suitable for ANDing into an expression when doing a join
   Example: If we had CUSTOMER.ADDRESS_ID = ADDRESS.ID as a parameter, and we want to AND this into an expression [:customer | customer address street = 'Main'] then we have customer as a base, and we get 
	(customer.ADDRESS.STREET = 'Main') AND (customer.CUSTOMER.ADDRESS_ID = customer.ADDRESS.ID)
	The primary key expression for the relationship has been translated into field references into the customer and address tables in a particular context."

	| sourceFieldExpression targetFieldExpression completeExpression |
	completeExpression := nil.
	sources with: targets
		do: 	[:sourceField :targetField | 
			sourceFieldExpression := source getField: sourceField.
			targetFieldExpression := target getField: targetField.
			completeExpression := (sourceFieldExpression equals: targetFieldExpression)
						exAnd: completeExpression].
	^completeExpression! !

!PrimaryKeyExpression methodsFor: 'iterating'!
fieldsDo: aBlock

	sources with: targets do: aBlock.! !

!PrimaryKeyExpression methodsFor: 'printing'!
printOn: aStream

	sources with: targets do: [:source :target |
		aStream nextPut: $(.
		source printSQLOn: aStream withParameters: #().
		aStream nextPutAll: ' = '.
		target printSQLOn: aStream withParameters: #().
		aStream nextPutAll: ') ']! !

!PrimaryKeyExpression methodsFor: 'printing'!
printSQLOn: aStream withParameters: aDictionary 
	1 to: sources size
		do: 
			[:i | 
			| eachTarget eachSource sourceValue |
			eachTarget := targets at: i.
			eachSource := sources at: i.
			eachTarget printSQLOn: aStream withParameters: aDictionary.
			sourceValue := aDictionary at: eachSource.
			sourceValue isNil 
				ifTrue: [aStream nextPutAll: ' IS NULL ']
				ifFalse: 
					[aStream nextPutAll: ' = '.
					(eachSource transform: sourceValue) printOn: aStream].
			i = targets size ifFalse: [aStream nextPutAll: ' AND ']]! !

!PrimaryKeyExpression methodsFor: 'preparing'!
additionalExpressions
	^#()! !

!PrimaryKeyExpression methodsFor: 'preparing'!
additionalExpressionsIn: aQuery 
	^#()! !

!PrimaryKeyExpression methodsFor: 'preparing'!
allTablesToPrint

	^targets inject: Set new into: [:sum :each | 
		sum add: each table. sum].! !

!PrimaryKeyExpression methodsFor: 'preparing'!
prepareIn: anObject 
	"Do nothing."

	^self asExpression prepareIn: anObject.! !

!PrimaryKeyExpression methodsFor: 'preparing'!
sourceForTarget: aField

	| index |
	index := targets indexOf: aField.
	index = 0 ifTrue: [^nil].
	^sources at: index.! !

!PrimaryKeyExpression methodsFor: 'testing'!
isPrimaryKeyExpression
	^true.! !

!PrimaryKeyExpression methodsFor: 'initialize'!
initialize

	sources := OrderedCollection new: 2.
	targets := OrderedCollection new: 2.
	base := BaseExpression new.! !

!PrimaryKeyExpression methodsFor: 'api'!
addSource: aField target: anotherField

	sources add: aField.
	targets add: anotherField.! !

!PrimaryKeyExpression methodsFor: 'api'!
asGlorpExpression

	^self.! !

!PrimaryKeyExpression methodsFor: 'api'!
asGlorpExpressionForDescriptor: aDescriptor

	base descriptor: aDescriptor.! !

!PrimaryKeyExpression methodsFor: 'api'!
mapFromSource: sourceObject andTarget: targetObject intoRowsIn: aRowMap 

	sources with: targets
		do: [:eachSourceField :eachTargetField | 
			FieldUnifier
				unifyFields: (Array with: eachSourceField with: eachTargetField)
				correspondingTo: (Array with: sourceObject with: targetObject)
				in: aRowMap]! !


!PrimaryKeyExpression class methodsFor: 'instance creation'!
from: aField to: anotherField

	^self new
		addSource: aField target: anotherField.! !

!PrimaryKeyExpression class methodsFor: 'instance creation'!
from: from1Field to: to1Field
from: from2Field to: to2Field

	^self new
		addSource: from1Field target: to1Field;
		addSource: from2Field target: to2Field.! !

!PrimaryKeyExpression class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!Proxy methodsFor: 'api'!
getValue

	isInstantiated ifTrue: [^value].
	value := query executeWithParameters: parameters in: session.
	isInstantiated := true.
	^value.! !

!Proxy methodsFor: 'initialize'!
doesNotUnderstand: aMessage

	^self getValue perform: aMessage selector withArguments: aMessage arguments.! !

!Proxy methodsFor: 'initialize'!
initialize

	isInstantiated := false.! !

!Proxy methodsFor: 'testing'!
isInstantiated
	^isInstantiated.! !

!Proxy methodsFor: 'accessing'!
class

	^Proxy.! !

!Proxy methodsFor: 'accessing'!
parameters
	^parameters! !

!Proxy methodsFor: 'accessing'!
parameters: aDictionary
	parameters := aDictionary.! !

!Proxy methodsFor: 'accessing'!
query
	^query! !

!Proxy methodsFor: 'accessing'!
query: aQuery 
	query := aQuery! !

!Proxy methodsFor: 'accessing'!
session
	^session! !

!Proxy methodsFor: 'accessing'!
session: aSession 
	session := aSession! !

!Proxy methodsFor: 'notification'!
glorpPostFetch: aSession! !


!Proxy class methodsFor: 'instance creation'!
new

	^super new initialize.! !

!Proxy class methodsFor: 'instance creation'!
returningManyOf: aClass where: aBlock
	
	^self new query: (Query returningManyOf: aClass where: aBlock).! !

!Proxy class methodsFor: 'instance creation'!
returningOneOf: aClass where: aBlock
	
	^self new query: (Query returningOneOf: aClass where: aBlock).! !


!Query methodsFor: 'executing'!
executeIn: aSession

	^self executeWithParameters:#() in: aSession.! !

!Query methodsFor: 'sql generation'!
printTablesOn: stream 
	GlorpHelper 
		print: [:table | table sqlTableName]
		on: stream
		for: self tablesToPrint
		separatedBy: ', '! !

!Query methodsFor: 'accessing'!
session
	^session! !

!Query methodsFor: 'accessing'!
session: aSession 
	session := aSession! !

!Query methodsFor: 'initialize'!
initialize

	prepared := false.! !


!AbstractReadQuery methodsFor: 'accessing'!
criteria
	^criteria! !

!AbstractReadQuery methodsFor: 'accessing'!
readsOneObject: aBoolean 
	readsOneObject := aBoolean! !

!AbstractReadQuery methodsFor: 'accessing'!
resultClass
	^resultClass! !

!AbstractReadQuery methodsFor: 'accessing'!
returnProxies
	^returnProxies! !

!AbstractReadQuery methodsFor: 'accessing'!
returnProxies: aBoolean
	returnProxies := aBoolean! !

!AbstractReadQuery methodsFor: 'accessing'!
shouldRefresh
	^shouldRefresh! !

!AbstractReadQuery methodsFor: 'accessing'!
shouldRefresh: aBoolean
	shouldRefresh := aBoolean! !

!AbstractReadQuery methodsFor: 'executing'!
setUpCriteria
	criteria := criteria asGlorpExpressionForDescriptor: (session descriptorFor: resultClass)! !

!AbstractReadQuery methodsFor: 'initialize/release'!
initResultClass: aClass criteria: theCriteria singleObject: aBoolean 
	resultClass := aClass.
	criteria := theCriteria asGlorpExpression.
	readsOneObject := aBoolean.! !

!AbstractReadQuery methodsFor: 'initialize/release'!
initialize
	
	super initialize.
	returnProxies := false.
	shouldRefresh := false.! !


!DeleteQuery methodsFor: 'executing'!
executeWithParameters: parameterDictionary in: aSession 
	| |
	session := aSession.
	self prepare.
	session accessor executeSQLString: (self sqlWith: parameterDictionary).
	session cacheRemoveObject: objectToDelete.! !

!DeleteQuery methodsFor: 'sql generation'!
sqlWith: aDictionary 
	"This is likely to break horribly if there is more than one table involved. If that happens, do I have to split this up into multiple deletes?"

	| string stream |
	self prepare.
	stream := WriteStream on: (String new: 100).
	stream nextPutAll: 'DELETE FROM '.
	self printTablesOn: stream.
	stream nextPutAll: ' WHERE '.
	criteria printSQLOn: stream withParameters: aDictionary.
	string := stream contents.
	^string! !

!DeleteQuery methodsFor: 'sql generation'!
tablesToPrint
	^criteria ultimateBaseExpression tables.! !

!DeleteQuery methodsFor: 'preparing'!
prepare
	prepared ifTrue: [^self].
	self setUpCriteria. "In case it hasn't already been done"
	criteria := criteria prepareIn: self.
	prepared := true.! !

!DeleteQuery methodsFor: 'preparing'!
setUpCriteria
	criteria := self descriptor primaryKeyExpressionFor: objectToDelete.
	criteria := criteria asGlorpExpressionForDescriptor: (session descriptorFor: objectToDelete)! !

!DeleteQuery methodsFor: 'accessing'!
descriptor
	^session descriptorFor: objectToDelete.! !

!DeleteQuery methodsFor: 'accessing'!
objectToDelete: anObject
	objectToDelete := anObject.! !


!Query class methodsFor: 'instance creation'!
new

	^super new initialize.! !

!Query class methodsFor: 'instance creation'!
returningManyOf: aClass where: criteria
	"Backward-compatibility, since we changed the class name."
	^ReadQuery returningManyOf: aClass where: criteria.! !

!Query class methodsFor: 'instance creation'!
returningOneOf: aClass where: criteria
	"Backward-compatibility, since we changed the class name."
	^ReadQuery returningOneOf: aClass where: criteria.! !


!AbstractReadQuery class methodsFor: 'instance creation'!
returningManyOf: aClass where: criteria

	^self new
		initResultClass: aClass criteria: criteria singleObject: false.! !

!AbstractReadQuery class methodsFor: 'instance creation'!
returningOneOf: aClass where: criteria

	^self new
		initResultClass: aClass criteria: criteria singleObject: true.! !


!DeleteQuery class methodsFor: 'instance creation'!
for: anObject
	^self new objectToDelete: anObject.! !


!QueryStub methodsFor: 'accessing'!
result
	^result! !

!QueryStub methodsFor: 'accessing'!
result: anObject
	result := anObject! !

!QueryStub methodsFor: 'executing'!
executeWithParameters: parameterArray in: aSession 

	aSession register: result.
	^result.! !


!ReadQuery methodsFor: 'printing'!
printWhereClauseOn: aStream withParameters: anArray 
	criteria printSQLOn: aStream withParameters: anArray! !

!ReadQuery methodsFor: 'accessing'!
absentBlock

	absentBlock == nil ifTrue: [^[nil]].
	^absentBlock.! !

!ReadQuery methodsFor: 'accessing'!
defaultTracing
	^self descriptor defaultTracing.! !

!ReadQuery methodsFor: 'accessing'!
descriptor
	^session descriptorFor: resultClass.! !

!ReadQuery methodsFor: 'executing'!
executeWithParameters: parameterDictionary in: aSession 
	| cacheHit |
	session := aSession.
	self setUpCriteria.
	cacheHit := self checkCacheWithParameters: parameterDictionary.
	cacheHit isNil ifFalse: [^cacheHit].
	^self 
		readFromDatabaseWithParameters: parameterDictionary
		tracing: self defaultTracing
		inSession: aSession! !

!ReadQuery methodsFor: 'executing'!
readFromDatabaseForTraceNodes: aCollection withParameters: aDictionary
	| simpleQuery |
	simpleQuery := self asSimpleQueryForTraceNodes: aCollection.
	^simpleQuery readFromDatabaseWithParameters: aDictionary! !

!ReadQuery methodsFor: 'executing'!
readFromDatabaseWithParameters: aDictionary tracing: aTracing inSession: anObject 
	| traceNodeSets result |
	self setupTracing: aTracing.
	traceNodeSets := aTracing traceNodeSets.
	result := self readFromDatabaseForTraceNodes: traceNodeSets first
				withParameters: aDictionary.
	readsOneObject ifTrue: [^result isEmpty ifTrue: [self absentBlock value] ifFalse: [result first]].
	2 to: traceNodeSets size
		do: 
			[:i | 
			self readFromDatabaseForTraceNodes: (traceNodeSets at: i)
				withParameters: aDictionary].
	^result! !

!ReadQuery methodsFor: 'caching'!
checkCacheWithParameters: aCollection 
	"We can only check the cache on parameterized queries, and even then it doesn't work for composite keys. Needs work"
	| primaryKey |
	readsOneObject ifFalse: [^nil].
	self shouldRefresh ifTrue: [^nil].
	primaryKey := self primaryKeyWithParameters: aCollection.
	primaryKey isNil ifTrue: [^nil].
	^session 
		cacheAt: primaryKey
		forClass: resultClass
		ifNone: [nil]! !

!ReadQuery methodsFor: 'caching'!
primaryKeyWithParameters: aCollection 
	
	aCollection isEmpty ifTrue: [^nil].
	"Return any element. Clearly broken for composite keys"
	aCollection do: [:each | ^each].! !

!ReadQuery methodsFor: 'tracing'!
setupTracing: aTracing 
	self descriptor setupTracing: aTracing! !

!ReadQuery methodsFor: 'converting'!
asSimpleQueryForTraceNodes: anArray

	| newQuery |
	newQuery := SimpleQuery new
		initResultClass: resultClass criteria: criteria singleObject: readsOneObject.
	newQuery session: session.
	newQuery returnProxies: self returnProxies.
	newQuery shouldRefresh: self shouldRefresh.
	newQuery setUpCriteria.
	newQuery traceNodes: anArray.
	^newQuery.! !


!ReadStream methodsFor: 'Not categorized'!
collect: aBlock 
	| newStream |
	newStream := WriteStream on: collection species new.
	[self atEnd] whileFalse: [newStream nextPut: (aBlock value: self next)].
	^newStream contents! !


!RelationExpression methodsFor: 'accessing'!
leftChild
	^leftChild! !

!RelationExpression methodsFor: 'accessing'!
leftChild: anExpression 
	leftChild := anExpression! !

!RelationExpression methodsFor: 'accessing'!
relation

	^relation.! !

!RelationExpression methodsFor: 'accessing'!
relation: aSymbol

	relation := aSymbol.! !

!RelationExpression methodsFor: 'accessing'!
rightChild
	^rightChild! !

!RelationExpression methodsFor: 'accessing'!
rightChild: anExpression
	rightChild := anExpression! !

!RelationExpression methodsFor: 'navigating'!
ultimateBaseExpression

	^leftChild ultimateBaseExpression.! !

!RelationExpression methodsFor: 'preparing'!
additionalExpressions

	^#().! !

!RelationExpression methodsFor: 'preparing'!
asExpressionJoiningSource: source toTarget: target
	"Create a general expression which represents this relationship where the values of the targets (which are normally parameters) are supplied out of the context provided by 'target' and the source fields are referencing things out of the context of source. Produces something suitable for ANDing into an expression when doing a join
   Example: If we had CUSTOMER.ADDRESS_ID = ADDRESS.ID as a parameter, and we want to AND this into an expression [:customer | customer address street = 'Main'] then we have customer as a base, and we get 
	(customer.ADDRESS.STREET = 'Main') AND (customer.CUSTOMER.ADDRESS_ID = customer.ADDRESS.ID)
	The primary key expression for the relationship has been translated into field references into the customer and address tables in a particular context."

	| left right |
	left := leftChild asExpressionJoiningSource: source toTarget: target.
	right := rightChild asExpressionJoiningSource: source toTarget: target.
	^self class new 
		relation: relation;
		leftChild: left;
		rightChild: right.! !

!RelationExpression methodsFor: 'api'!
get: aSymbol withArguments: anArray
	"Return the mapping expression corresponding to the named attribute"

	^anArray isEmpty 
		ifTrue: [self error: 'Only binary relationships supported right now']
		ifFalse: [RelationExpression named: aSymbol basedOn: self withArguments: anArray].! !

!RelationExpression methodsFor: 'iterating'!
do: aBlock skipping: aSet
	
	(aSet includes: self) ifTrue: [^self].
	aSet add: self.
	leftChild do: aBlock skipping: aSet.
	rightChild do: aBlock skipping: aSet.
	aBlock value: self.! !

!RelationExpression methodsFor: 'printing'!
printOnlySelfOn: aStream

	aStream nextPutAll: relation.! !

!RelationExpression methodsFor: 'printing'!
printTreeOn: aStream 
	aStream
		print: leftChild;
		space;
		nextPutAll: relation;
		space;
		print: rightChild! !

!RelationExpression methodsFor: 'testing'!
isPrimaryKeyExpression
	^false.! !

!RelationExpression methodsFor: 'private/initializing'!
named: aSymbol basedOn: anExpression withArguments: anArray

	relation := self operationFor: aSymbol.
	leftChild := anExpression.
	rightChild := anArray first asGlorpExpressionOn: anExpression ultimateBaseExpression.! !

!RelationExpression methodsFor: 'private/initializing'!
operationFor: aSymbol
	"Simple translation of operators"

	aSymbol == #exAnd: ifTrue: [^#AND].
	aSymbol == #& ifTrue: [^#AND].
	aSymbol == #exOr: ifTrue: [^#OR].
	aSymbol == #| ifTrue: [^#OR].
	aSymbol == #~= ifTrue: [^#<>].
	 aSymbol == #like: ifTrue: [^#LIKE].
	 aSymbol == #in: ifTrue: [^#IN].
	^aSymbol.! !

!RelationExpression methodsFor: 'printing SQL'!
printSQLOn: aStream withParameters: aDictionary 
	leftChild printSQLOn: aStream withParameters: aDictionary.
	(rightChild valueIn: aDictionary) isNil ifTrue: [^self printWithNullOn: aStream].
	aStream
		space;
		nextPutAll: self relation;
		space.
	rightChild printSQLOn: aStream withParameters: aDictionary.! !

!RelationExpression methodsFor: 'printing SQL'!
printWithNullOn: aStream
	aStream nextPutAll: ' IS '.
	self relation = #<> ifTrue: [
		aStream nextPutAll: 'NOT '].
	aStream nextPutAll: 'NULL'.! !


!CollectionExpression methodsFor: 'printing SQL'!
printSQLOn: aStream withParameters: aDictionary 
	"Don't print the left child or ourselves, just the expression that is the right side.
e.g. aPerson addresses anySatisfy: [:each | each city='Ottawa'] prints as
where (address.city = 'Ottawa')
The relation 'aPerson addresses' will ensure that the join gets printed"
	rightChild printSQLOn: aStream withParameters: aDictionary.! !

!CollectionExpression methodsFor: 'private/initializing'!
named: aSymbol basedOn: anExpression withArguments: anArray
	"We know that our argument is a block and it operates on the elements of the receiver, i.e. the left child"

	relation := aSymbol.
	leftChild := anExpression.
	rightChild := anArray first asGlorpExpressionOn: leftChild.! !


!RelationExpression class methodsFor: 'instance creation'!
named: aSymbol basedOn: anExpression withArguments: anArray

	^self new
		named: aSymbol
		basedOn: anExpression
		withArguments: anArray.! !


!RelationshipMapping methodsFor: 'accessing'!
allTables

	mappingCriteria isNil ifTrue: [^#()].
	^mappingCriteria allTables.! !

!RelationshipMapping methodsFor: 'accessing'!
mappedFields
	"Return a collection of fields that this mapping will write into any of the containing object's rows"

	^self mappingCriteria allSourceFields.! !

!RelationshipMapping methodsFor: 'accessing'!
mappingCriteria
	"Private - Answer the value of the receiver's ''mappingCriteria'' instance variable."

	^mappingCriteria! !

!RelationshipMapping methodsFor: 'accessing'!
mappingCriteria: anObject
	"Private - Set the value of the receiver's ''mappingCriteria'' instance variable to the argument, anObject."

	mappingCriteria := anObject! !

!RelationshipMapping methodsFor: 'accessing'!
referenceClass
	"Private - Answer the value of the receiver's ''referenceClass'' instance variable."

	^referenceClass! !

!RelationshipMapping methodsFor: 'accessing'!
referenceClass: anObject
	"Private - Set the value of the receiver's ''referenceClass'' instance variable to the argument, anObject."

	referenceClass := anObject! !

!RelationshipMapping methodsFor: 'accessing'!
referenceDescriptor

	^self system descriptorFor: self referenceClass.! !

!RelationshipMapping methodsFor: 'accessing'!
shouldProxy

	^shouldProxy.! !

!RelationshipMapping methodsFor: 'accessing'!
shouldProxy: aBoolean

	shouldProxy := aBoolean.! !

!RelationshipMapping methodsFor: 'testing'!
controlsTables
	"Return true if this type of method 'owns' the tables it's associated with, and expression nodes using this mapping should alias those tables where necessary"

	^true! !

!RelationshipMapping methodsFor: 'testing'!
isIndependentRelationship
	"True when the mapping associates different tables."

	^true! !

!RelationshipMapping methodsFor: 'testing'!
isRelationship
	"True when the mapping associates different classes."

	^true! !

!RelationshipMapping methodsFor: 'initializing'!
initialize

	super initialize.
	shouldProxy := true.! !

!RelationshipMapping methodsFor: 'mapping'!
extendedMappingCriteria

	^mappingCriteria.! !

!RelationshipMapping methodsFor: 'mapping'!
isValidTarget: anObject

	^anObject class == Proxy
		ifTrue: [anObject isInstantiated]
		ifFalse: [anObject notNil].! !

!RelationshipMapping methodsFor: 'mapping'!
mapFromObject: anObject intoRowsIn: aRowMap 
	
	| target |
	target := self getValueFrom: anObject.
	(self isValidTarget: target)
		ifTrue: [self mapFromObject: anObject toTarget: target puttingRowsIn: aRowMap].! !

!RelationshipMapping methodsFor: 'mapping'!
mapFromRow: anArray intoObject: anObject inElementBuilder: anElementBuilder 
	| parameters |
	parameters := IdentityDictionary new.
	mappingCriteria fieldsDo: [:eachSource :eachTarget | 
		parameters 
			at: eachSource
			put: (anElementBuilder valueOf: eachSource in: anArray)].
	self setValueIn: anObject
		to: (self shouldProxy 
				ifTrue: 
					[(self newProxy)
						session: descriptor session;
						parameters: parameters]
				ifFalse: [self query executeWithParameters: parameters in: descriptor session])! !

!RelationshipMapping methodsFor: 'mapping'!
query

	self subclassResponsibility.! !

!RelationshipMapping methodsFor: 'api'!
referencedIndependentObjectsFrom: anObject

	^self getValueFrom: anObject.! !

!RelationshipMapping methodsFor: 'processing'!
trace: aTracing context: anExpression
	"Currently we don't trace relationships across tables, so all we do here
is accumulate the list of embedded mappings"
	| newContext |
	(aTracing tracesThrough: self) ifFalse: [^self].
	newContext := anExpression get: attributeName.
	aTracing addTracing: newContext.
	self referenceDescriptor trace: aTracing context: newContext.! !

!RelationshipMapping methodsFor: 'preparing'!
joinExpressionFor: targetExpression
	"We're looking for the object represented by this mapping, and we know the object represented by its source. Use our mapping criteria to construct a join that traverses that instance of this relationship"

	| sourceExpression |
	sourceExpression := targetExpression base.
	^self extendedMappingCriteria asExpressionJoiningSource: sourceExpression toTarget: targetExpression.! !

!RelationshipMapping methodsFor: 'proxies'!
newProxy

	| proxy |
	proxy := Proxy new.
	proxy query: (self query).
	^proxy.! !


!ManyToManyMapping methodsFor: 'mapping'!
extendedMappingCriteria
	"In order to do a many-to-many read we need more information than just the write, we need to know 
	the relationship to the other table. Construct that based on the table information"

	| generalMappingCriteria base |
	generalMappingCriteria := mappingCriteria asExpression.
	base := generalMappingCriteria ultimateBaseExpression.
	^generalMappingCriteria exAnd: 
		(self expressionFromLinkToReferenceTableWithBase: base) .! !

!ManyToManyMapping methodsFor: 'mapping'!
mapFromObject: anObject toTarget: aCollection puttingRowsIn: aRowMap 

	aCollection do: [:each | 
		(self isValidTarget: each) ifTrue: [
			| rowMapKey |
			rowMapKey := (RowMapKey new)
						key1: anObject;
						key2: each.
			mappingCriteria 
				mapFromSource: anObject
				andTarget: rowMapKey
				intoRowsIn: aRowMap]]! !

!ManyToManyMapping methodsFor: 'mapping'!
query
	^ReadQuery returningManyOf: referenceClass
		where: self extendedMappingCriteria! !

!ManyToManyMapping methodsFor: 'private/expressions'!
expressionFromLinkToReferenceTable
	| referenceKeys linkTable constraints referenceTables expression |
	referenceKeys := mappingCriteria targetKeys asOrderedCollection.
	linkTable := referenceKeys first table.
	"Find all the foreign keys in the link table that aren't the ones from our source to the link, assuming that they will all be from the link to the target"
	constraints := linkTable foreignKeyConstraints reject: [:each | 
		(referenceKeys includes: each sourceField)].

	"Validate that we can handle this case"
	referenceTables := (constraints collect: [:each | each targetField table]) asSet.
	referenceTables size = 1 ifFalse: [self error: 'Cannot handle this general a case'].

	expression := PrimaryKeyExpression new.
	constraints do: [:each |
		expression
			addSource: each sourceField
			target: each targetField].
	^expression asExpression.! !

!ManyToManyMapping methodsFor: 'private/expressions'!
expressionFromLinkToReferenceTableWithBase: base
	| referenceKeys linkTable constraints referenceTables expression |
	referenceKeys := mappingCriteria targetKeys asOrderedCollection.
	linkTable := referenceKeys first table.
	"Find all the foreign keys in the link table that aren't the ones from our source to the link, assuming that they will all be from the link to the target"
	constraints := linkTable foreignKeyConstraints reject: [:each | 
		(referenceKeys includes: each sourceField)].

	"Validate that we can handle this case"
	referenceTables := (constraints collect: [:each | each targetField table]) asSet.
	referenceTables size > 1 ifTrue: [self error: 'Cannot handle this general a case'].
	referenceTables size = 0 ifTrue: [self error: 'No tables found. Did you set up foreign key references in the table definitions?'].

	expression := nil.
	constraints do: [:each | |src target |
		src := (base getTable: each sourceField table) getField: each sourceField.
		target := (base getTable: each targetField table) getField: each targetField.
		expression := expression isNil 
			ifTrue: [src equals: target]
			ifFalse: [expression exAnd: (src equals: target)]].
	^expression.! !

!ManyToManyMapping methodsFor: 'api'!
referencedIndependentObjectsFrom: anObject

	| collection includingTheCollectionItself |
	collection := super referencedIndependentObjectsFrom: anObject.
	includingTheCollectionItself := OrderedCollection new: (collection size + 1).
	includingTheCollectionItself addAll: collection.
	includingTheCollectionItself add: collection.
	^includingTheCollectionItself.! !


!OneToManyMapping methodsFor: 'mapping'!
mapFromObject: anObject toTarget: aCollection puttingRowsIn: aRowMap 
	aCollection do: [:each | 
		(self isValidTarget: each) ifTrue: [
			mappingCriteria 
				mapFromSource: anObject
				andTarget: each
				intoRowsIn: aRowMap]].! !

!OneToManyMapping methodsFor: 'mapping'!
query
	^ReadQuery returningManyOf: referenceClass where: mappingCriteria! !

!OneToManyMapping methodsFor: 'api'!
referencedIndependentObjectsFrom: anObject

	| collection includingTheCollectionItself |
	collection := super referencedIndependentObjectsFrom: anObject.
	includingTheCollectionItself := OrderedCollection new: (collection size + 1).
	includingTheCollectionItself addAll: collection.
	includingTheCollectionItself add: collection.
	^includingTheCollectionItself.! !


!OneToOneMapping methodsFor: 'testing'!
isOneToOne

	^true! !

!OneToOneMapping methodsFor: 'mapping'!
mapFromObject: anObject toTarget: target puttingRowsIn: aRowMap 

	mappingCriteria 
		mapFromSource: anObject
		andTarget: target
		intoRowsIn: aRowMap! !

!OneToOneMapping methodsFor: 'mapping'!
query
	^ReadQuery returningOneOf: referenceClass where: mappingCriteria! !

!OneToOneMapping methodsFor: 'mapping'!
referencedIndependentObjectsFrom: anObject

	^Array with: (self getValueFrom: anObject).! !


!EmbeddedValueOneToOneMapping methodsFor: 'transformations'!
defaultTransformationExpressionFor: aDescriptor 
	"If there's no transformation, get all the mapped fields from the other descriptor and construct a transformation of each onto itself. This lets us unify the fields in my row with the fields in its row"

	| fields transform |
	fields := IdentitySet new.
	aDescriptor mappings do: [:each | fields addAll: each mappedFields].
	transform := PrimaryKeyExpression new.
	fields do: [:each | transform addSource: each target: each].
	^transform! !

!EmbeddedValueOneToOneMapping methodsFor: 'transformations'!
hasTransformation

	^false.! !

!EmbeddedValueOneToOneMapping methodsFor: 'transformations'!
transformationExpression

	^self hasFieldTranslation
		ifTrue: [fieldTranslation]
		ifFalse: [self defaultTransformationExpressionFor: (self referenceDescriptor)].! !

!EmbeddedValueOneToOneMapping methodsFor: 'internal'!
fieldsForSelectStatement
	"Return a collection of fields that this mapping will read from a row"
	"Return nothing, because our sub-objects will take care of adding their own fields, translated correctly through us."
	^#().! !

!EmbeddedValueOneToOneMapping methodsFor: 'internal'!
mappedFields
	"Return a collection of fields that this mapping will write into any of the containing object's rows"

	fieldTranslation isNil ifFalse: [^fieldTranslation allSourceFields].
	^self referenceDescriptor mappedFields.! !

!EmbeddedValueOneToOneMapping methodsFor: 'accessing'!
fieldTranslation
	^fieldTranslation! !

!EmbeddedValueOneToOneMapping methodsFor: 'accessing'!
fieldTranslation: aPrimaryKeyExpression 
	fieldTranslation := aPrimaryKeyExpression! !

!EmbeddedValueOneToOneMapping methodsFor: 'accessing'!
hasFieldTranslation

	^fieldTranslation notNil.! !

!EmbeddedValueOneToOneMapping methodsFor: 'testing'!
controlsTables
	"Return true if this type of method 'owns' the tables it's associated with, and expression nodes using this mapping should alias those tables where necessary"

	^false! !

!EmbeddedValueOneToOneMapping methodsFor: 'testing'!
isIndependentRelationship
	"True when the mapping associates different tables."

	^false! !

!EmbeddedValueOneToOneMapping methodsFor: 'testing'!
shouldProxy

	^false.! !

!EmbeddedValueOneToOneMapping methodsFor: 'mapping'!
mapFromObject: anObject toTarget: target puttingRowsIn: aRowMap 

	self transformationExpression 
		mapFromSource: anObject
		andTarget: target
		intoRowsIn: aRowMap.

	(aRowMap rowsForKey: target) do: [:each | each shouldBeWritten: false]! !

!EmbeddedValueOneToOneMapping methodsFor: 'mapping'!
mapFromRow: valueCollection intoObject: anObject inElementBuilder: anElementBuilder 

	| myTraceNode myBuilder |
	"If the object already has a value in my slot, then this it got a cache hit, the embedded value was carried along for the ride, and we don't need to assign anything"
	(self getValueFrom: anObject) isNil ifFalse: [^self].

	"Otherwise, we need to look up the trace node that corresponds to this mapping, and get its instance"
	myTraceNode := anElementBuilder expression get: attributeName.
	myBuilder := anElementBuilder query elementBuilderFor: myTraceNode.
	self setValueIn: anObject to: myBuilder instance.! !

!EmbeddedValueOneToOneMapping methodsFor: 'mapping'!
translateFields: anOrderedCollection 
	fieldTranslation isNil ifTrue: [^anOrderedCollection].
	^anOrderedCollection collect: [:each |
		fieldTranslation sourceForTarget: each].! !

!EmbeddedValueOneToOneMapping methodsFor: 'preparing'!
joinExpressionFor: targetExpression
	"We're looking for the object represented by this mapping, and we know the object represented by its source. Use our mapping criteria to construct a join that traverses that instance of this relationship.
	Embedded values never induce a join."

	^nil.! !


!RowMap methodsFor: 'lookup'!
findOrAddRowForTable: aTable withKey: aKey 
	| submap |
	submap := self subMapForTable: aTable withKey: aKey.
	^submap at: aKey ifAbsentPut: [DatabaseRow newForTable: aTable withOwner: aKey]! !

!RowMap methodsFor: 'lookup'!
includesRowForTable: aTable withKey: aKey

	(self subMapForTable: aTable ifAbsent: [^false]) at: aKey ifAbsent: [^false].
	^true.! !

!RowMap methodsFor: 'lookup'!
rowForTable: aTable withKey: aKey 
	^(self subMapForTable: aTable) at: aKey! !

!RowMap methodsFor: 'iterating'!
keysAndValuesDo: aBlock 
	self tables do: [:each | 
		(self subMapForTable: each) keysAndValuesDo: aBlock].! !

!RowMap methodsFor: 'iterating'!
rowsDo: aBlock 
	self tables do: [:each | self rowsForTable: each do: aBlock]! !

!RowMap methodsFor: 'iterating'!
rowsForTable: aTable do: aBlock 
	^(self subMapForTable: aTable) do: aBlock! !

!RowMap methodsFor: 'counting'!
numberOfEntries
	^rowDictionary inject: 0 into: [:sum :each | sum + each size]! !

!RowMap methodsFor: 'counting'!
numberOfEntriesForTable: aTable

	^(self subMapForTable: aTable) size.! !

!RowMap methodsFor: 'private/mapping'!
dictionaryClassRequiredForKeysOfType: aClass

	^aClass == RowMapKey
		ifTrue: [Dictionary]
		ifFalse: [IdentityDictionary].! !

!RowMap methodsFor: 'private/mapping'!
rowsForKey: aKey 
	"Return a collection of all rows for any table which are keyed by aKey"

	| rowsForKey |
	rowsForKey := OrderedCollection new: 5.
	rowDictionary do: 
			[:each | 
			| row |
			row := each at: aKey ifAbsent: [nil].
			row isNil ifFalse: [rowsForKey add: row]].
	^rowsForKey.! !

!RowMap methodsFor: 'private/mapping'!
subMapForTable: aTable
	
	^self subMapForTable: aTable withKey: nil.! !

!RowMap methodsFor: 'private/mapping'!
subMapForTable: aTable ifAbsent: aBlock 
	^rowDictionary at: aTable ifAbsent: aBlock! !

!RowMap methodsFor: 'private/mapping'!
subMapForTable: aTable withKey: anObject 
	^rowDictionary at: aTable
		ifAbsentPut: [(self dictionaryClassRequiredForKeysOfType: anObject class) new]! !

!RowMap methodsFor: 'private/mapping'!
tables

	^rowDictionary keys.! !

!RowMap methodsFor: 'initialize/release'!
initialize
	rowDictionary := IdentityDictionary new! !


!RowMap class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!RowMapKey methodsFor: 'comparing'!
= aRowMapKey

	aRowMapKey class == self class ifFalse: [^false].
	^(key1 == aRowMapKey key1 and: [key2 == aRowMapKey key2]) or: [
		key2 == aRowMapKey key1 and: [key1 == aRowMapKey key2]].! !

!RowMapKey methodsFor: 'comparing'!
hash

	^key1 identityHash bitXor: key2 identityHash.! !

!RowMapKey methodsFor: 'accessing'!
key1
	^key1! !

!RowMapKey methodsFor: 'accessing'!
key1: anObject
	key1 := anObject! !

!RowMapKey methodsFor: 'accessing'!
key2
	^key2! !

!RowMapKey methodsFor: 'accessing'!
key2: anObject
	key2 := anObject! !


!SQLServerPlatform methodsFor: 'SQL'!
isODBCPlatform

	^true! !

!SQLServerPlatform methodsFor: 'SQL'!
supportsConstraints

	^true! !

!SQLServerPlatform methodsFor: 'SQL'!
typeStringFor: type ofSize: size 

	type == #number ifTrue: [^'integer'].
	type == #string ifTrue: [^'varchar(', size printString, ')'].
	self error: 'invalid field type'.! !


!SequencePolicy methodsFor: 'accessing'!
field
	"Private - Answer the value of the receiver's ''field'' instance variable."

	^field! !

!SequencePolicy methodsFor: 'accessing'!
field: anObject
	"Private - Set the value of the receiver's ''field'' instance variable to the argument, anObject."

	field := anObject! !

!SequencePolicy methodsFor: 'initialize/release'!
initialize! !

!SequencePolicy methodsFor: 'sequencing'!
postWriteAssignSequenceValueFor: aField in: aRow

	self subclassResponsibility.! !

!SequencePolicy methodsFor: 'sequencing'!
preWriteAssignSequenceValueFor: aField in: aRow

	self subclassResponsibility.! !


!InMemorySequencePolicy methodsFor: 'initialize/release'!
initialize

	super initialize.
	count := 0.! !

!InMemorySequencePolicy methodsFor: 'sequencing'!
postWriteAssignSequenceValueFor: aField in: aRow! !

!InMemorySequencePolicy methodsFor: 'sequencing'!
preWriteAssignSequenceValueFor: aField in: aRow

	aRow at: aField put: (count := count + 1).! !


!NullSequencePolicy methodsFor: 'sequencing'!
postWriteAssignSequenceValueFor: aField in: aRow! !

!NullSequencePolicy methodsFor: 'sequencing'!
preWriteAssignSequenceValueFor: aField in: aRow! !


!SequencePolicy class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!InMemorySequencePolicy class methodsFor: 'defaults'!
default

	^self new.! !


!NullSequencePolicy class methodsFor: 'defaults'!
default

	^self new.! !

!NullSequencePolicy class methodsFor: 'instance creation'!
new

	Singleton isNil ifTrue: [Singleton := self basicNew].
	^Singleton.! !


!SequenceTablePolicy methodsFor: 'accessing'!
sequenceTableName
	"Private - Answer the value of the receiver's ''sequenceTableName'' instance variable."

	^sequenceTableName! !

!SequenceTablePolicy methodsFor: 'accessing'!
sequenceTableName: anObject
	"Private - Set the value of the receiver's ''sequenceTableName'' instance variable to the argument, anObject."

	sequenceTableName := anObject! !


!SequenceTablePolicy class methodsFor: 'defaults'!
default

	^self new sequenceTableName: 'SEQUENCE'.! !


!ServiceCharge methodsFor: 'accessing'!
amount
	^amount! !

!ServiceCharge methodsFor: 'accessing'!
amount: anObject
	amount := anObject! !

!ServiceCharge methodsFor: 'accessing'!
description
	^description! !

!ServiceCharge methodsFor: 'accessing'!
description: anObject
	description := anObject! !

!ServiceCharge methodsFor: 'initialize'!
initialize! !


!ServiceCharge class methodsFor: 'instance creation'!
default

	^self new
		amount: (GlorpMoney forAmount: 3);
		description: 'additional overcharge'.! !

!ServiceCharge class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!Session methodsFor: 'initialize'!
initialize

	self initializeCache.! !

!Session methodsFor: 'initialize'!
initializeCache

	cache := CacheManager forSession: self.! !

!Session methodsFor: 'initialize'!
reset

	self initializeCache.
	currentUnitOfWork := nil.! !

!Session methodsFor: 'accessing'!
accessor
	^accessor! !

!Session methodsFor: 'accessing'!
accessor: aDatabaseAccessor 
	accessor := aDatabaseAccessor! !

!Session methodsFor: 'accessing'!
applicationData
	^applicationData! !

!Session methodsFor: 'accessing'!
applicationData: anObject	
	applicationData := anObject! !

!Session methodsFor: 'accessing'!
system
	^system! !

!Session methodsFor: 'caching'!
cacheAt: aKey  forClass: aClass ifNone: failureBlock

	^cache lookupClass: aClass key: aKey ifAbsent: failureBlock.! !

!Session methodsFor: 'caching'!
cacheAt: keyObject put: valueObject 
	^cache at: keyObject insert: valueObject! !

!Session methodsFor: 'caching'!
cacheContainsObjectForRow: aDatabaseRow
	^(self cacheLookupObjectForRow: aDatabaseRow) notNil.! !

!Session methodsFor: 'caching'!
cacheLookupForClass: aClass key: aKey 
	^self 
		cacheAt: aKey
		forClass: aClass
		ifNone: [nil]! !

!Session methodsFor: 'caching'!
cacheLookupObjectForRow: aDatabaseRow
	^self 
		cacheLookupForClass: aDatabaseRow owner class
		key: aDatabaseRow primaryKey! !

!Session methodsFor: 'caching'!
cacheRemoveObject: anObject
	| key |
	key := (self descriptorFor: anObject) primaryKeyFor: anObject.
	cache removeClass: anObject class key: key.! !

!Session methodsFor: 'copying'!
copy

	^self shallowCopy postCopy.! !

!Session methodsFor: 'copying'!
postCopy

	super postCopy.
	self initializeCache.
	currentUnitOfWork := nil.! !

!Session methodsFor: 'api'!
descriptorFor: aClass

	^system descriptorFor: aClass.! !

!Session methodsFor: 'api'!
hasDescriptorFor: aClass

	^system hasDescriptorFor: aClass.! !

!Session methodsFor: 'api'!
isRegistered: anObject

	currentUnitOfWork isNil ifTrue: [^false].
	^currentUnitOfWork isRegistered: anObject.! !

!Session methodsFor: 'api'!
register: anObject

	currentUnitOfWork isNil ifTrue: [^self].
	currentUnitOfWork register: anObject.! !

!Session methodsFor: 'api'!
system: aSystem 
	aSystem session: self.
	system := aSystem! !

!Session methodsFor: 'queries'!
delete: anObject

	^self execute: (DeleteQuery for: anObject).! !

!Session methodsFor: 'queries'!
execute: aQuery

	^aQuery executeIn: self.! !

!Session methodsFor: 'queries'!
readManyOf: aClass where: aBlock

	^self execute: (ReadQuery returningManyOf: aClass where: aBlock).! !

!Session methodsFor: 'queries'!
readOneOf: aClass where: aBlock

	^self execute: (ReadQuery returningOneOf: aClass where: aBlock).! !

!Session methodsFor: 'read/write'!
writeRow: aDatabaseRow 
	aDatabaseRow shouldBeWritten ifFalse: [^self].
	aDatabaseRow preWriteAssignSequences.
	accessor executeSQLString: (self sqlStringFor: aDatabaseRow). 
	aDatabaseRow postWriteAssignSequences! !

!Session methodsFor: 'events'!
sendPostFetchEventTo: anObject
	anObject glorpPostFetch: self.! !

!Session methodsFor: 'events'!
sendPostWriteEventTo: anObject
	anObject glorpPostWrite: self.! !

!Session methodsFor: 'events'!
sendPreWriteEventTo: anObject
	anObject glorpPreWrite: self.! !

!Session methodsFor: 'api/transactions'!
beginTransaction
	accessor beginTransaction.! !

!Session methodsFor: 'api/transactions'!
beginUnitOfWork

	self hasUnitOfWork ifTrue: [self error: 'Cannot nest units of work yet'].
	currentUnitOfWork := UnitOfWork new.
	currentUnitOfWork session: self.! !

!Session methodsFor: 'api/transactions'!
commitTransaction
	accessor commitTransaction! !

!Session methodsFor: 'api/transactions'!
commitUnitOfWork

	currentUnitOfWork commit.
	currentUnitOfWork := nil.! !

!Session methodsFor: 'api/transactions'!
hasUnitOfWork

	^currentUnitOfWork notNil.! !

!Session methodsFor: 'api/transactions'!
rollbackTransaction
	accessor rollbackTransaction.! !

!Session methodsFor: 'api/transactions'!
rollbackUnitOfWork

	currentUnitOfWork abort.
	currentUnitOfWork := nil.! !

!Session methodsFor: 'internal/writing'!
createRowsFor: anObject in: rowMap

	(self descriptorFor: anObject class) createRowsFor: anObject in: rowMap.! !

!Session methodsFor: 'internal/writing'!
shouldInsert: aDatabaseRow 
	^(self cacheContainsObjectForRow: aDatabaseRow) not.! !

!Session methodsFor: 'internal/writing'!
sqlInsertStringFor: aDatabaseRow 
	| stream |
	stream := WriteStream on: (String new: 100).
	stream nextPutAll: 'INSERT INTO '.
	aDatabaseRow table printSQLOn: stream withParameters: #().
	stream nextPutAll: ' VALUES ('.
	aDatabaseRow printFieldValuesOn: stream.
	stream nextPutAll: ')'.
	^stream contents! !

!Session methodsFor: 'internal/writing'!
sqlStringFor: aDatabaseRow 
	
	^(self shouldInsert: aDatabaseRow)
		ifTrue: [self  sqlInsertStringFor: aDatabaseRow]
		ifFalse: [self sqlUpdateStringFor: aDatabaseRow].! !

!Session methodsFor: 'internal/writing'!
sqlUpdateStringFor: aDatabaseRow 
	| stream |
	stream := WriteStream on: (String new: 100).
	stream nextPutAll: 'UPDATE '.
	aDatabaseRow table printSQLOn: stream withParameters: #().
	stream nextPutAll: ' SET '.
	GlorpHelper 
		print: [:eachField | aDatabaseRow equalityStringForField: eachField]
		on: stream
		for: aDatabaseRow fields
		separatedBy: ', '.
	stream nextPutAll: ' WHERE '.
	aDatabaseRow printPrimaryKeyStringOn: stream.
	^stream contents! !

!Session methodsFor: 'internal/writing'!
tablesInCommitOrder

	^(TableSorter for: system allTables) sort.! !

!Session methodsFor: 'private'!
privateGetCache

	^cache.! !

!Session methodsFor: 'private'!
privateGetCurrentUnitOfWork

	^currentUnitOfWork.! !

!Session methodsFor: 'As yet unclassified'!
hasExpired: anObject 
	^cache hasExpired: anObject.! !

!Session methodsFor: 'As yet unclassified'!
hasObjectExpiredOfClass: aClass withKey: key
	^cache hasObjectExpiredOfClass: aClass withKey: key.! !


!Session class methodsFor: 'instance creation'!
forSystem: aSystem 
	^self new system: aSystem! !

!Session class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!SessionResource methodsFor: 'accessing'!
newSession

	self setUp.
	^self session.! !

!SessionResource methodsFor: 'accessing'!
session

	^session.! !

!SessionResource methodsFor: 'setup'!
setUp

	| login |
	super setUp.
	login := DatabaseLoginResource current.
	GlorpDemoTablePopulatorResource current.
	session := Session new.
	session system: GlorpDemoDescriptorSystem new.
	session accessor: login accessor.! !


!SessionResource class methodsFor: 'resources'!
resources

	^Array with: DatabaseLoginResource with: GlorpDemoTablePopulatorResource.! !


!SimpleQuery methodsFor: 'accessing'!
builders

	^builders.! !

!SimpleQuery methodsFor: 'accessing'!
criteria: anExpression
	criteria := anExpression! !

!SimpleQuery methodsFor: 'accessing'!
elementBuilderFor: anExpression 
	^builders detect: [:each | each expression == anExpression] ifNone: [nil].! !

!SimpleQuery methodsFor: 'accessing'!
fields
	^fields! !

!SimpleQuery methodsFor: 'accessing'!
resultClass: aClass 
	resultClass := aClass.! !

!SimpleQuery methodsFor: 'accessing'!
traceNodes
	^traceNodes! !

!SimpleQuery methodsFor: 'accessing'!
traceNodes: aCollection
	traceNodes := aCollection collect: [:each | self traceExpressionInContextFor: each].
	builders := traceNodes collect: [:each | ElementBuilder for: each in: self].! !

!SimpleQuery methodsFor: 'sql generation'!
printSelectFields: aCollection on: stream 
	GlorpHelper 
		print: [:field | field qualifiedName]
		on: stream
		for: aCollection
		separatedBy: ', '! !

!SimpleQuery methodsFor: 'sql generation'!
printSelectFieldsOn: stream 
	distinctFields isNil 
		ifTrue: [self printSelectFields: fields on: stream]
		ifFalse: 
			[stream nextPutAll: 'DISTINCT '.
			self printSelectFields: distinctFields on: stream.
			distinctFields size = fields size ifFalse: [stream nextPutAll: ', '].
			self 
				printSelectFields: (fields copyFrom: distinctFields size + 1 to: fields size)
				on: stream]! !

!SimpleQuery methodsFor: 'sql generation'!
sqlWith: aDictionary 
	| string stream |
	self prepare.
	stream := WriteStream on: (String new: 100).
	stream nextPutAll: 'SELECT '.
	self printSelectFieldsOn: stream.
	stream
		cr;
		nextPutAll: ' FROM '.
	self printTablesOn: stream.
	stream
		cr;
		nextPutAll: ' WHERE '.
	criteria printSQLOn: stream withParameters: aDictionary.
	string := stream contents.
	^string! !

!SimpleQuery methodsFor: 'sql generation'!
tablesToPrint
	| allTables |
	allTables := (fields collect: [:each | each table]) asSet.
	allTables addAll: criteria allTablesToPrint.
	^allTables.! !

!SimpleQuery methodsFor: 'fields'!
addDistinctField: aField

	distinctFields isNil ifTrue: [distinctFields := OrderedCollection new].
	distinctFields add: aField.! !

!SimpleQuery methodsFor: 'fields'!
addFields: aliasedFields returningTranslationForFields: originalFields distinct: isDistinct 
	"The query has computed a set of fields the way the mappings see them, which are then transformed to account for field aliasing in embedded mappings. Add those to our collection, and set up the translation which knows which fields are at which index in the resulting row. If necessary, note that those fields are selected as distinct"

	| translation |
	translation := IdentityDictionary new.
	aliasedFields with: originalFields
		do: 
			[:aliased :original | 
			| position |
			position := fields indexOf: aliased.
			position = 0 
				ifTrue: 
					[fields add: aliased.
					position := fields size.
					isDistinct ifTrue: [self addDistinctField: aliased]].
			translation at: original put: position].
	^translation! !

!SimpleQuery methodsFor: 'executing'!
buildObjectsFrom: anArray 
	builders do: [:each | each findInstanceForRow: anArray useProxy: self returnProxies].
	builders do: [:each | each buildObjectFrom: anArray].
	^builders first instance! !

!SimpleQuery methodsFor: 'executing'!
computeFields
	builders 
		do: [:each | each hasFieldTranslations ifFalse: [self computeFieldsFor: each]]! !

!SimpleQuery methodsFor: 'executing'!
computeFieldsFor: anElementBuilder 
	| translatedFields |
	translatedFields := self 
		addFields: anElementBuilder fieldsForSelectStatement
		returningTranslationForFields: anElementBuilder fieldsFromMyPerspective
		distinct: anElementBuilder requiresDistinct.
	anElementBuilder fieldTranslations: translatedFields.! !

!SimpleQuery methodsFor: 'executing'!
readFromDatabaseWithParameters: aDictionary 
	| rows objects |
	rows := session accessor executeSQLString: (self sqlWith: aDictionary).
	objects := rows collect: [:each | self buildObjectsFrom: each].
	objects do: [:each | session sendPostFetchEventTo: each].
	^objects! !

!SimpleQuery methodsFor: 'preparing'!
assignTableAliases
	| tableNumber |
	criteria isPrimaryKeyExpression ifTrue: [^self].
	tableNumber := 1.
	criteria do: [:each |
		tableNumber := each assignTableAliasesStartingAt: tableNumber].! !

!SimpleQuery methodsFor: 'preparing'!
prepare
	prepared ifTrue: [^self].
	self setUpCriteria. "In case it hasn't already been done"
	criteria := criteria prepareIn: self.
	self assignTableAliases.
	self computeFields.
	prepared := true.! !

!SimpleQuery methodsFor: 'preparing'!
traceExpressionInContextFor: anExpression 
	
	^anExpression rebuildOn: criteria ultimateBaseExpression.! !

!SimpleQuery methodsFor: 'initialize'!
initResultClass: aClass criteria: theCriteria singleObject: aBoolean 
	super 
		initResultClass: aClass
		criteria: theCriteria
		singleObject: aBoolean.
	prepared := false.
	fields := OrderedCollection new.! !


!SqueakDatabaseAccessor methodsFor: 'transactions' stamp: 'nop 5/29/2002 23:36'!
beginTransaction
	logging
		ifTrue: [Transcript show: 'Begin Transaction';
				 cr].
	isInTransaction _ true.
	connection execute: 'BEGIN TRANSACTION'! !

!SqueakDatabaseAccessor methodsFor: 'transactions' stamp: 'nop 5/29/2002 23:36'!
commitTransaction
	logging
		ifTrue: [Transcript show: 'Commit Transaction';
				 cr].
	isInTransaction := false.
	connection execute: 'COMMIT TRANSACTION'.! !

!SqueakDatabaseAccessor methodsFor: 'transactions' stamp: 'nop 5/29/2002 23:37'!
isInTransaction
	^ isInTransaction! !

!SqueakDatabaseAccessor methodsFor: 'transactions' stamp: 'nop 5/29/2002 23:36'!
rollbackTransaction
	logging
		ifTrue: [Transcript show: 'Rollback Transaction';
				 cr].
	isInTransaction := false.
	connection execute: 'ROLLBACK TRANSACTION'! !

!SqueakDatabaseAccessor methodsFor: 'executing'!
disconnect

	^connection disconnect! !

!SqueakDatabaseAccessor methodsFor: 'executing' stamp: 'nop 5/31/2002 18:16'!
executeSQLString: aString 
	| resultSet rowCollection |
	logging
		ifTrue: [Transcript show: aString;
				 cr].
	resultSet _ connection execute: aString.
	resultSet errorResponse notNil
		ifTrue: [self externalDatabaseErrorSignal signal: resultSet errorResponse value].
	rowCollection _ OrderedCollection new.
	resultSet rows
		do: [:ea | 
			| columns data descr | 
			columns _ OrderedCollection new.
			data _ ea data.
			descr _ ea description columnDescriptions.
			1
				to: data size
				do: [:idx | (descr at: idx) isNumber
						ifTrue: [|nbr |
								nbr := data at: idx.
								(nbr respondsTo: #asNumber) ifTrue: [nbr := nbr asNumber].
								columns add: nbr]
						ifFalse: [columns
								add: (data at: idx)]].
			rowCollection add: columns asArray].
	^ rowCollection asArray! !

!SqueakDatabaseAccessor methodsFor: 'executing' stamp: 'nop 5/29/2002 15:50'!
externalDatabaseErrorSignal
	^ Error! !

!SqueakDatabaseAccessor methodsFor: 'login' stamp: 'nop 5/29/2002 18:40'!
connectionArgs
	currentLogin isNil
		ifTrue: [^ PGConnectionArgs
		hostname: 'localhost'
		portno: port
		databaseName: 'squeakdb'
		userName: 'bern'
		password: 'bern'].
	^ PGConnectionArgs
		hostname: (currentLogin connectString copyUpTo: $_)
		portno: port
		databaseName: (currentLogin connectString copyAfter: $_)
		userName: currentLogin username
		password: currentLogin password! !

!SqueakDatabaseAccessor methodsFor: 'login' stamp: 'nop 5/29/2002 15:19'!
connectionClassForLogin: aLogin 
	aLogin database class == PostgreSQLPlatform
		ifTrue: [^ Smalltalk at: #PGConnection].
	self error: 'Unknown database: ' , aLogin database! !

!SqueakDatabaseAccessor methodsFor: 'login'!
isLoggedIn

	connection isNil ifTrue: [^false].
	^connection isConnected! !

!SqueakDatabaseAccessor methodsFor: 'login' stamp: 'nop 5/29/2002 18:40'!
loginIfError: aBlock 
	logging
		ifTrue: [Transcript show: 'Login';
				 cr].
	connection _ self connectionClass new.
	self
		doCommand: [connection startup: self connectionArgs]
		ifError: aBlock.
	logging
		ifTrue: [Transcript show: 'Login finished';
				 cr]! !

!SqueakDatabaseAccessor methodsFor: 'login' stamp: 'nop 5/29/2002 17:11'!
logout
	self isLoggedIn
		ifFalse: [^ self].
	logging
		ifTrue: [Transcript show: 'Logout';
				 cr].
	self
		doCommand: [connection terminate].
	logging
		ifTrue: [Transcript show: 'Logout finished';
				 cr]! !

!SqueakDatabaseAccessor methodsFor: 'login' stamp: 'nop 5/29/2002 18:32'!
port: aPort 
	aPort isInteger ifTrue: [
	port _ aPort].
	^port! !

!SqueakDatabaseAccessor methodsFor: 'login'!
showDialog: aString

	(Smalltalk at: #Dialog) warn: aString.! !

!SqueakDatabaseAccessor methodsFor: 'initialize' stamp: 'nop 5/29/2002 23:35'!
initialize
	super initialize.
	logging _ true.
	port _ 5432.
	isInTransaction := false.! !


!String methodsFor: 'glorp'!
glorpPrintSQLOn: aStream

	self printOn: aStream.! !


!Symbol methodsFor: 'glorp'!
asGlorpExpression
	"In theory we can have parameters that aren't field references, but have to be supplied when executing, and these can't be used in a join. These aren't used yet"
	^ParameterExpression for: self.! !

!Symbol methodsFor: 'glorp' stamp: 'nop 5/31/2002 15:15'!
isSymbol
	^true! !


!TableExpression methodsFor: 'accessing'!
base

	^base.! !

!TableExpression methodsFor: 'accessing'!
printsTable

	^true.! !

!TableExpression methodsFor: 'accessing'!
table

	^table.! !

!TableExpression methodsFor: 'accessing'!
ultimateBaseExpression
	^base ultimateBaseExpression.! !

!TableExpression methodsFor: 'preparing'!
aliasedTableFor: aDatabaseTable 
	^self controlsTables 
		ifTrue: [super aliasedTableFor: aDatabaseTable]
		ifFalse: [base aliasedTableFor: aDatabaseTable]! !

!TableExpression methodsFor: 'preparing'!
asExpressionJoiningSource: source toTarget: target
	"Create a general expression which represents this relationship where the values of the targets (which are normally parameters) are supplied out of the context provided by 'target' and the source fields are referencing things out of the context of source. Produces something suitable for ANDing into an expression when doing a join
   Example: If we had CUSTOMER.ADDRESS_ID = ADDRESS.ID as a parameter, and we want to AND this into an expression [:customer | customer address street = 'Main'] then we have customer as a base, and we get 
	(customer.ADDRESS.STREET = 'Main') AND (customer.CUSTOMER.ADDRESS_ID = customer.ADDRESS.ID)
	The primary key expression for the relationship has been translated into field references into the customer and address tables in a particular context."

	^target getTable: table.! !

!TableExpression methodsFor: 'preparing'!
controlsTables
	"We can end up with a table expression built on top of a base that has the same table. If so, we don't count as controlling that table"

	base isNil ifTrue: [^true].
	base hasDescriptor ifFalse: [^true].
	^(base descriptor tables includes: table) not.! !

!TableExpression methodsFor: 'preparing'!
tables

	^Array with: table.! !

!TableExpression methodsFor: 'preparing'!
tablesToPrint

	self controlsTables ifFalse: [^#()].
	^Array with: (self aliasedTableFor: table).! !

!TableExpression methodsFor: 'iterating'!
do: aBlock skipping: aSet
	"Iterate over the expression tree"

	(aSet includes: self) ifTrue: [^self].
	aSet add: self.
	base do: aBlock skipping: aSet.
	aBlock value: self.! !

!TableExpression methodsFor: 'printing'!
printOnlySelfOn: aStream

	table printSQLOn: aStream withParameters: #().
	self printTableAliasesOn: aStream.! !

!TableExpression methodsFor: 'printing'!
printTreeOn: aStream 
	base printOn: aStream.
	aStream nextPut: $..
	table printSQLOn: aStream withParameters: #()! !

!TableExpression methodsFor: 'initialize/release'!
table: aDatabaseTable base: aBaseExpression

	table := aDatabaseTable.
	base := aBaseExpression.! !


!TableExpression class methodsFor: 'instance creation'!
forTable: aDatabaseTable basedOn: aBaseExpression

	^self new table: aDatabaseTable base: aBaseExpression; yourself! !


!TableSorter methodsFor: 'accessing'!
addTable: aTable 
	tables add: aTable! !

!TableSorter methodsFor: 'accessing'!
hasBeenVisited: aTable

	^visitedTables includes: aTable.! !

!TableSorter methodsFor: 'accessing'!
markVisited: aTable

	visitedTables add: aTable.! !

!TableSorter methodsFor: 'initializing'!
initialize
	tables := OrderedCollection new: 100.
	visitedTables := IdentitySet new: 100.! !

!TableSorter methodsFor: 'sorting'!
sort
	orderedTables := OrderedCollection new: tables size.
	tables do: [:each | self visit: each].
	^orderedTables! !

!TableSorter methodsFor: 'sorting'!
visit: aTable 
	"The essential bit of topological sort. Visit each node in post-order, traversing dependencies, based on foreign key constraints to database-generated fields. "

	(self hasBeenVisited: aTable) ifTrue: [^self].
	self markVisited: aTable.
	self visitDependentTablesFor: aTable.
	orderedTables add: aTable! !

!TableSorter methodsFor: 'sorting'!
visitDependentTablesFor: aTable 
	aTable foreignKeyConstraints do: [:eachConstraint | 
		| fieldFromOtherTable |
		fieldFromOtherTable := eachConstraint targetField.
		fieldFromOtherTable isGenerated 
			ifTrue: [self visit: fieldFromOtherTable table]]! !


!TableSorter class methodsFor: 'instance creation'!
for: tables

	| sorter |
	sorter := self new.
	tables do: [:each |
		sorter addTable: each].
	^sorter.! !

!TableSorter class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!TestCase methodsFor: 'Accessing'!
unfinished

	"indicates an unfinished test"! !


!BasicMappingTest methodsFor: 'tests'!
testGet
	
	self assert: (mapping getValueFrom: person) = 1! !

!BasicMappingTest methodsFor: 'tests'!
testSet
	
	mapping setValueIn: person to: 2.
	self assert: person id = 2.
	self assert: (mapping getValueFrom: person) = 2.! !

!BasicMappingTest methodsFor: 'support'!
setUp

	super setUp.
	mapping := DirectMapping new.
	mapping attributeName: #id.
	person := Person example1.! !


!CacheTest methodsFor: 'support'!
setUp

	| session |
	super setUp.
	session := Session forSystem: GlorpDemoDescriptorSystem new.
	cache := session privateGetCache.! !

!CacheTest methodsFor: 'tests'!
testDuplicates 
	| c1 c2 |
	c1 := Customer example1.
	c2 := Customer example1.
	cache at: 3 insert: c1.
	cache at: 3 insert: c2.
	self assert: (cache lookupClass: Customer key: 3) = c1.! !

!CacheTest methodsFor: 'tests'!
testDuplicatesDifferentClasses

	| cust trans |
	cust := Customer example1.
	trans := BankTransaction example1.
	cache at: 3 insert: cust.
	cache at: 3 insert: trans.
	self assert: (cache lookupClass: Customer key: 3) = cust.
	self assert: (cache lookupClass: BankTransaction key: 3) = trans.! !

!CacheTest methodsFor: 'tests'!
testInsert
	| customer |
	customer := Customer example1.
	cache at: 3 insert: customer.
	self assert: (cache lookupClass: Customer key: 3) == customer! !


!CommitOrderTest methodsFor: 'tests'!
testCommitOrder

	| sorter |
	sorter := TableSorter for: (Array with: (system tableNamed: 'BANK_TRANS') with: (system tableNamed: 'GR_CUSTOMER')).
	self assert: sorter sort first name = 'GR_CUSTOMER'.! !

!CommitOrderTest methodsFor: 'tests'!
testCommitOrder2
	"Test for a cycle between t1 and t2 with t3 also pointing to both. Order of t1, t2 is indeterminate, but t3 should be last"

	| t2fk t3fk t3fk2 sorter t1fk |
	t1id useSequencingInMemory.
	t2id useSequencingInMemory.

	t1fk := t1 newFieldNamed: 'T2_ID'.
	t1 addForeignKeyFrom: t1fk to: (t2id).
	t2fk := t2 newFieldNamed: 'T1_ID'.
	t2 addForeignKeyFrom: t2fk to: (t1id).

	t3 := DatabaseTable new name: 'T3'.
	t3fk := t3 newFieldNamed: 'T2_ID'.
	t3 addForeignKeyFrom: t3fk to: (t2id).
	t3fk2 := t3 newFieldNamed: 'T1_ID'.
	t3 addForeignKeyFrom: t3fk2 to: (t1id).

	sorter := TableSorter for: (Array with: t3 with: t2 with: t1).
	self assert: sorter sort last name = 'T3'.! !

!CommitOrderTest methodsFor: 'tests'!
testCommitOrderNonSequencedFieldsDontCount

	"Test for a cycle between t1 and t2 with t3 also pointing to both, but with nothing sequenced. Order should be completely indeterminate. We rely on the topological sort being predictable and depending on the insert order so that if we feed objects with no dependencies in in different orders we should get different results."

	| t1fk t2fk t3fk t3fk2 sorter sorter2 |
	t1fk := t1 newFieldNamed: 'T2_ID'.
	t1 addForeignKeyFrom: t1fk to: (t2id).
	t2fk := t2 newFieldNamed: 'T1_ID'.
	t2 addForeignKeyFrom: t2fk to: (t1id).

	t3 := DatabaseTable new name: 'T3'.
	t3fk := t3 newFieldNamed: 'T2_ID'.
	t3 addForeignKeyFrom: t3fk to: (t2id).
	t3fk2 := t3 newFieldNamed: 'T1_ID'.
	t3 addForeignKeyFrom: t3fk2 to: (t1id).

	sorter := TableSorter for: (Array with: t3 with: t2 with: t1).
	sorter2 := TableSorter for: (Array with: t1 with: t2 with: t3).
	self assert: sorter sort first ~= sorter2 sort first.! !

!CommitOrderTest methodsFor: 'support'!
setUp

	super setUp.
	system := GlorpDemoDescriptorSystem new.
	t1 := DatabaseTable new name: 'T1'.
	t1id := (t1 newFieldNamed: 'ID') bePrimaryKey.
	t2 := DatabaseTable new name: 'T2'.
	t2id := (t2 newFieldNamed: 'ID') bePrimaryKey.

	t3 := DatabaseTable new name: 'T3'.
	t3id := (t3 newFieldNamed: 'ID') bePrimaryKey.! !


!ConstantMappingTest methodsFor: 'support'!
setUp
	super setUp.
	mappingToClass := ConstantMapping new
		attributeName: #slot;
		constantValue: 34.
	mappingToRow := ConstantMapping new.
	mappingToSession := ConstantMapping new! !

!ConstantMappingTest methodsFor: 'tests'!
testConstantInClass

	slot := nil.
	mappingToClass mapFromRow: nil intoObject: self inElementBuilder: nil.
	self assert: slot = 34.! !

!ConstantMappingTest methodsFor: 'tests'!
testConstantInClassDoesNotWriteToRow

	"Would raise an exception if it tried to write into nil"
	mappingToClass mapFromObject: self intoRowsIn: nil.! !

!ConstantMappingTest methodsFor: 'tests'!
testGetValue

	slot := nil.
	self assert: (mappingToClass getValueFrom: self) = 34.! !

!ConstantMappingTest methodsFor: 'tests'!
testSessionValue
	mappingToClass constantValueIsSession.
	self assert: (mappingToClass constantValueIn: 38)== 38.! !


!DatabaseBasicTest methodsFor: 'accessing'!
accessor
	^self databaseLoginResource accessor! !

!DatabaseBasicTest methodsFor: 'support'!
databaseLoginResource
	^DatabaseLoginResource current.! !

!DatabaseBasicTest methodsFor: 'support'!
setUp

	super setUp.
	system := GlorpDemoDescriptorSystem new.! !

!DatabaseBasicTest methodsFor: 'tests'!
testBeginTransactionWithCommit
	self assert: self accessor isInTransaction not.
	self accessor beginTransaction.
	self assert: self accessor isInTransaction.
	self accessor commitTransaction.
	self assert: self accessor isInTransaction not! !

!DatabaseBasicTest methodsFor: 'tests'!
testBeginTransactionWithRollback
	self assert: self accessor isInTransaction not.
	self accessor beginTransaction.
	self assert: self accessor isInTransaction.
	self accessor rollbackTransaction.
	self assert: self accessor isInTransaction not! !

!DatabaseBasicTest methodsFor: 'tests'!
testCreateTable
	| selectResult |
	
	[self accessor 
		executeSQLString: 'CREATE TABLE GLORP_TEST_CREATE (ID varchar(4))'.
	selectResult := self accessor 
				executeSQLString: 'SELECT * FROM GLORP_TEST_CREATE'.
	self assert: selectResult isEmpty] 
			ensure: 
				[self accessor dropTableNamed: 'GLORP_TEST_CREATE'
					ifAbsent: [:ex | self assert: false]]! !

!DatabaseBasicTest methodsFor: 'tests'!
testDropMissingTable
	| absentFlag |
	absentFlag := false.
	self accessor 
		dropTableNamed: 'GLORP_TEST_DROP' 
		ifAbsent: [:ex  | absentFlag := true. ex sunitExitWith: nil].
	self assert: absentFlag! !

!DatabaseBasicTest methodsFor: 'tests'!
testReadEmpty
	| results |
	results := self accessor
				executeSQLString: 'SELECT * FROM PERSON'.
	self assert: results size = 0! !

!DatabaseBasicTest methodsFor: 'tests'!
testReadStatement
	| results |
	results := self accessor
				executeSQLString: 'SELECT * FROM STUFF ORDER BY ID'.
	self assert: results size = 5.
	self assert: results first size = 2.
	self assert: results first last =  'abc'! !


!DatabaseBasicTest class methodsFor: 'resources'!
resources
	^Array with: DatabaseLoginResource with: GlorpDemoTablePopulatorResource.! !


!DatabaseFieldTest methodsFor: 'support'!
setUp

	| table |
	super setUp.
	table := DatabaseTable new name: 'T'.
	field := (table newFieldNamed: 'TEST') beNumeric.
	negatedField := TransformedField new 
		parent: field; 
		transformation: [:each | each * -1];
		stringTransformation: [:each | (each , ' * -1')].! !

!DatabaseFieldTest methodsFor: 'tests'!
testInvert
	| newField |
	newField := field inverted.
	self assert: newField class == TransformedField.
	self assert: newField parent == field! !

!DatabaseFieldTest methodsFor: 'tests'!
testPrint

	| stream |
	stream := String new writeStream.
	negatedField printSQLOn: stream withParameters: #().
	self assert: stream contents = 'T.TEST * -1'.! !

!DatabaseFieldTest methodsFor: 'tests'!
testTransformation

	self assert: (field convertToDatabaseForm: 'abc') = 'abc'.
	self assert: (field convertToDatabaseForm: #abc) = 'abc'.
	self assert: (field convertToDatabaseForm: -3) = -3.
	self assert: (negatedField convertToDatabaseForm: -3) = 3.! !


!DatabaseLoginTest methodsFor: 'accessing'!
accessor

	^accessor.! !

!DatabaseLoginTest methodsFor: 'support'!
setUp
	super setUp.
	login := DatabaseLoginResource defaultLogin.
	accessor := DatabaseAccessor forLogin: login.! !

!DatabaseLoginTest methodsFor: 'tests'!
testLogin
	self assert: self accessor isLoggedIn not.
	self accessor login.
	self assert: self accessor isLoggedIn.
	self accessor logout.
	self assert: self accessor isLoggedIn not! !

!DatabaseLoginTest methodsFor: 'tests'!
testUnsuccessfulLogin

	| anotherAccessor invalidLogin |
	invalidLogin := DatabaseLoginResource defaultLogin copy.
	invalidLogin
		password: 'you will never ever guess this password';
		username: 'not a valid user name'.
	anotherAccessor := DatabaseAccessor forLogin: invalidLogin.

	self assert: anotherAccessor isLoggedIn not.
	anotherAccessor loginIfError: [:ex | ].
	Dialect isVisualAge ifFalse: [ "The isLoggedIn is unreliable under VA, can return false positive"
		self assert: anotherAccessor isLoggedIn not].
	anotherAccessor logout.! !


!DatabaseLoginTest class methodsFor: 'resources'!
resources

	^Array with: DatabaseLoginResource.! !


!DatabaseSessionTest methodsFor: 'support'!
setUp

	super setUp.
	session := SessionResource current newSession.! !

!DatabaseSessionTest methodsFor: 'tests'!
testWriteRow
	| rowToWrite fields rowReadFromDatabase |
	rowToWrite := session system examplePersonRow2.
	
	[session beginTransaction.
	session writeRow: rowToWrite.
	rowReadFromDatabase := (session accessor
				executeSQLString: 'SELECT * FROM ' , rowToWrite table name) first.
	fields := rowToWrite table fields.
	(1 to: fields size) with: fields
		do: [:index :field | self assert: (rowReadFromDatabase atIndex: index) = (rowToWrite at: field)]] 
			ensure: [session rollbackTransaction]! !


!DatabaseSessionTest class methodsFor: 'resources'!
resources

	^Array with: SessionResource.! !


!DeleteQueryTest methodsFor: 'support'!
setUp

	session := SessionResource current newSession.! !

!DeleteQueryTest methodsFor: 'support'!
tearDown

	session := nil.! !

!DeleteQueryTest methodsFor: 'tests'!
testExecute
	| customer query result |	
	[session beginTransaction.
	session accessor executeSQLString: 'INSERT INTO GR_CUSTOMER VALUES (1,''Fred Flintstone'')'.
	customer := session execute: (ReadQuery returningOneOf: Customer where: [:each | each id = 1]).
	self assert: (session cacheLookupForClass: Customer key: 1) == customer.
	query := DeleteQuery for: customer.
	session execute: query.
	result := session accessor executeSQLString: 'SELECT * FROM GR_CUSTOMER WHERE ID=1'.
	self assert: result isEmpty.
	self assert: (session cacheLookupForClass: Customer key: 1) isNil]
		ensure: [session rollbackTransaction]! !

!DeleteQueryTest methodsFor: 'tests'!
testSQL
	| customer query sql |
	customer := Customer new.
	customer id: 12.
	query := DeleteQuery for: customer.
	query session: session.
	query prepare.
	sql := query sqlWith: Dictionary new.
	self assert: sql asLowercase = 'delete from gr_customer where gr_customer.id = 12'.! !


!DescriptorTest methodsFor: 'support'!
setUp

	system := GlorpDemoDescriptorSystem new.! !

!DescriptorTest methodsFor: 'tests'!
errorSignal

	Dialect isVisualAge ifTrue: [^(Smalltalk at: #SystemExceptions) at: 'ExAll'].
	^Error.! !

!DescriptorTest methodsFor: 'tests'!
testAllMappingsForField
	| descriptor mappings |
	descriptor := system descriptorFor: Customer.
	mappings := descriptor 
				allMappingsForField: ((system tableNamed: 'GR_CUSTOMER') fieldNamed: 'ID').
	self assert: mappings first attributeName = #id! !

!DescriptorTest methodsFor: 'tests'!
testMappedFields

	| descriptor |
	descriptor := system descriptorFor: BankTransaction.
	self assert: (descriptor mappedFields) = (descriptor table fields).
	self assert: (descriptor fieldsForSelectStatement) = (descriptor table fields copyFrom: 1 to: 2).! !

!DescriptorTest methodsFor: 'tests'!
testMappingForField
	| descriptor mapping |
	descriptor := system descriptorFor: Customer.
	mapping := descriptor 
				directMappingForField: ((system tableNamed: 'GR_CUSTOMER') fieldNamed: 'ID').
	self assert: mapping attributeName = #id! !

!DescriptorTest methodsFor: 'tests'!
testPrimaryKeyExpressionFor

	| descriptor trans exp |
	descriptor := system descriptorFor: BankTransaction.
	trans := BankTransaction new.
	trans id: 42.
	exp := descriptor primaryKeyExpressionFor: trans.
	self assert: exp relation = #=.
	self assert: exp rightChild value = 42.! !

!DescriptorTest methodsFor: 'tests'!
testPrimaryKeyExpressionForFailing

	| descriptor trans |
	descriptor := system descriptorFor: BankTransaction.
	trans := Customer new.
	self should: [descriptor primaryKeyExpressionFor: trans] raise: self errorSignal.! !

!DescriptorTest methodsFor: 'tests'!
testPrimaryKeyExpressionForWithCompositeKey

	self unfinished.! !


!DictionaryMappingTest methodsFor: 'support'!
setUp

	system := GlorpEncyclopediaDescriptorSystem new.! !

!DictionaryMappingTest methodsFor: 'tests'!
testCasesToWrite

	"How des a dictionary mapping relate to 1:many vs many:many.
	dictionary of strings to strings
	dictionary of strings to objects
	dictionary of objects to objects
	keys always have to be related to values somehow, because I can't extract the association otherwise. Both might also be associated to source.
    You should be able to use the topological sort to determine the create/delete order of tables as well"! !

!DictionaryMappingTest methodsFor: 'tests'!
testStringToObject

	| encyclopedia rowMap entryTable entries |
	encyclopedia := Encyclopedia example1.
	entries := encyclopedia entries asOrderedCollection.
	entryTable := system tableNamed: 'ENCYCENTRY'.
	rowMap := RowMap new.
"	(system descriptorFor: Encyclopedia) createRowsFor: encyclopedia in: rowMap.

	self assert: (rowMap includesRowForTable: entryTable withKey: entries first).
	self assert: rowMap size = 3."

	"So what happens here. We need to know how the rows for the associations get created. Do we treat the associations as objects (risking loss of identity issues in some dictionary implementations), create composite keys similar to many-many, or what?"! !


!DirectMappingTest methodsFor: 'support'!
setUp

	system := GlorpDemoDescriptorSystem new.
	mapping := DirectMapping from: #id to: ((system tableNamed: 'GR_CUSTOMER') fieldNamed: 'ID')! !

!DirectMappingTest methodsFor: 'tests'!
testExpressionFor
	| cust exp |
	cust := Customer new.
	cust id: 12.
	exp := mapping expressionFor: cust.
	self assert: exp rightChild class == ConstantExpression.
	self assert: exp rightChild value = 12.
	self assert: exp relation = #=.
	self assert: exp leftChild class == FieldExpression.! !


!ExpressionBasicPropertiesTest methodsFor: 'tests'!
testHasDescriptorForBase

	| exp |
	self assert: base hasDescriptor.
	exp := [:a | a ] asGlorpExpressionOn: base.
	self assert: exp hasDescriptor.! !

!ExpressionBasicPropertiesTest methodsFor: 'tests'!
testHasDescriptorForDirect

	| exp |
	exp := [:a | a id ] asGlorpExpressionOn: base.
	self deny: exp hasDescriptor.! !

!ExpressionBasicPropertiesTest methodsFor: 'tests'!
testHasDescriptorForOneToMany

	| exp |
	exp := [:a | a emailAddresses ] asGlorpExpressionOn: base.
	self assert: exp hasDescriptor.! !

!ExpressionBasicPropertiesTest methodsFor: 'tests'!
testHasDescriptorForOneToOne

	| exp |
	exp := [:a | a address ] asGlorpExpressionOn: base.
	self assert: exp hasDescriptor.! !

!ExpressionBasicPropertiesTest methodsFor: 'tests'!
testHasDescriptorForPrimaryKeyExpression

	| exp |
	exp := PrimaryKeyExpression new.
	self deny: exp hasDescriptor.! !

!ExpressionBasicPropertiesTest methodsFor: 'tests'!
testHasDescriptorForRelation

	| exp |
	exp := [:a | a = 3] asGlorpExpressionOn: base.
	self deny: exp hasDescriptor.! !

!ExpressionBasicPropertiesTest methodsFor: 'tests'!
testHasDescriptorForTwoLevelDirect

	| exp |
	exp := [:a | a address street] asGlorpExpressionOn: base.
	self deny: exp hasDescriptor.
	self assert: exp base hasDescriptor.! !

!ExpressionBasicPropertiesTest methodsFor: 'tests'!
testHasDescriptorForUninitializedBase

	self deny: BaseExpression new hasDescriptor.! !

!ExpressionBasicPropertiesTest methodsFor: 'support'!
setUp

	base := BaseExpression new descriptor: (GlorpDemoDescriptorSystem new descriptorFor: Person).! !


!ExpressionIterationTest methodsFor: 'tests'!
helpTestSingleNodeDo: exp 
	self assert: (exp collect: [:each | each]) = (OrderedCollection with: exp)! !

!ExpressionIterationTest methodsFor: 'tests'!
testDoBase
	self helpTestSingleNodeDo:BaseExpression new! !

!ExpressionIterationTest methodsFor: 'tests'!
testDoCollection

	| exp l r |
	exp := CollectionExpression new.
	l := BaseExpression new.
	r := BaseExpression new.
	exp leftChild: l; rightChild: r.
	self assert: ((exp collect: [:each | each]) = (OrderedCollection with: l with: r with: exp)).! !

!ExpressionIterationTest methodsFor: 'tests'!
testDoConstant
	self helpTestSingleNodeDo: ConstantExpression new! !

!ExpressionIterationTest methodsFor: 'tests'!
testDoField

	| exp |
	exp := FieldExpression new.
	exp field: nil base: BaseExpression new.
	self assert: ((exp collect: [:each | each]) = (OrderedCollection with: exp base with: exp)).! !

!ExpressionIterationTest methodsFor: 'tests'!
testDoMapping

	| exp |
	exp := MappingExpression new.
	exp named: 'foo' basedOn: BaseExpression new.
	self assert: ((exp collect: [:each | each]) = (OrderedCollection with: exp base with: exp)).! !

!ExpressionIterationTest methodsFor: 'tests'!
testDoParameter

	| exp |
	exp := ParameterExpression new.
	exp field: nil base: BaseExpression new.
	self assert: ((exp collect: [:each | each]) = (OrderedCollection with: exp base with: exp)).! !

!ExpressionIterationTest methodsFor: 'tests'!
testDoRelation

	| exp l r |
	exp := RelationExpression new.
	l := BaseExpression new.
	r := BaseExpression new.
	exp leftChild: l; rightChild: r.
	self assert: ((exp collect: [:each | each]) = (OrderedCollection with: l with: r with: exp)).! !

!ExpressionIterationTest methodsFor: 'tests'!
testDoTable

	| exp |
	exp := TableExpression new.
	exp table: nil base: BaseExpression new.
	self assert: ((exp collect: [:each | each]) = (OrderedCollection with: exp base with: exp)).! !

!ExpressionIterationTest methodsFor: 'tests'!
testDoWithCommonBase

	| exp l r base |
	exp := RelationExpression new.
	base := BaseExpression new.
	l := MappingExpression new.
	l named: nil basedOn: base.
	r := MappingExpression new.
	r named: nil basedOn: base.
	exp leftChild: l; rightChild: r.	
	self assert: ((exp collect: [:each | each]) = (OrderedCollection with: base with: l with: r with: exp)).! !


!ExpressionJoiningTest methodsFor: 'tests'!
join: exp 
	^exp asExpressionJoiningSource: source toTarget: target! !

!ExpressionJoiningTest methodsFor: 'tests'!
resultOfJoiningFieldFor: aTable toExpressionBuiltOn: anotherTable
	| exp table |
	base descriptor: (system descriptorFor: Customer).
	exp := FieldExpression forField: (aTable fieldNamed: 'ID')
				basedOn: BaseExpression new.
	table := base getTable: anotherTable.

	^exp asExpressionJoiningSource: base toTarget: table.! !

!ExpressionJoiningTest methodsFor: 'tests'!
testBase
	| result |
	result := self join: base. 
	self assert: result == source.! !

!ExpressionJoiningTest methodsFor: 'tests'!
testConstant
	| exp |
	exp := ConstantExpression for: 42.
	self assert: (self join: exp) == exp! !

!ExpressionJoiningTest methodsFor: 'tests'!
testField
	| exp  result |
	exp := FieldExpression forField: (DatabaseField named: 'test')
				basedOn: base.
	result := self join: exp.
	self assert: result base == source.
	self assert: result field == exp field! !

!ExpressionJoiningTest methodsFor: 'tests'!
testFieldBuiltOnDifferentTable
	| result custTable |
	custTable := system tableNamed: 'GR_CUSTOMER'.
	result := self 
		resultOfJoiningFieldFor: custTable
		toExpressionBuiltOn: custTable.
	self assert: result base == (base getTable: custTable).
	self assert: result field == ((system tableNamed: 'GR_CUSTOMER') fieldNamed: 'ID').! !

!ExpressionJoiningTest methodsFor: 'tests'!
testFieldBuiltOnSameTable
	| exp result base2 table custTable |
	system := GlorpDemoDescriptorSystem new.
	custTable := system tableNamed: 'GR_CUSTOMER'.
	base2 := BaseExpression new.
	base2 descriptor: (system descriptorFor: Customer).
	table := base2 getTable: custTable.
	exp := FieldExpression forField: (custTable fieldNamed: 'ID')
				basedOn: base.
	result := exp asExpressionJoiningSource: base2 toTarget: table.
	self assert: result base == table.
	self assert: result field == exp field.! !

!ExpressionJoiningTest methodsFor: 'tests'!
testMapping
	| result exp |
	exp := base get: #foo.
	result := self join: exp. 
	self assert: result base == source.
	self assert: result name = #foo.! !

!ExpressionJoiningTest methodsFor: 'tests'!
testParameter
	| result exp table field |
	table := DatabaseTable named: 'T'.	
	field := DatabaseField named: 'F'.
	table addField: field.
	exp := base getParameter: field.
	result := self join: exp.
	self assert: result base == source.
	self assert: result class == FieldExpression.
	self assert: result field == field.! !

!ExpressionJoiningTest methodsFor: 'tests'!
testRelation
	| result exp |
	exp := [:a | a foo = 3] asGlorpExpressionOn: base.
	result := self join: exp. 

	self assert: result class == RelationExpression.
	self assert: result rightChild == exp rightChild.
	self assert: result leftChild base == source.! !

!ExpressionJoiningTest methodsFor: 'tests'!
testRelation2
	| result exp field | 
	field := DatabaseField named: 'fred'.
	exp := [:a | a foo = field] asGlorpExpressionOn: base.
	result := self join: exp. 
	self assert: result class == RelationExpression.
	self assert: result rightChild class == FieldExpression.
	self assert: result rightChild field == field.
	self assert: result leftChild base == source.! !

!ExpressionJoiningTest methodsFor: 'tests'!
testSelfJoinWithPrimaryKeyExpression

	| pkExpression |
	pkExpression := PrimaryKeyExpression 
		from: (system tableNamed: 'GR_CUSTOMER')
		to: (system tableNamed: 'GR_CUSTOMER').
	self unfinished.! !

!ExpressionJoiningTest methodsFor: 'tests'!
testTable
	| result exp table |
	table := DatabaseTable named: 'T'.
	exp := base getTable: table.
	result := self join: exp. 
	self assert: result base == target.
	self assert: result table == table.! !

!ExpressionJoiningTest methodsFor: 'support'!
setUp

	source := BaseExpression new.
	target := source get: #relation.
	base := BaseExpression new.
	system := GlorpDemoDescriptorSystem new.! !

!ExpressionJoiningTest methodsFor: 'support'!
tearDown

	source := nil.
	target := nil.
	base := nil.
	system := nil.! !


!ExpressionTableAliasingTest methodsFor: 'tests'!
helpTestBasicAliasing: aTable 
	self deny: exp hasTableAliases.
	exp assignTableAliasesStartingAt: 1.
	self assert: exp hasTableAliases.
	self assert: exp tableAliases size = 1.
	self 
		assert: (exp tableAliases at: aTable) name 
				= 't1'! !

!ExpressionTableAliasingTest methodsFor: 'tests'!
testBase
	exp := BaseExpression new.
	exp descriptor: (system descriptorFor: Customer).
	self helpTestBasicAliasing: (system tableNamed: 'GR_CUSTOMER').! !

!ExpressionTableAliasingTest methodsFor: 'tests'!
testMapping
	| base |
	base := BaseExpression new.
	base descriptor: (system descriptorFor: Customer).
	exp := base get: 'transactions'.
	self helpTestBasicAliasing: (system tableNamed: 'BANK_TRANS').! !

!ExpressionTableAliasingTest methodsFor: 'tests'!
testTable
	| base transTable |
	base := BaseExpression new.
	base descriptor: (system descriptorFor: Customer).
	transTable := system tableNamed: 'BANK_TRANS'.
	exp := base getTable: transTable.
	self helpTestBasicAliasing: transTable! !

!ExpressionTableAliasingTest methodsFor: 'tests'!
testTableSameAsBase
	| base custTable |
	base := BaseExpression new.
	base descriptor: (system descriptorFor: Customer).
	custTable := system tableNamed: 'GR_CUSTOMER'.
	exp := base getTable: custTable.
	self deny: exp hasTableAliases.
	exp assignTableAliasesStartingAt: 1.
	base assignTableAliasesStartingAt: 42.
	self deny: exp hasTableAliases.
	self assert: (exp aliasedTableFor: custTable) name = 't42'! !

!ExpressionTableAliasingTest methodsFor: 'support'!
setUp

	system := GlorpDemoDescriptorSystem new.! !


!ExpressionTest methodsFor: 'support'!
assertIdentityOf: aBlock and: anotherBlock
	
 	| base |
	base := BaseExpression new.
	self assert: (aBlock asGlorpExpressionOn: base) == (anotherBlock asGlorpExpressionOn: base)! !

!ExpressionTest methodsFor: 'support'!
denyIdentityOf: aBlock and: anotherBlock
 
	| base |
	base := BaseExpression new.
	self deny: (aBlock asGlorpExpressionOn: base) == (anotherBlock asGlorpExpressionOn: base)! !

!ExpressionTest methodsFor: 'tests'!
testAndOperation
	| expression fred base |
	fred := 'Fred'.
	base := BaseExpression new.
	expression := [:a | (a firstName = fred) & (a firstName ~= fred)] asGlorpExpressionOn: base.
	self assert: expression class == RelationExpression.
	self assert: expression relation = #AND.! !

!ExpressionTest methodsFor: 'tests'!
testAndOperation2
	| expression fred base |
	fred := 'Fred'.
	base := BaseExpression new.
	expression := [:a | (a firstName = fred) exAnd: (a firstName ~= fred)] asGlorpExpressionOn: base.
	self assert: expression class == RelationExpression.
	self assert: expression relation = #AND.! !

!ExpressionTest methodsFor: 'tests'!
testAnySatisfy

	| expression |
	expression := [:a | a items anySatisfy: [:each | each id = 7]] asGlorpExpression.
	self assert: expression class == CollectionExpression.
	self assert: expression leftChild == expression rightChild leftChild base.! !

!ExpressionTest methodsFor: 'tests'!
testAnySatisfyPrint

	| expression system stream |
	system :=  GlorpDemoDescriptorSystem new.
	expression := [:cust | cust transactions anySatisfy: [:each | each id = 7]]
		asGlorpExpressionForDescriptor: (system descriptorFor: Customer).
	stream := WriteStream on: (String new: 100).
	expression printSQLOn: stream withParameters: Dictionary new.
	self assert: stream contents = 'BANK_TRANS.ID = 7'.! !

!ExpressionTest methodsFor: 'tests'!
testBetweenAnd
	| expression base |
	base := BaseExpression new.
	expression := [:a | a between: 3 and: 4] asGlorpExpressionOn: base.
	self assert: expression class == RelationExpression.
	self assert: expression relation = #AND.
	self assert: expression leftChild relation == #>.
	self assert: expression leftChild rightChild value == 3.
	self assert: expression rightChild relation == #<.
	self assert: expression rightChild rightChild value == 4.! !

!ExpressionTest methodsFor: 'tests'!
testEqualityOperation
	| expression fred base |
	fred := 'Fred'.
	base := BaseExpression new.
	expression := [:a | a firstName = fred] asGlorpExpressionOn: base.
	self assert: expression leftChild == (base get: #firstName).
	self assert: expression rightChild class == ConstantExpression.
	self assert: expression rightChild value == fred.
	self assert: expression relation == #=! !

!ExpressionTest methodsFor: 'tests'!
testFindingMapping

	| base baseDescriptor system|
	system := GlorpDemoDescriptorSystem new.
	baseDescriptor := system descriptorFor: BankTransaction.
	base := BaseExpression new descriptor: baseDescriptor.
	self assert: (base get: #serviceCharge) mapping == (baseDescriptor mappingForAttributeNamed: #serviceCharge).
	self assert: (base get: #serviceCharge) sourceDescriptor == (system descriptorFor: BankTransaction).
	self assert: (base get: #serviceCharge) descriptor == (system descriptorFor: ServiceCharge).! !

!ExpressionTest methodsFor: 'tests'!
testIsNullPrint

	| expression system stream |
	system :=  GlorpDemoDescriptorSystem new.
	expression := [:cust | cust id = nil]
		asGlorpExpressionForDescriptor: (system descriptorFor: Customer).
	stream := WriteStream on: (String new: 100).
	expression printSQLOn: stream withParameters: Dictionary new.
	self assert: stream contents = 'GR_CUSTOMER.ID IS NULL'.! !

!ExpressionTest methodsFor: 'tests'!
testJoinOperation
	| userExpression base expression addressTable personTable system query field1 field2 |
	system := GlorpDemoDescriptorSystem new.
	addressTable := system tableNamed: 'GR_ADDRESS'.
	personTable := system tableNamed: 'PERSON'.
	base := BaseExpression new.
	base descriptor: (system descriptorFor: Person).
	userExpression := [:aPerson | aPerson address number = 12] 
				asGlorpExpressionOn: base.
	query := SimpleQuery returningOneOf: Person where: userExpression.
	query traceNodes: #().
	query session: (Session new system: system).
	self assert: (userExpression additionalExpressionsIn: query) size = 1.
	query prepare.
	expression := query criteria.
	self
		assert: expression ultimateBaseExpression == base;
		assert: expression relation == #AND;
		assert: expression leftChild == userExpression;
		assert: expression rightChild relation == #=.
	field1 :=  expression rightChild leftChild field.
	self assert: field1 table parent == personTable.
	self assert: field1 name = 'ADDRESS_ID'.
	field2 := expression rightChild rightChild field.
	self assert: field2 table parent == addressTable.
	self assert: field2 name = 'ID'.! !

!ExpressionTest methodsFor: 'tests'!
testMappingBase

	| base |
	base := BaseExpression new.
	self assert: (base get: #someAttribute) base == base! !

!ExpressionTest methodsFor: 'tests'!
testMappingExpressionIdentity
 
	self assertIdentityOf: [:a | a someAttribute] and: [:a | a someAttribute].! !

!ExpressionTest methodsFor: 'tests'!
testMappingExpressionIdentity2

	self assertIdentityOf: [:a | a perform: #someAttribute] and: [:a | a someAttribute].! !

!ExpressionTest methodsFor: 'tests'!
testMappingExpressionIdentity3

	self assertIdentityOf: [:a | a get: #someAttribute] and: [:a | a someAttribute].! !

!ExpressionTest methodsFor: 'tests'!
testMappingExpressionIdentity4

	self denyIdentityOf: [:a | a get: #someAttribute] and: [:a | a someOtherAttribute].! !

!ExpressionTest methodsFor: 'tests'!
testNotNullPrint

	| expression system stream |
	system :=  GlorpDemoDescriptorSystem new.
	expression := [:cust | cust id ~= nil]
		asGlorpExpressionForDescriptor: (system descriptorFor: Customer).
	stream := WriteStream on: (String new: 100).
	expression printSQLOn: stream withParameters: Dictionary new.
	self assert: stream contents = 'GR_CUSTOMER.ID IS NOT NULL'.! !

!ExpressionTest methodsFor: 'tests'!
testOrOperation
	| expression fred base |
	fred := 'Fred'.
	base := BaseExpression new.
	expression := [:a | (a firstName = fred) | (a firstName ~= fred)] asGlorpExpressionOn: base.
	self assert: expression class == RelationExpression.
	self assert: expression relation = #OR.! !

!ExpressionTest methodsFor: 'tests'!
testOrOperation2
	| expression fred base |
	fred := 'Fred'.
	base := BaseExpression new.
	expression := [:a | (a firstName = fred) exOr: (a firstName ~= fred)] asGlorpExpressionOn: base.
	self assert: expression class == RelationExpression.
	self assert: expression relation = #OR.! !

!ExpressionTest methodsFor: 'tests'!
testTwoLevelMappingExpressionIdentity

	self assertIdentityOf: [:a | a someAttribute someAttribute] and: [:a | a someAttribute someAttribute].
	self denyIdentityOf: [:a | a someAttribute someAttribute] and: [:a | a someAttribute].! !


!InsertUpdateTest methodsFor: 'support'!
rowFor: anObject

	| rowMap rows |
	rowMap := RowMap new.
	session createRowsFor: anObject in: rowMap.
	rows := rowMap rowsForKey: anObject.
	self assert: rows size = 1.
	^rows first.! !

!InsertUpdateTest methodsFor: 'support'!
setUp
	super setUp.
	session := SessionResource current newSession.! !

!InsertUpdateTest methodsFor: 'tests'!
testFunctionalInsertUpdateForInsert

	| testObject |
	[session beginTransaction.
	session beginUnitOfWork.
	testObject := Customer example1.
	testObject id: 876.
	session register: testObject.
	session commitUnitOfWork.
	self assert: (testObject seenPreWrite = true).
	self assert: (testObject seenPostWrite = true).

	session beginUnitOfWork.
	session register: testObject.
	testObject name: 'Change of name'.
	session commitUnitOfWork]
		ensure: [session rollbackTransaction]! !

!InsertUpdateTest methodsFor: 'tests'!
testRowOwnership

	| aCustomer rowMap |
	aCustomer := Customer new.
	rowMap := RowMap new.
	(session descriptorFor: Customer) createRowsFor: aCustomer in: rowMap.
	rowMap rowsDo: [:each | 
		self assert: each owner = aCustomer].! !

!InsertUpdateTest methodsFor: 'tests'!
testShouldInsertForInsert

	| testObject row |
	testObject := Customer example1.
	testObject id: 876.
	row :=self rowFor: testObject.
	self assert: (session shouldInsert: row).! !

!InsertUpdateTest methodsFor: 'tests'!
testShouldInsertForUpdate

	| testObject row |
	session beginUnitOfWork.
	testObject := Customer example1.
	testObject id: 876.
	session cacheAt: 876 put: testObject.
	row :=self rowFor: testObject.
	self deny: (session shouldInsert: row).! !


!InsertUpdateTest class methodsFor: 'resources'!
resources

	^Array with: SessionResource.! !


!MappingTest methodsFor: 'tests'!
helperForMergedOneToOneReversingWriteOrder: aBoolean 
	| account accountTable row |
	account := GlorpBankExampleSystem new objectNumber: 1 ofClass: BankAccount.
	accountTable := system tableNamed: 'BANK_ACCT'.
	aBoolean 
		ifTrue: 
			[self write: account.
			self write: account accountNumber]
		ifFalse: 
			[self write: account accountNumber.
			self write: account].
	self assert: (rowMap includesRowForTable: accountTable withKey: account).
	self assert: (rowMap includesRowForTable: accountTable
				withKey: account accountNumber).
	row := self rowFor: account.
	self assert: (row at: (accountTable fieldNamed: 'ID')) = account id.
	(Array with: row with: (self rowFor: account accountNumber)) do: 
			[:each | 
			self assert: (each at: (accountTable fieldNamed: 'BANK_CODE')) 
						= account accountNumber bankCode.
			self assert: (each at: (accountTable fieldNamed: 'BRANCH_NO')) 
						= account accountNumber branchNumber.
			self assert: (each at: (accountTable fieldNamed: 'ACCT_NO')) 
						= account accountNumber accountNumber].
	self assert: (rowMap numberOfEntriesForTable: accountTable) = 2! !

!MappingTest methodsFor: 'tests'!
helperForNestedMergedOneToOneReversingWriteOrder: aBoolean 
	| trans transTable moneyTable row fieldNames fieldValues |
	trans := GlorpBankExampleSystem new objectNumber: 1
				ofClass: BankTransaction.
	transTable := system tableNamed: 'BANK_TRANS'.
	moneyTable := system tableNamed: 'MONEY_IMAGINARY_TABLE'.
	aBoolean 
		ifTrue: 
			[self write: trans.
			self write: trans amount.
			self write: trans serviceCharge.
			self write: trans serviceCharge amount]
		ifFalse: 
			[self write: trans serviceCharge amount.
			self write: trans serviceCharge.
			self write: trans amount.
			self write: trans].
	self 
		assert: (rowMap rowForTable: transTable withKey: trans) shouldBeWritten.
	self 
		assert: (rowMap rowForTable: transTable withKey: trans serviceCharge) 
				shouldBeWritten not.
	self 
		assert: (rowMap rowForTable: moneyTable withKey: trans amount) 
				shouldBeWritten not.
	self 
		assert: (rowMap rowForTable: moneyTable withKey: trans serviceCharge amount) 
				shouldBeWritten not.
	row := self rowFor: trans.
	self assert: (row at: (transTable fieldNamed: 'ID')) = trans id.
	fieldNames := #('AMT_CURR' 'AMT_AMT' 'SRVC_DESC' 'SRVC_AMT_CURR' 'SRVC_AMT_AMT').
	fieldValues := (Array 
				with: trans amount currency asString
				with: trans amount amount
				with: trans serviceCharge description) 
					, (Array with: trans serviceCharge amount currency asString
							with: trans serviceCharge amount amount).
	fieldNames with: fieldValues
		do: [:fieldName :value | self assert: (row at: (transTable fieldNamed: fieldName)) = value].
	self assert: (rowMap numberOfEntriesForTable: transTable) = 2.
	self assert: (rowMap numberOfEntriesForTable: moneyTable) = 2! !

!MappingTest methodsFor: 'tests'!
testManyToMany

	| customer customerTable accountTable linkTable linkRow |
	customer := GlorpBankExampleSystem new objectNumber: 1 ofClass: Customer.
	rowMap := RowMap new.
	customerTable := system tableNamed: 'GR_CUSTOMER'.
	accountTable := system tableNamed: 'BANK_ACCT'.
	linkTable := system tableNamed: 'CUSTOMER_ACCT_LINK'.

	self write: customer.
	customer accounts do: [:each | 
		self write: each].

	self assert: (rowMap includesRowForTable: customerTable withKey: customer).
	customer accounts do: [:each |
		self assert: (rowMap includesRowForTable: accountTable withKey: each).
		self assert: (rowMap includesRowForTable: linkTable withKey: (RowMapKey new key1: customer; key2: each))].

	customer accounts do: [:each | | rowMapKey |
		self assert: ((self rowFor: each) at: (accountTable fieldNamed: 'ID')) = each id.
		rowMapKey := RowMapKey new key1: customer; key2: each.
		linkRow := rowMap rowForTable: linkTable withKey: rowMapKey.
		self assert: (linkRow at: (linkTable fieldNamed: 'ACCT_ID')) = each id.
		self assert: (linkRow at: (linkTable fieldNamed: 'CUSTOMER_ID')) = customer id.
		].
	self assert: ((self rowFor: customer) at: (customerTable fieldNamed: 'ID')) = customer id.
	self assert: ((rowMap numberOfEntriesForTable: linkTable) = 2).
	self assert: ((rowMap numberOfEntriesForTable: customerTable) = 1).! !

!MappingTest methodsFor: 'tests'!
testMergedOneToOne
	self helperForMergedOneToOneReversingWriteOrder: false! !

!MappingTest methodsFor: 'tests'!
testMergedOneToOneReversingWrites
	self helperForMergedOneToOneReversingWriteOrder: true! !

!MappingTest methodsFor: 'tests'!
testMissingDescriptor
	self assert: (system descriptorFor: nil) isNil.
	self assert: (system descriptorFor: UndefinedObject) isNil.
	self assert: (system descriptorFor: 3) isNil! !

!MappingTest methodsFor: 'tests'!
testMultipleTableCreation
	
	| descriptor table passenger table2 row1 row2 |
	descriptor := system descriptorFor: Passenger.
	passenger := Passenger example1.
	rowMap := RowMap new.
	table := system existingTableNamed: 'PASSENGER'.
	table2 := system existingTableNamed: 'FREQUENT_FLYER'.
	descriptor createRowsFor: passenger in: rowMap.
	self assert: (rowMap includesRowForTable: table withKey: passenger).
	self assert: (rowMap includesRowForTable: table2 withKey: passenger).
	row1 := rowMap rowForTable: table withKey: passenger.
	self assert: (row1 at: (table fieldNamed: 'ID'))
			= passenger id.
	self assert: (row1 at: (table fieldNamed: 'NAME'))
			= passenger name.
	row2 := rowMap rowForTable: table2 withKey: passenger.
	self assert: (row2 at: (table2 fieldNamed: 'ID'))
			= passenger id.
	self assert: (row2 at: (table2 fieldNamed: 'POINTS')) = passenger frequentFlyerPoints.
	self assert: rowMap numberOfEntries = 2! !

!MappingTest methodsFor: 'tests'!
testNestedMergedOneToOne
	self helperForNestedMergedOneToOneReversingWriteOrder: false! !

!MappingTest methodsFor: 'tests'!
testNestedMergedOneToOneReversingWriteOrder
	self helperForNestedMergedOneToOneReversingWriteOrder: true.! !

!MappingTest methodsFor: 'tests'!
testNilOneToOne
	
	| person  personTable addressTable |
	person := Person example1.
	person address: nil.
	self write: person.
	self write: person address.
	personTable := system existingTableNamed: 'PERSON'.
	addressTable := system existingTableNamed: 'GR_ADDRESS'.

	self assert: (rowMap includesRowForTable: personTable withKey: person).
	self deny: (rowMap includesRowForTable: addressTable withKey: person address).

	self assert: rowMap numberOfEntries = 1.! !

!MappingTest methodsFor: 'tests'!
testOneToMany

	| customer customerTable transactionTable |
	customer := Customer example1.
	rowMap := RowMap new.
	customerTable := system tableNamed: 'GR_CUSTOMER'.
	transactionTable := system tableNamed: 'BANK_TRANS'.
	self write: customer.
	customer transactions do: [:each |
		self write: each].

	self assert: (rowMap includesRowForTable: customerTable withKey: customer).
	customer transactions do: [:each |
		self assert: (rowMap includesRowForTable: transactionTable withKey: each)].

	customer transactions do: [:each |
		self assert: ((self rowFor: each) at: (transactionTable fieldNamed: 'OWNER_ID')) = customer id].
	self assert: ((self rowFor: customer) at: (customerTable fieldNamed: 'ID')) = customer id.! !

!MappingTest methodsFor: 'tests'!
testOneToOne
	
	| person  personTable addressTable |
	person := Person example1.
	self write: person.
	self write: person address.
	personTable := system existingTableNamed: 'PERSON'.
	addressTable := system existingTableNamed: 'GR_ADDRESS'.

	self assert: (rowMap includesRowForTable: personTable withKey: person).
	self assert: (rowMap includesRowForTable: addressTable withKey: person address).

	self assert: ((self rowFor: person address) at: (addressTable fieldNamed: 'ID')) = person address id.
	self assert: ((self rowFor: person) at: (personTable fieldNamed: 'ADDRESS_ID')) = person address id.
	self assert: rowMap numberOfEntries = 2.! !

!MappingTest methodsFor: 'tests'!
testOneToOneWithProxy

	| person  personTable addressTable proxy stub |
	person := Person example1.
	proxy := Proxy new.
	proxy session: Session new.
	stub := QueryStub returningOneOf: Address where: [:address | address id = 1].
	stub result: person address.
	proxy query: stub.
	person address: proxy.
	self deny: person address isInstantiated.

	self write: person.

	personTable := system existingTableNamed: 'PERSON'.
	addressTable := system existingTableNamed: 'GR_ADDRESS'.

	self assert: (rowMap includesRowForTable: personTable withKey: person).
	self deny: (rowMap includesRowForTable: addressTable withKey: person address).

	self deny: ((self rowFor: person) hasValueFor: (personTable fieldNamed: 'ADDRESS_ID')).
	self assert: rowMap numberOfEntries = 1.! !

!MappingTest methodsFor: 'tests'!
testRowCreation

	| descriptor person row table  |
	descriptor := system descriptorFor: Person.
	person := Person example1.
	rowMap := RowMap new.
	table := system existingTableNamed: 'PERSON'.
	descriptor createRowsFor: person in: rowMap.
	self assert: (rowMap includesRowForTable: table withKey: person).
	row := rowMap rowForTable: table withKey: person.
	self assert: (row at: (table fieldNamed: 'ID')) = person id.
	self assert: (row at: (table fieldNamed: 'NAME')) = person name.
	self assert: rowMap numberOfEntries = 2.! !

!MappingTest methodsFor: 'support'!
rowFor: anObject
	| descriptor |
	descriptor := system descriptorFor: anObject.
	descriptor isNil ifTrue: [^nil].

	^rowMap rowForTable: descriptor table withKey: anObject.! !

!MappingTest methodsFor: 'support'!
setUp
	super setUp.
	system := GlorpDemoDescriptorSystem new.
	rowMap := RowMap new.! !

!MappingTest methodsFor: 'support'!
write: anObject

	| descriptor |
	descriptor := system descriptorFor: anObject. 
	descriptor isNil ifTrue: [^self].
	descriptor createRowsFor: anObject in: rowMap.! !


!MessageCollectorTest methodsFor: 'support'!
setUp
	super setUp.
	collector := MessageArchiver new! !

!MessageCollectorTest methodsFor: 'tests'!
dNUException
	^Dialect isVisualAge 
		ifTrue: [(Smalltalk at: #SystemExceptions) at: 'ExAll']
		ifFalse: [MessageNotUnderstood].! !

!MessageCollectorTest methodsFor: 'tests'!
testExpressionCreation
	| exp |
	exp := collector foo asGlorpExpression.
	self assert: exp name == #foo.
	self assert: exp base class == BaseExpression! !

!MessageCollectorTest methodsFor: 'tests'!
testMessageCollectorDNU
	| message caught |
	message := Message selector: #foo arguments: #().
	caught := false.
	[collector basicDoesNotUnderstand: message] on: self dNUException
		do: [:signal | caught := true. signal sunitExitWith: nil].
	self assert: caught! !

!MessageCollectorTest methodsFor: 'tests'!
testMessageIntercept

	| foo |
	foo := collector foo.
	self assert: foo privateGlorpMessage selector == #foo.
	self assert: foo privateGlorpReceiver == collector.! !


!ObjectTransactionTest methodsFor: 'support'!
setUp

	transaction := ObjectTransaction new! !

!ObjectTransactionTest methodsFor: 'tests'!
testArray
	| object |
	object := #(1 2 3 4 5) copy.
	transaction begin.
	transaction register: object.
	object
		at: 1 put: #one;
		at: 2 put: object.
	transaction abort.
	self
		assert: (object at: 1) == 1;
		assert: (object at: 2) == 2! !

!ObjectTransactionTest methodsFor: 'tests'!
testBecome
	| object |
	object := 'hello'.
	transaction begin.
	transaction register: object.
	object become: Set new.
	transaction abort.
	self 
		assert: object class == '' class;
		assert: object = 'hello'.! !

!ObjectTransactionTest methodsFor: 'tests'!
testCommit
	| array |
	array := #(1 2 3 4 5) copy.
	transaction begin.
	transaction register: array.
	array
		at: 1 put: #one;
		at: 2 put: array.
	transaction commit.
	self
		assert: (array at: 1) == #one;
		assert: (array at: 2) == array! !

!ObjectTransactionTest methodsFor: 'tests'!
testHashedCollection
	| object originalMembers |
	object := Set new.
	originalMembers := #(one two three 'four' 5 'vi' (1 2 3 4 5 6 7)) collect: [:each | each copy].
	object addAll: originalMembers.
	transaction begin.
	transaction register: object.
	object remove: #one; remove: (originalMembers at: 4).
	object add: 1.
	originalMembers last at: 7 put: 'seven'.
	transaction abort.
	self
		assert: object size = originalMembers size;
		assert: (object includes: originalMembers first);
		assert: (object includes: (originalMembers at: 4));
		assert: object size = (object rehash; size).
	originalMembers do: [:each | self assert: (object includes: each)]! !

!ObjectTransactionTest methodsFor: 'tests'!
testString
	| object |
	object := 'Hello, World!!' copy.
	transaction begin.
	transaction register: object.
	object
		at: 1 put: $h;
		at: 2 put: $E.
	transaction abort.
	self
		assert: object first  == $H;
		assert: (object at: 2) == $e! !


!PrimaryKeyExpressionTest methodsFor: 'support'!
setUp

	system := GlorpDemoDescriptorSystem new.
	expression := PrimaryKeyExpression 
		from: ((system tableNamed: 'BANK_ACCT') fieldNamed: 'ID')
		to: ((system tableNamed: 'CUSTOMER_ACCT_LINK') fieldNamed: 'ACCT_ID').

	compoundExpression := PrimaryKeyExpression
		from: ((system tableNamed: 'BANK_ACCT') fieldNamed: 'ID')
		to: ((system tableNamed: 'PERSON') fieldNamed: 'NAME').
	compoundExpression
		addSource: ((system tableNamed: 'BANK_ACCT') fieldNamed: 'BANK_CODE')
		target: ((system tableNamed: 'PERSON') fieldNamed: 'ADDRESS_ID').! !

!PrimaryKeyExpressionTest methodsFor: 'tests'!
testAsExpressionCompound
	
	| e personTable accountTable |
	personTable := system tableNamed: 'PERSON'.
	accountTable := system tableNamed: 'BANK_ACCT'.

	e := compoundExpression asExpression.
	self assert: (e isKindOf: RelationExpression).
	self assert: e relation == #AND.
	self assert: (e leftChild isKindOf: RelationExpression).
	self assert: (e leftChild relation == #=).
	self assert: (e leftChild leftChild isKindOf: FieldExpression).
	self assert: e leftChild leftChild field == (personTable fieldNamed: 'NAME').
	self assert: (e leftChild rightChild isKindOf: ParameterExpression).
	self assert: e leftChild rightChild field == (accountTable fieldNamed: 'ID').


	self assert: (e rightChild isKindOf: RelationExpression).
	self assert: (e rightChild relation == #=).
	self assert: (e rightChild leftChild isKindOf: FieldExpression).
	self assert: e rightChild leftChild field == (personTable fieldNamed: 'ADDRESS_ID').
	self assert: (e rightChild rightChild isKindOf: ParameterExpression).
	self assert: e rightChild rightChild field = (accountTable fieldNamed: 'BANK_CODE').! !

!PrimaryKeyExpressionTest methodsFor: 'tests'!
testAsExpressionSingle
	
	| e field param |
	e := expression asExpression.
	self assert: (e isKindOf: RelationExpression).
	self assert: e relation == #=.
	field := e leftChild.
	self assert: (field isKindOf: FieldExpression).
	self assert: field field == ((system tableNamed: 'CUSTOMER_ACCT_LINK') fieldNamed: 'ACCT_ID').
	param := e rightChild.
	self assert: (param isKindOf: ParameterExpression).
	self assert: param field == ((system tableNamed: 'BANK_ACCT') fieldNamed: 'ID').! !

!PrimaryKeyExpressionTest methodsFor: 'tests'!
testCompoundSQLPrinting
	| stream params|
	stream := WriteStream on: (String new: 100).
	params := Dictionary new.
	params at: ((system tableNamed: 'BANK_ACCT') fieldNamed: 'ID') put: 1.
	params at: ((system tableNamed: 'BANK_ACCT') fieldNamed: 'BANK_CODE') put: 3.
	compoundExpression printSQLOn: stream withParameters: params.
	self assert: stream contents = 'PERSON.NAME = 1 AND PERSON.ADDRESS_ID = 3'.! !

!PrimaryKeyExpressionTest methodsFor: 'tests'!
testCreation

	self assert: expression allSourceFields size = 1.
	self assert: expression allSourceFields first == ((system tableNamed: 'BANK_ACCT') fieldNamed: 'ID').! !

!PrimaryKeyExpressionTest methodsFor: 'tests'!
testParameterCount

	self assert: expression numberOfParameters = 1.
	self assert: compoundExpression numberOfParameters = 2.! !

!PrimaryKeyExpressionTest methodsFor: 'tests'!
testSQLPrinting
	| stream params |
	stream := WriteStream on: (String new: 100).
	params := Dictionary new.
	params at: ((system tableNamed: 'BANK_ACCT') fieldNamed: 'ID') put: 'abc'.
	expression printSQLOn: stream withParameters: params.
	self assert: stream contents = 'CUSTOMER_ACCT_LINK.ACCT_ID = ''abc'''! !


!ProxyTest methodsFor: 'tests'!
testCreation

	| proxy |
	proxy := Proxy new.
	self deny: proxy isInstantiated.! !

!ProxyTest methodsFor: 'tests'!
testInstantiationFromStub

	| proxy stub |

	proxy := Proxy new.
	proxy session: session.
	stub := QueryStub returningOneOf: Address where: [:address | address id = 1].
	stub result: 42.
	proxy query: stub.
	proxy parameters: #().

	self assert: (proxy getValue notNil).
	self assert: proxy getValue = 42.
	self assert: proxy isInstantiated.! !

!ProxyTest methodsFor: 'support'!
setUp

	session := SessionResource current newSession.! !


!ProxyTest class methodsFor: 'resources'!
resources

	^Array with: SessionResource.! !


!QueryTableAliasingTest methodsFor: 'tests'!
testAliasWithEmbeddedMapping

	self unfinished.! !

!QueryTableAliasingTest methodsFor: 'tests'!
testBuildingObject

	| customer |
	elementBuilder instance: Customer new.
	elementBuilder requiresPopulating: true.
	elementBuilder buildObjectFrom: #(12 'Name').
	customer := elementBuilder instance.
	self assert: customer class == Customer.
	self assert: customer id = 12.
	self assert: customer name = 'Name'.! !

!QueryTableAliasingTest methodsFor: 'tests'!
testElementBuilderFields
	elementBuilder fieldsForSelectStatement 
		do: [:each | self assert: each table name = 't1']! !

!QueryTableAliasingTest methodsFor: 'tests'!
testExpressionTableAlias
	| fields |
	fields := expression translateFields: expression descriptor mappedFields.
	fields do: [:each | self assert: each table name = 't1']! !

!QueryTableAliasingTest methodsFor: 'tests'!
testQueryPrintingFields
	| stream |
	query 
		initResultClass: Customer
		criteria: expression
		singleObject: true.
	query traceNodes: expression.
	query computeFields.
	stream := String new writeStream.
	query printSelectFieldsOn: stream.
	self assert: stream contents = 't1.ID, t1.NAME'! !

!QueryTableAliasingTest methodsFor: 'tests'!
testQueryPrintingSimpleWhereClause
	| string |
	string := self helpTestPrintingWhereClause: ((expression get: #name) get: #= withArguments: #('Fred')).
	self assert: string = 't1.NAME = ''Fred'''! !

!QueryTableAliasingTest methodsFor: 'tests'!
testQueryPrintingTables
	| stream string |
	query 
		initResultClass: Customer
		criteria: expression
		singleObject: true.
	query traceNodes: expression.
	query computeFields.
	stream := String new writeStream.
	query printTablesOn: stream.
	string := stream contents.
	self assert: string = 'GR_CUSTOMER t1'! !

!QueryTableAliasingTest methodsFor: 'support'!
helpTestPrintingWhereClause: anExpression
	| stream |
	query 
		initResultClass: Customer
		criteria: expression
		singleObject: true.
	query traceNodes: expression.
	query computeFields.
	stream := String new writeStream.
	anExpression	
		printSQLOn: stream
		withParameters: Dictionary new.
	^stream contents.! !

!QueryTableAliasingTest methodsFor: 'support'!
setUp
	query := SimpleQuery new.
	expression := BaseExpression new.
	system := GlorpDemoDescriptorSystem new.
	expression descriptor: (system descriptorFor: Customer).
	elementBuilder := ElementBuilder for: expression in: query.
	expression aliasTable: (system tableNamed: 'GR_CUSTOMER') to: 't1'! !


!ReadQueryTest methodsFor: 'support'!
setUp

	session := SessionResource current newSession.! !

!ReadQueryTest methodsFor: 'tests'!
testCriteriaSetup
	| query |
	query := ReadQuery returningOneOf: Address where: [:each | each id = 12].
	query session: session.
	query setUpCriteria.
	self assert: query criteria class == RelationExpression.
	self assert: query criteria ultimateBaseExpression descriptor 
				== (session descriptorFor: Address)! !


!ReadQueryTest class methodsFor: 'resources'!
resources

	^Array with: SessionResource.! !


!ReadingTest methodsFor: 'tests'!
helperForTestReadEmbeddedOneToOne
	| transRow query result |
	
	[session beginTransaction.
	transRow := session system exampleBankTransactionRow.
	session writeRow: transRow.
	query := ReadQuery returningOneOf: BankTransaction
				where: [:each | each id = 1].
	result := query executeIn: session] 
			ensure: [session rollbackTransaction].
	self assert: result serviceCharge notNil.
	self assert: result serviceCharge description = 'additional overcharge'.
	self assert: result amount currency = 'CDN'.
	self assert: result amount amount = '7'.
	self assert: result serviceCharge amount currency = 'USD'.
	self assert: result serviceCharge amount amount = '2'.
	^result.! !

!ReadingTest methodsFor: 'tests'!
readPersonWithAddressForExpression: aBlock 
	| object query results personRow addressRow |
	
	[session beginTransaction.
	addressRow := session system exampleAddressRow.
	session writeRow: addressRow.
	personRow := session system examplePersonRow1.
	session writeRow: personRow.
	query := ReadQuery returningManyOf: Person where: aBlock.
	results := query executeIn: session.
	self assert: results size = 1.
	object := results first.
	self assert: object class = Person.
	self assert: object id = 3.
	self assert: object name = 'aPerson'.
	self assert: object address class == Proxy.
	self assert: object address getValue id = 123.
	self assert: object address getValue class == Address] 
			ensure: [session rollbackTransaction]! !

!ReadingTest methodsFor: 'tests'!
testBuildBankTransactionAndDependentsFromRow
	| transactionDescriptor object row moneyDescriptor money1 table translations |
	transactionDescriptor := system descriptorFor: BankTransaction.
	object := BankTransaction new.
	row := #(99 nil 'CDN' 98 'service charge' 'USD' 97).
	transactionDescriptor 
		populateObject: object
		fromRow: row
		inBuilder: ElementBuilder new.
	self assert: object id = 99.
	moneyDescriptor := system descriptorFor: GlorpMoney.
	money1 := GlorpMoney new.
	table := system tableNamed: 'MONEY_IMAGINARY_TABLE'.
	translations := IdentityDictionary new.
	translations at: (table fieldNamed: 'CURRENCY') put: 3.
	translations at: (table fieldNamed: 'AMOUNT') put: 4.
	moneyDescriptor 
		populateObject: money1
		fromRow: row
		inBuilder: (ElementBuilder new fieldTranslations: translations).
	self assert: money1 amount = 98.
	self assert: money1 currency = 'CDN'! !

!ReadingTest methodsFor: 'tests'!
testBuildPersonFromRow
	| descriptor object address |
	address := Address new.
	session cacheAt: 127 put: address.
	descriptor := system descriptorFor: Person.
	system tableNamed: 'PERSON'.
	object := Person new.
	descriptor 
		populateObject: object
		fromRow: #(456 'Ralph' 127)
		inBuilder: ElementBuilder new.
	self assert: object class = Person.
	self assert: object id = 456.
	self assert: object name = 'Ralph'.
	self assert: object address getValue == address! !

!ReadingTest methodsFor: 'tests'!
testNonRefreshAddress
	"Test that if we don't set the refresh flag on the query we don't re-read the data"
	| query rowToWrite address modifiedRow |
	
	[session beginTransaction.
	rowToWrite := session system exampleAddressRow.
	session writeRow: rowToWrite.
	address := session readOneOf: Address
				where: [:each | each id = 123].
	modifiedRow := session system exampleModifiedAddressRow.
	modifiedRow owner: address. "Otherwise it thinks it's an insert"
	session writeRow: modifiedRow.
	query := Query returningOneOf: Address where: [:each | each id = 123].
	query executeIn: session.
	self assert: address street = 'Paseo Montril'.] 
			ensure: [session rollbackTransaction].! !

!ReadingTest methodsFor: 'tests'!
testReadAccountsWithMultipleAnySatisfy
	| query result |
	
	[| block |
	session beginTransaction.
	self writeCustomer1Rows.
	block := 
			[:account | 
			(account accountHolders anySatisfy: [:each | each id = 24]) 
				| (account accountHolders anySatisfy: [:each | each id = 27])].
	query := ReadQuery returningManyOf: BankAccount where: block.
	result := session execute: query.
	self assert: result size = 2] 
			ensure: [session rollbackTransaction]! !

!ReadingTest methodsFor: 'tests'!
testReadAddress
	| object query results rowToWrite |
	
	[session beginTransaction.
	rowToWrite := session system exampleAddressRow.
	session writeRow: rowToWrite.
	query := ReadQuery returningManyOf: Address
				where: [:address | address id = 123].
	results := query executeIn: session] 
			ensure: [session rollbackTransaction].
	self assert: results size = 1.
	object := results first.
	self assert: object class = Address.
	self assert: object id = 123.
	self assert: object street = 'Paseo Montril'.
	self assert: object number = '10185'! !

!ReadingTest methodsFor: 'tests'!
testReadAddressProxy
	| object query results rowToWrite proxy |

	[session beginTransaction.
		rowToWrite := session system exampleAddressRow.
		session writeRow: rowToWrite.
		query := (Query
			returningManyOf: Address
			where: ([:address | address id = 123]) ) returnProxies: true.
		results := query executeIn: session.
		self assert: (results size = 1).
		proxy := results first.
		object := proxy getValue]
	ensure: [session rollbackTransaction].

	self assert: (proxy class = Proxy).
	self assert: (object class = Address).
	self assert: (object id = 123).
	self assert: (object street = 'Paseo Montril').
	self assert: (object number = '10185').! !

!ReadingTest methodsFor: 'tests'!
testReadAddressProxyAlreadyInMemory
	"Check that if the object is already in memory we don't create a proxy for it, just return the instance"
	| object query results rowToWrite |

	[session beginTransaction.
		rowToWrite := session system exampleAddressRow.
		session writeRow: rowToWrite.
		session readOneOf: Address where: [:address | address id = 123].
		query := (Query
			returningManyOf: Address
			where: ([:address | address id = 123]) ) returnProxies: true.
		results := query executeIn: session.
		self assert: (results size = 1).
		object := results first]
	ensure: [session rollbackTransaction].

	self assert: (object class = Address).
	self assert: (object id = 123).
	self assert: (object street = 'Paseo Montril').
	self assert: (object number = '10185').! !

!ReadingTest methodsFor: 'tests'!
testReadCustomerWithAccounts
	| query id1 id2 result accounts backRef1 backRef2 accountIds |
	
	[session beginTransaction.
	accountIds := self writeCustomer1Rows.
	id1 := accountIds at: 1.
	id2 := accountIds at: 2.
	query := ReadQuery returningOneOf: Customer
				where: [:person | person id = 27].
	result := session execute: query.
	self assert: result seenPostFetch = true.
	accounts := result accounts getValue.
	self assert: accounts size = 2.
	self assert: (accounts first id = id1 or: [accounts last id = id1]).
	self assert: (accounts first id = id2 or: [accounts last id = id2]).
	self assert: accounts first id ~= accounts last id.
	backRef1 := accounts first accountHolders getValue.
	self assert: backRef1 size = 1.
	self assert: backRef1 first = result.
	backRef2 := accounts first accountHolders getValue.
	self assert: backRef2 size = 1.
	self assert: backRef2 first = result] 
			ensure: [session rollbackTransaction]! !

!ReadingTest methodsFor: 'tests'!
testReadCustomerWithAnySatisfy
	| query result accounts |
	
	[session beginTransaction.
	self writeCustomer1Rows.
	query := ReadQuery returningManyOf: Customer
				where: 
					[:person | 
					person accounts anySatisfy: [:each | each accountNumber branchNumber > 0]].
	result := session execute: query.
	self assert: result size = 1.
	accounts := result first accounts getValue.
	self assert: accounts size = 2.
	query := ReadQuery returningManyOf: Customer
				where: 
					[:person | 
					person accounts anySatisfy: [:each | each accountNumber branchNumber = 2]].
	result := session execute: query.
	self assert: result size = 1.
	accounts := result first accounts getValue.
	self assert: accounts size = 2] 
			ensure: [session rollbackTransaction]! !

!ReadingTest methodsFor: 'tests'!
testReadCustomerWithMultipleAnySatisfy
	| query result |
	
	[| block |
	session beginTransaction.
	self writeCustomer1Rows.
	block := 
			[:person | 
			(person accounts anySatisfy: [:each | each accountNumber branchNumber = 2]) 
				& (person accounts 
						anySatisfy: [:each | each accountNumber branchNumber = 3])].
	query := ReadQuery returningManyOf: Customer where: block.
	result := session execute: query.
	self assert: result size = 1] 
			ensure: [session rollbackTransaction]! !

!ReadingTest methodsFor: 'tests'!
testReadEmbeddedOneToOne

	self helperForTestReadEmbeddedOneToOne.! !

!ReadingTest methodsFor: 'tests'!
testReadPassenger
	| passengerRow1 passengerRow2 query result |
	
	[session beginTransaction.
	passengerRow1 := session system examplePassengerRow.
	session writeRow: passengerRow1.
	passengerRow2 := session system exampleFrequentFlyerRow.
	session writeRow: passengerRow2.
	query := ReadQuery returningOneOf: Passenger
				where: [:passenger | passenger id = 1].
	result := query executeIn: session.
	self assert: result id = 1.
	self assert: result name = 'Some Passenger'.
	self assert: result frequentFlyerPoints = 10000] 
			ensure: [session rollbackTransaction]! !

!ReadingTest methodsFor: 'tests'!
testReadPersonWithAddress
	self readPersonWithAddressForExpression: [:pers | pers id = 3]! !

!ReadingTest methodsFor: 'tests'!
testReadPersonWithEmailAddresses
	| query personRow addressRow emailAddress1Row emailAddress2Row id1 id2 result emailAddresses |
	
	[session beginTransaction.
	addressRow := session system exampleAddressRow.
	session writeRow: addressRow.
	personRow := session system examplePersonRow1.
	session writeRow: personRow.
	emailAddress1Row := session system exampleEmailAddressRow1.
	emailAddress2Row := session system exampleEmailAddressRow2.
	id1 := emailAddress1Row at: (emailAddress1Row table fieldNamed: 'ID').
	id2 := emailAddress2Row at: (emailAddress2Row table fieldNamed: 'ID').
	session writeRow: emailAddress1Row.
	session writeRow: emailAddress2Row.
	query := ReadQuery returningOneOf: Person where: [:person | person id = 3].
	result := query executeIn: session.
	emailAddresses := result emailAddresses getValue.
	self assert: emailAddresses size = 2.
	self 
		assert: (emailAddresses first id = id1 or: [emailAddresses last id = id1]).
	self 
		assert: (emailAddresses first id = id2 or: [emailAddresses last id = id2]).
	self assert: emailAddresses first id ~= emailAddresses last id] 
			ensure: [session rollbackTransaction]! !

!ReadingTest methodsFor: 'tests'!
testReadPersonWithJoinToAddress
	self 
		readPersonWithAddressForExpression: [:person | person address street = 'Paseo Montril']! !

!ReadingTest methodsFor: 'tests'!
testReadWithCacheHits
	| query addressRow result1 result2 |
	
	[session beginTransaction.
	addressRow := session system exampleAddressRow.
	session writeRow: addressRow.
	query := ReadQuery returningOneOf: Address
				where: [:address | address id = 123].
	result1 := query executeIn: session.
	result2 := query executeIn: session.
	self assert: result1 == result2] 
			ensure: [session rollbackTransaction]! !

!ReadingTest methodsFor: 'tests'!
testRefreshAddress
	"Check that we refresh correctly when the refresh flag is set"
	| query rowToWrite address modifiedRow |
	
	[session beginTransaction.
	rowToWrite := session system exampleAddressRow.
	session writeRow: rowToWrite.
	address := session readOneOf: Address
				where: [:each | each id = 123].
	modifiedRow := session system exampleModifiedAddressRow.
	modifiedRow owner: address. "Otherwise it thinks it's an insert"
	session writeRow: modifiedRow.
	query := Query returningOneOf: Address where: [:each | each id = 123].
	query shouldRefresh: true.
	query executeIn: session.
	self assert: address street = 'Something Else'.] 
			ensure: [session rollbackTransaction].! !

!ReadingTest methodsFor: 'tests'!
testRegisteringWithEmbeddedMapping

	| bankTrans |
	session beginUnitOfWork.
	bankTrans := self helperForTestReadEmbeddedOneToOne.
	self assert: (session isRegistered: bankTrans).
	self assert: (session isRegistered: bankTrans serviceCharge).
	self assert: (session isRegistered: bankTrans serviceCharge amount).! !

!ReadingTest methodsFor: 'tests'!
writeCustomer1Rows
	| id1 id2 customerRow accountRow1 accountRow2 linkRow1 linkRow2 |

	customerRow := session system exampleCustomerRow1.
	accountRow1 := session system exampleAccountRow1. 
	accountRow2 := session system exampleAccountRow2.
	linkRow1 := session system exampleCALinkRow1.
	linkRow2 := session system exampleCALinkRow2.
	session writeRow: customerRow.
	session writeRow: accountRow1.
	session writeRow: accountRow2.
	session writeRow: linkRow1.
	session writeRow: linkRow2.

	id1 := accountRow1 at: (accountRow1 table fieldNamed: 'ID').
	id2 := accountRow2 at: (accountRow2 table fieldNamed: 'ID').
	^Array with: id1 with: id2.! !

!ReadingTest methodsFor: 'support'!
setUp
	super setUp.
	session := SessionResource current newSession.
	system := session system.! !

!ReadingTest methodsFor: 'accessing'!
session
	^session.! !


!ReadingTest class methodsFor: 'resources'!
resources

	^Array with: SessionResource.! !


!RowMapUnificationTest methodsFor: 'support'!
setUp

	super setUp.
	t1 := DatabaseTable named: 'T1'.
	t2 := DatabaseTable named: 'T2'.
	t3 := DatabaseTable named: 'T3'.
	f1 := t1 newFieldNamed: 'f1'.
	f2 := t2 newFieldNamed: 'f2'.
	f3 := t3 newFieldNamed: 'f3'.
	rowMap := RowMap new.
	o1 := 'one'.
	o2 := 'two'.
	o3 := 'three'.! !

!RowMapUnificationTest methodsFor: 'tests'!
testDoubleRowUnificationDifferentRows

	FieldUnifier 
		unifyFields: (Array with: f1 with: f2)
		correspondingTo: (Array with: o1 with: o2)
		in: rowMap.
	FieldUnifier 
		unifyFields: (Array with: f3 with: f2)
		correspondingTo: (Array with: o3 with: o2)
		in: rowMap.
	(rowMap rowForTable: t1 withKey: o1) at: f1 put: 42.
	self assert: ((rowMap rowForTable: t1 withKey: o1) at: f1) = 42.
	self assert: ((rowMap rowForTable: t2 withKey: o2) at: f2) = 42.
	self assert: ((rowMap rowForTable: t3 withKey: o3) at: f3) = 42.! !

!RowMapUnificationTest methodsFor: 'tests'!
testDoubleRowUnificationDifferentRows2

	FieldUnifier 
		unifyFields: (Array with: f1 with: f2)
		correspondingTo: (Array with: o1 with: o2)
		in: rowMap.
	FieldUnifier 
		unifyFields: (Array with: f2 with: f3)
		correspondingTo: (Array with: o2 with: o3)
		in: rowMap.
	(rowMap rowForTable: t1 withKey: o1) at: f1 put: 42.
	self assert: ((rowMap rowForTable: t1 withKey: o1) at: f1) = 42.
	self assert: ((rowMap rowForTable: t2 withKey: o2) at: f2) = 42.
	self assert: ((rowMap rowForTable: t3 withKey: o3) at: f3) = 42.! !

!RowMapUnificationTest methodsFor: 'tests'!
testDoubleRowUnificationDifferentRows3

	FieldUnifier 
		unifyFields: (Array with: f1 with: f2)
		correspondingTo: (Array with: o1 with: o2)
		in: rowMap.
	FieldUnifier 
		unifyFields: (Array with: f2 with: f3)
		correspondingTo: (Array with: o2 with: o3)
		in: rowMap.
	(rowMap rowForTable: t3 withKey: o3) at: f3 put: 42.
	self assert: ((rowMap rowForTable: t1 withKey: o1) at: f1) = 42.
	self assert: ((rowMap rowForTable: t2 withKey: o2) at: f2) = 42.
	self assert: ((rowMap rowForTable: t3 withKey: o3) at: f3) = 42.! !

!RowMapUnificationTest methodsFor: 'tests'!
testDoubleRowUnificationDifferentRows4

	| t4 f4 o4 |
	t4 := DatabaseTable named: 'T4'.
	f4 := t4 newFieldNamed: 'f4'.
	o4 := 'four'.

	FieldUnifier 
		unifyFields: (Array with: f1 with: f2)
		correspondingTo: (Array with: o1 with: o2)
		in: rowMap.
	FieldUnifier 
		unifyFields: (Array with: f3 with: f4)
		correspondingTo: (Array with: o3 with: o4)
		in: rowMap.
	FieldUnifier 
		unifyFields: (Array with: f2 with: f3)
		correspondingTo: (Array with: o2 with: o3)
		in: rowMap.
	(rowMap rowForTable: t1 withKey: o1) at: f1 put: 42.
	self assert: ((rowMap rowForTable: t1 withKey: o1) at: f1) = 42.
	self assert: ((rowMap rowForTable: t2 withKey: o2) at: f2) = 42.
	self assert: ((rowMap rowForTable: t3 withKey: o3) at: f3) = 42.
	self assert: ((rowMap rowForTable: t4 withKey: o4) at: f4) = 42.! !

!RowMapUnificationTest methodsFor: 'tests'!
testDoubleRowUnificationSameRow

	FieldUnifier 
		unifyFields: (Array with: f1 with: f2)
		correspondingTo: (Array with: o1 with: o2)
		in: rowMap.
	FieldUnifier 
		unifyFields: (Array with: f1 with: f2)
		correspondingTo: (Array with: o1 with: o2)
		in: rowMap.
	(rowMap rowForTable: t1 withKey: o1) at: f1 put: 42.
	self assert: ((rowMap rowForTable: t1 withKey: o1) at: f1) = 42.
	self assert: ((rowMap rowForTable: t2 withKey: o2) at: f2) = 42! !

!RowMapUnificationTest methodsFor: 'tests'!
testDoubleRowUnificationSameRow2

	FieldUnifier 
		unifyFields: (Array with: f1 with: f2)
		correspondingTo: (Array with: o1 with: o2)
		in: rowMap.
	FieldUnifier 
		unifyFields: (Array with: f2 with: f1)
		correspondingTo: (Array with: o2 with: o1)
		in: rowMap.
	(rowMap rowForTable: t1 withKey: o1) at: f1 put: 42.
	self assert: ((rowMap rowForTable: t1 withKey: o1) at: f1) = 42.
	self assert: ((rowMap rowForTable: t2 withKey: o2) at: f2) = 42! !

!RowMapUnificationTest methodsFor: 'tests'!
testIteration

	| rows r1 r2 r3 count |
	r1 := rowMap findOrAddRowForTable: t1 withKey: o1.
	r2 := rowMap findOrAddRowForTable: t1 withKey: o2.
	r3 := rowMap findOrAddRowForTable: t2 withKey: o2.
	rows := IdentitySet new.
	count := 0.
	rowMap rowsDo: [:each | 
		count := count + 1.
		rows add: each].
	self assert: count = 3.
	self assert: (rows includes: r1).
	self assert: (rows includes: r3).
	self assert: (rows includes: r2).! !

!RowMapUnificationTest methodsFor: 'tests'!
testStoreThenUnify

	(rowMap findOrAddRowForTable: t1 withKey: o1) at: f1 put: 12.
	FieldUnifier 
		unifyFields: (Array with: f2 with: f3)
		correspondingTo: (Array with: o2 with: o3)
		in: rowMap.
	FieldUnifier 
		unifyFields: (Array with: f1 with: f2)
		correspondingTo: (Array with: o1 with: o2)
		in: rowMap.
	self assert: ((rowMap rowForTable: t1 withKey: o1) at: f1) = 12.
	self assert: ((rowMap rowForTable: t2 withKey: o2) at: f2) = 12.
	self assert: ((rowMap rowForTable: t3 withKey: o3) at: f3) = 12.! !

!RowMapUnificationTest methodsFor: 'tests'!
testStoreWithRowMapKey

	| a b key1 key2 key3 table r1 r2 r3 |
	a := Object new.
	b := Object new.
	key1 := RowMapKey new key1: a; key2: b.
	key2 := RowMapKey new key1: a; key2: b.
	key3 := RowMapKey new key1: b; key2: a.
	table := DatabaseTable new.

	r1 := rowMap findOrAddRowForTable: table withKey: key1.
	r2 := rowMap findOrAddRowForTable: table withKey: key2.
	r3 := rowMap findOrAddRowForTable: table withKey: key3.

	self assert: r1 == r2.
	self assert: r2 == r3.
	self assert: r1 owner == key1.! !


!SQLPrintingTest methodsFor: 'tests'!
testDatePrinting
	| date stream |
	date := Date newDay: 14 month: #Nov year: 1997.
	stream := WriteStream on: String new.
	date glorpPrintSQLOn: stream.
	self assert: stream contents = '''1997-11-14'''.

	date := Date newDay: 2 month: #May year: 2002.
	stream := WriteStream on: String new.
	date glorpPrintSQLOn: stream.
	self assert: stream contents = '''2002-05-02'''.! !


!SessionTest methodsFor: 'support'!
addCustomerToCache
	| customer |
	customer := Customer example1.
	customer id: 3.
	session cacheAt: 3 put: customer.
	^customer! !

!SessionTest methodsFor: 'support'!
setUp
	system := GlorpDemoDescriptorSystem new.
	session := Session new.
	session system: system! !

!SessionTest methodsFor: 'tests'!
testAddingDescriptors

	self assert: session system == system.
	self assert: system session == session.
	(session descriptorFor: Address) session == session.! !

!SessionTest methodsFor: 'tests'!
testExecuteQuery
	| q result |
	q := QueryStub new result: 3.
	result := session execute: q.
	self assert: result = 3! !

!SessionTest methodsFor: 'tests'!
testHasExpired1
	| customer |
	customer := self addCustomerToCache.
	self deny: (session hasExpired: customer)! !

!SessionTest methodsFor: 'tests'!
testHasExpired2
	| customer |
	(session system descriptorFor: Customer) 
		cachePolicy: (TimedExpiryCachePolicy new timeout: 0).
	customer := self addCustomerToCache.
	self assert: (session hasExpired: customer)! !

!SessionTest methodsFor: 'tests'!
testHasObjectOfClassExpired1

	self addCustomerToCache.
	self deny: (session hasObjectExpiredOfClass: Customer withKey: 3).! !

!SessionTest methodsFor: 'tests'!
testHasObjectOfClassExpired2

	(session system descriptorFor: Customer) 
		cachePolicy: (TimedExpiryCachePolicy new timeout: 0).
	self addCustomerToCache.
	self assert: (session hasObjectExpiredOfClass: Customer withKey: 3).! !


!SimpleQueryTest methodsFor: 'tests'!
testComputingFieldsForDirectMappings

	| query table tracing |
	query := SimpleQuery returningOneOf: Address where: [:each | each id = 1].
	query session: session.
	query setUpCriteria.
	tracing := Tracing new.
	query traceNodes: (tracing traceNodeSets first).
	query computeFields.
	
	table := session system tableNamed: 'GR_ADDRESS'.
	self assert: (query fields = table fields).! !

!SimpleQueryTest methodsFor: 'tests'!
testComputingFieldsForReferenceMappings

	| query table tracing |
	query := SimpleQuery returningOneOf: Person where: [:each | each id = 1].
	query session: session.
	query setUpCriteria.
	tracing := Tracing new.
	query traceNodes: (tracing traceNodeSets first).
	query computeFields.
	
	table := session system tableNamed: 'PERSON'.
	self assert: (query fields = table fields).! !

!SimpleQueryTest methodsFor: 'tests'!
testDescriptorAssignmentToCriteria

	| query |
	query := SimpleQuery returningOneOf: Address where: [:each | each id = 1].
	query session: session.
	query setUpCriteria.
	self assert: query criteria ultimateBaseExpression descriptor == (session descriptorFor: Address).! !

!SimpleQueryTest methodsFor: 'tests'!
testFieldAliasingForEmbeddedMappings

	| query table tracing |
	query := SimpleQuery returningOneOf: BankTransaction where: [:each | each id = 1].
	query session: session.
	query setUpCriteria.
	tracing := Tracing new.
	(session descriptorFor: BankTransaction)  setupTracing: tracing.
	query traceNodes: (tracing traceNodeSets first).
	query computeFields.
	table := session system tableNamed: 'BANK_TRANS'.
	self assert: (query fields = table fields).
	self assert: (query builders first translateFieldPosition: (table fieldNamed: 'ID')) = 1.
	self assert: (query builders first translateFieldPosition: (table fieldNamed: 'OWNER_ID')) = 2.! !

!SimpleQueryTest methodsFor: 'tests'!
testPrimaryKeyExpressionWithMultipleTables

	| query sql tracing sqlStream result |
	query := SimpleQuery returningOneOf: Passenger where: [:each | each id = 1].
	query session: session.
	query setUpCriteria.
	tracing := Tracing new.
	(session descriptorFor: Passenger)  setupTracing: tracing.
	query traceNodes: (tracing traceNodeSets first).
	query prepare.

	sql := query sqlWith: (Dictionary new).
	sqlStream := ReadStream on: sql asLowercase.
	sqlStream skipToAll: 'where '.
	Dialect isVisualWorks ifTrue: [sqlStream skip: 'where ' size]. "<Grumble grumble> stupid incompatibilities"
	result := sqlStream upToEnd.
	self assert: result = 't1.id = 1 and t1.id = t2.id'.! !

!SimpleQueryTest methodsFor: 'support'!
setUp

	session := SessionResource current newSession.! !

!SimpleQueryTest methodsFor: 'support'!
tearDown

	session := nil.! !


!SimpleQueryTest class methodsFor: 'resources'!
resources

	^Array with: SessionResource.! !


!TableTest methodsFor: 'support'!
setUp
	super setUp.
	system := GlorpDemoDescriptorSystem new.
	descriptors := system allDescriptors.! !

!TableTest methodsFor: 'tests'!
testBasicSequencing
	| row |
	row := DatabaseRow newForTable: (system existingTableNamed: 'BANK_TRANS').
	row preWriteAssignSequences.
	row postWriteAssignSequences.
	self assert: (row at: ((system existingTableNamed: 'BANK_TRANS') fieldNamed: 'ID')) = 1.! !

!TableTest methodsFor: 'tests'!
testCircularFieldRefs

	| field table1 table2 |
	table1 := DatabaseTable named: 'BAR'.
	field := table1 newFieldNamed: 'FOO'.
	table2 := DatabaseTable named: 'BLETCH'.
	table1 addForeignKeyFrom: field to: (table2 fieldNamed: 'FLIRP').
	self assert: (table2 fieldNamed: 'FLIRP') = table1 foreignKeyConstraints first targetField.! !

!TableTest methodsFor: 'tests'!
testConstraintCreation

	| constraint |
	constraint := ForeignKeyConstraint 
		sourceField: ((system tableNamed: 'BANK_ACCT') fieldNamed: 'BANK_CODE')
		targetField: ((system tableNamed: 'PERSON') fieldNamed: 'ID').
	self assert: constraint creationString = 
		 'CONSTRAINT BANK_ACCT_B_TO_PERSON_ID_REF FOREIGN KEY (BANK_CODE) REFERENCES PERSON (ID)'.
	self assert: constraint dropString = 
		 'ALTER TABLE BANK_ACCT DROP CONSTRAINT BANK_ACCT_B_TO_PERSON_ID_REF'.! !

!TableTest methodsFor: 'tests'!
testFieldTable

	| field table |
	field := DatabaseField named: 'FOO'.
	table := DatabaseTable named: 'BAR'.
	table addField: field.
	self assert: (table fieldNamed: 'FOO') = field.! !

!TableTest methodsFor: 'tests'!
testPrimaryKeyFields

	| pkFields table |
	table := system tableNamed: 'BANK_TRANS'.
	pkFields := table primaryKeyFields.
	self assert: pkFields size = 1.
	self assert: (pkFields at: 1) == (table fieldNamed: 'ID').! !

!TableTest methodsFor: 'tests'!
testPrimaryKeyFields2

	| table field |
	table := DatabaseTable new.
	field := DatabaseField new name: 'FRED'; bePrimaryKey.
	table addField: field.
	self assert: table primaryKeyFields size = 1.
	self assert: (table primaryKeyFields at: 1) == field.! !

!TableTest methodsFor: 'tests'!
testPrimaryKeyFieldsNoPK

	| pkFields table |
	table := system tableNamed: 'CUSTOMER_ACCT_LINK'.
	pkFields := table primaryKeyFields.
	self assert: pkFields size = 0.! !

!TableTest methodsFor: 'tests'!
testPrintingWithParent

	| t t1 |
	t := system tableNamed: 'GR_CUSTOMER'.
	t1 := t copy.
	t1 parent: t.
	t1 name: 'foo'.
	self assert: t1 sqlTableName = 'GR_CUSTOMER foo'.! !

!TableTest methodsFor: 'tests'!
testPrintingWithoutParent

	| t t1 |
	t := system tableNamed: 'GR_CUSTOMER'.
	self assert: t sqlTableName = 'GR_CUSTOMER'.! !

!TableTest methodsFor: 'tests'!
testRowCreation

	| row |
	row := system examplePersonRow1.
	self assert: (row at: (row table fieldNamed: 'ID')) = 3.! !

!TableTest methodsFor: 'tests'!
testTwoSequences
	| row1 row2 bankTable idField |
	bankTable := system existingTableNamed: 'BANK_TRANS'.
	row1 := DatabaseRow newForTable: bankTable.
	row2 := DatabaseRow newForTable: bankTable.
	row1 preWriteAssignSequences.
	row1 postWriteAssignSequences.
	row2 preWriteAssignSequences.
	row2 postWriteAssignSequences.
	idField := bankTable fieldNamed: 'ID'.
	self assert: (row1 at: idField) = 1.
	self assert: (row2 at: idField) = 2.! !


!TestPGConnection methodsFor: 'private' stamp: 'nop 5/31/2002 18:52'!
defaultConnectionArgs
	^ PGConnectionArgs
		hostname: 'localhost'
		portno: 5432
		databaseName: 'squeakdb'
		userName: 'bern'
		password: 'bern'! !


!TestRunner methodsFor: 'initialize' stamp: 'nop 5/31/2002 14:06'!
gatherTestNames
	| theNames |
	theNames _ (TestCase allSubclasses
				collect: [:each | each name]) asSortedCollection.
	theNames
		remove: #TestViaMethodCall
		ifAbsent: [^ theNames].
	TestViaMethodCall addClassesTo: theNames.
	^ theNames! !


!TimedExpiryCachePolicy methodsFor: 'wrap/unwrap'!
cacheEntryFor: anObject

	^Array with: Time totalSeconds with: anObject.! !

!TimedExpiryCachePolicy methodsFor: 'wrap/unwrap'!
contentsOf: aCacheEntry

	^aCacheEntry at: 2.! !

!TimedExpiryCachePolicy methodsFor: 'wrap/unwrap'!
hasExpired: aCacheEntry

	^(Time totalSeconds - (aCacheEntry at: 1)) >= timeout.! !

!TimedExpiryCachePolicy methodsFor: 'accessing'!
timeout
	^timeout! !

!TimedExpiryCachePolicy methodsFor: 'accessing'!
timeout: anInteger
	timeout := anInteger! !

!TimedExpiryCachePolicy methodsFor: 'initialize'!
initialize

	super initialize.
	timeout := 300.! !


!TimedExpiryCacheTest methodsFor: 'support'!
setUp
	super setUp.
	self setUpExpiryWithRealDelay.! !

!TimedExpiryCacheTest methodsFor: 'support'!
setUpExpiryWithRealDelay
	(cache session descriptorFor: Customer) cachePolicy: (TimedExpiryCachePolicy new timeout: 1).
	(cache session descriptorFor: BankTransaction) cachePolicy: (TimedExpiryCachePolicy new timeout: 1).! !

!TimedExpiryCacheTest methodsFor: 'support'!
setUpExpiryWithZeroDelay
	(cache session descriptorFor: Customer) cachePolicy: (TimedExpiryCachePolicy new timeout: 0).
	(cache session descriptorFor: BankTransaction) cachePolicy: (TimedExpiryCachePolicy new timeout: 0).! !

!TimedExpiryCacheTest methodsFor: 'support'!
setUpForExpiryActionOf: aSymbol
	(cache session descriptorFor: Customer) cachePolicy expiryAction: aSymbol.
	(cache session descriptorFor: BankTransaction) cachePolicy expiryAction: aSymbol.! !

!TimedExpiryCacheTest methodsFor: 'support'!
setUpForNotify
	self setUpForExpiryActionOf: #notify.! !

!TimedExpiryCacheTest methodsFor: 'support'!
setUpForNotifyAndRemove
	self setUpForExpiryActionOf: #notifyAndRemove.! !

!TimedExpiryCacheTest methodsFor: 'support'!
setUpForRefresh
	self setUpForExpiryActionOf: #refresh.! !

!TimedExpiryCacheTest methodsFor: 'tests'!
testExpiryReturningNilWithRealDelay
	"test that objects expire with a non-zero delay time."
	| customer customer2 |
	customer := Customer example1.
	cache at: 3 insert: customer.
	self deny: (cache lookupClass: Customer key: 3 ifAbsent: [nil]) == nil.
	(Delay forSeconds: 2) wait.
	self assert: (cache lookupClass: Customer key: 3 ifAbsent: [nil]) == nil.
	customer2 := Customer new.
	cache at: 3 insert: customer2.
	self assert: (cache lookupClass: Customer key: 3 ifAbsent: [nil]) == customer2.! !

!TimedExpiryCacheTest methodsFor: 'tests'!
testNotify
	| customer |
	self setUpExpiryWithZeroDelay.
	self setUpForNotify.
	customer := Customer example1.
	cache at: 3 insert: customer.
	self deny: (cache lookupClass: Customer key: 3 ifAbsent: [nil]) == nil.
	self assert: customer seenExpiry = true.! !

!TimedExpiryCacheTest methodsFor: 'tests'!
testNotifyAndRemove
	| customer |
	self setUpExpiryWithZeroDelay.
	self setUpForNotifyAndRemove.
	customer := Customer example1.
	cache at: 3 insert: customer.
	self assert: (cache lookupClass: Customer key: 3 ifAbsent: [nil]) == nil.
	self assert: customer seenExpiry = true.! !


!Tracing methodsFor: 'accessing'!
addTracing: anExpression

	(allTracings includes: anExpression) ifFalse: [
		allTracings add: anExpression].! !

!Tracing methodsFor: 'accessing'!
base
	^base! !

!Tracing methodsFor: 'accessing'!
base: anExpression 
	base := anExpression! !

!Tracing methodsFor: 'initialize'!
initialize

	base := BaseExpression new.
	allTracings := OrderedCollection new.
	allTracings add: base.! !

!Tracing methodsFor: 'querying'!
traceNodeSets
	^Array with: allTracings.! !

!Tracing methodsFor: 'querying'!
tracesThrough: aMapping

	^aMapping isIndependentRelationship not.! !


!Tracing class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!TracingTest methodsFor: 'support'!
setUp

	tracing := Tracing new.! !

!TracingTest methodsFor: 'tests'!
testAddDuplicateTracings

	| all |
	tracing addTracing: (tracing base get: #foo).
	tracing addTracing: (tracing base get: #foo).
	self assert: tracing traceNodeSets size = 1.
	all := tracing traceNodeSets first.
	self assert: all size = 2.
	self assert: all first == tracing base.! !

!TracingTest methodsFor: 'tests'!
testAddRecursiveTracings

	| all |
	tracing addTracing: (tracing base get: #foo).
	tracing addTracing: ((tracing base get: #foo) get: #bar).
	self assert: tracing traceNodeSets size = 1.
	all := tracing traceNodeSets first.
	self assert: all size = 3.
	self assert: all first == tracing base.
	self assert: all last base == (all at: 2).! !

!TracingTest methodsFor: 'tests'!
testAddTracing

	| onlyNodeSet |
	tracing addTracing: (tracing base get: #foo).
	self assert: tracing traceNodeSets size = 1.
	onlyNodeSet := tracing traceNodeSets first.
	self assert: onlyNodeSet size = 2.
	self assert: onlyNodeSet first == tracing base.
	self assert: onlyNodeSet last == (tracing base get: #foo).! !

!TracingTest methodsFor: 'tests'!
testAddTwoTracings

	tracing addTracing: (tracing base get: #foo).
	tracing addTracing: (tracing base get: #bar).
	self assert: tracing traceNodeSets size = 1.
	self assert: tracing traceNodeSets first size = 3.
	self assert: tracing traceNodeSets first first == tracing base.! !


!TransformedField methodsFor: 'database'!
convertToDatabaseForm: anObject

	^parent convertToDatabaseForm: (self transform: anObject).! !

!TransformedField methodsFor: 'database'!
transform: anObject

	^transformation value: anObject! !

!TransformedField methodsFor: 'accessing'!
parent
	^parent.! !

!TransformedField methodsFor: 'accessing'!
parent: aDatabaseField 
	parent := aDatabaseField.! !

!TransformedField methodsFor: 'accessing'!
rootField

	^parent rootField.! !

!TransformedField methodsFor: 'accessing'!
stringTransformation: aBlockClosure 
	stringTransformation := aBlockClosure.! !

!TransformedField methodsFor: 'accessing'!
transformation: aBlock
	transformation := aBlock.! !

!TransformedField methodsFor: 'printing'!
printSQLOn: aWriteStream withParameters: anArray 
	| newStream |
	newStream := String new writeStream.
	parent printSQLOn: newStream withParameters: anArray.
	aWriteStream nextPutAll: (stringTransformation value: newStream contents)! !


!UnitOfWork methodsFor: 'begin/commit/abort'!
abort
	self reinitialize.! !

!UnitOfWork methodsFor: 'begin/commit/abort'!
begin
	self reinitialize.! !

!UnitOfWork methodsFor: 'begin/commit/abort'!
commit

	"First, look for new objects reachable from currently registered objects"
	self registeredObjectsDo: [:eachObject |
		self registerTransitiveClosureFrom: eachObject].

	self registeredObjectsDo: [:eachObject |
		session createRowsFor: eachObject in: rowMap].
	self buildCommitPlan.
	self registeredObjectsDo: [:eachObject |
		session sendPreWriteEventTo: eachObject].
	self writeRows.
	self registeredObjectsDo: [:eachObject |
		session sendPostWriteEventTo: eachObject].
	self updateSessionCache.! !

!UnitOfWork methodsFor: 'begin/commit/abort'!
rollback
	self abort.! !

!UnitOfWork methodsFor: 'initializing'!
initialize

	transaction := ObjectTransaction new.
	self reinitialize! !

!UnitOfWork methodsFor: 'initializing'!
reinitialize

	rowMap := RowMap new.
	transaction abort.! !

!UnitOfWork methodsFor: 'enumerating'!
registeredObjectsDo: aBlock

	transaction registeredObjectsDo: [:each |
		(session hasDescriptorFor: each) ifTrue: [
			aBlock value: each]].! !

!UnitOfWork methodsFor: 'enumerating'!
rowsForTable: aTable do: aBlock

	rowMap rowsForTable: aTable do: aBlock.! !

!UnitOfWork methodsFor: 'accessing'!
session
	"Private - Answer the value of the receiver's ''session'' instance variable."

	^session! !

!UnitOfWork methodsFor: 'accessing'!
session: anObject
	"Private - Set the value of the receiver's ''session'' instance variable to the argument, anObject."

	session := anObject! !

!UnitOfWork methodsFor: 'private/mapping'!
addObject: eachObject toCacheKeyedBy: key 
	
	self session cacheAt: key put: eachObject.! !

!UnitOfWork methodsFor: 'private/mapping'!
addToCommitPlan: aRow
	
	commitPlan add: aRow.! !

!UnitOfWork methodsFor: 'private/mapping'!
buildCommitPlan
	
	commitPlan := OrderedCollection new.
	session tablesInCommitOrder do: [:eachTable  |
		self rowsForTable: eachTable do: [:eachRow |
			self addToCommitPlan: eachRow]].! !

!UnitOfWork methodsFor: 'private/mapping'!
readBackNewRowInformation

	self registeredObjectsDo: [:each |
		(session descriptorFor: each class) readBackNewRowInformationFor: each].! !

!UnitOfWork methodsFor: 'private/mapping'!
registerTransitiveClosureFrom: anObject

	| descriptor |
	descriptor := session descriptorFor: anObject class.
	descriptor isNil ifTrue: [^self].
	descriptor referencedIndependentObjectsFrom: anObject do: [:each |
		self register: each].! !

!UnitOfWork methodsFor: 'private/mapping'!
updateSessionCache
	rowMap
		keysAndValuesDo: [:eachObject :eachRow | 
			eachRow shouldBeWritten ifTrue: [
				self updateSessionCacheFor: eachObject withRow: eachRow]]! !

!UnitOfWork methodsFor: 'private/mapping'!
updateSessionCacheFor: anObject withRow: aRow 
	| key existingObject |
	key := aRow primaryKey.
	existingObject := session cacheLookupForClass: anObject class key: key.
	existingObject isNil ifTrue: [self addObject: anObject toCacheKeyedBy: key]! !

!UnitOfWork methodsFor: 'private/mapping'!
writeRows

	commitPlan do: [:eachRow |
		session writeRow: eachRow].
	self readBackNewRowInformation.! !

!UnitOfWork methodsFor: 'registering'!
isRegistered: anObject

	^transaction isRegistered: anObject.! !

!UnitOfWork methodsFor: 'registering'!
register: anObject 

	| realObject |
	realObject := transaction register: anObject.
	self registerTransitiveClosureFrom: realObject.! !

!UnitOfWork methodsFor: 'private'!
privateGetTransaction

	^transaction.! !


!UnitOfWork class methodsFor: 'instance creation'!
new

	^super new initialize.! !


!UnitOfWorkTest methodsFor: 'support'!
exampleCustomerProxy
	| p |
	p := Proxy new.
	p session: session.
	p query: (QueryStub returningOneOf: Customer where: [:a | a id = 3]).
	p query result: (Customer new id: 3).
	^p! !

!UnitOfWorkTest methodsFor: 'support'!
exampleTransactionWithCustomerProxy
	| transaction |
	transaction := BankTransaction example1.
	transaction owner: self exampleCustomerProxy.
	^transaction! !

!UnitOfWorkTest methodsFor: 'support'!
setUp
	super setUp.
	session := SessionResource current newSession.! !

!UnitOfWorkTest methodsFor: 'tests'!
testAutomaticRegistrationOnRead
	| p c |
	p := self exampleCustomerProxy.
	c := p getValue.
	session beginUnitOfWork.
	session register: p.
	self assert: (session isRegistered: p).
	self assert: (session isRegistered: c).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction isRegistered: p).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction isRegistered: c).! !

!UnitOfWorkTest methodsFor: 'tests'!
testCommitOrderAtSessionLevel

	| tables | 
	tables := session tablesInCommitOrder.
	tables first name = 'CUSTOMER'.
	self unfinished.! !

!UnitOfWorkTest methodsFor: 'tests'!
testPostRegister
	| c1 t1 t2 |
	c1 := Customer example1.
	[session beginTransaction.
	session beginUnitOfWork.
	t1 := BankTransaction new.
	t2 := BankTransaction new.
	c1 addTransaction: t1.
	c1 addTransaction: t2.
	session register: c1.
	self assert: (session isRegistered: c1).
	self assert: (session isRegistered: t1).
	self assert: (session isRegistered: t2).
	session commitUnitOfWork]
		ensure: [session rollbackTransaction].
	"Need some assertions on what was written"
	self unfinished.! !

!UnitOfWorkTest methodsFor: 'tests'!
testPreRegister
	
	| c1 t1 t2 trans |
	c1 := Customer example1.
	[session beginTransaction.
	session beginUnitOfWork.
	session register: c1.
	t1 := BankTransaction new.
	t2 := BankTransaction new.
	c1 addTransaction: t1.
	c1 addTransaction: t2.
	trans := session privateGetCurrentUnitOfWork privateGetTransaction.
	session commitUnitOfWork.
	self assert: (trans isRegistered: c1).
	self assert: (trans isRegistered: t1).
	self assert: (trans isRegistered: t2).]
		ensure: [session rollbackTransaction].
	"Need some assertions on what got written"
	self unfinished.! !

!UnitOfWorkTest methodsFor: 'tests'!
testRegisterInstantiatedProxy
	| p c |
	p := self exampleCustomerProxy.
	c := p getValue.
	session beginUnitOfWork.
	session register: p.
	self assert: (session isRegistered: p).
	self assert: (session isRegistered: c).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction isRegistered: p).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction isRegistered: c).! !

!UnitOfWorkTest methodsFor: 'tests'!
testRegisterObjectWithInstantiatedProxy
	| transaction customer |
	transaction := self exampleTransactionWithCustomerProxy.
	customer := transaction owner getValue.
	session beginUnitOfWork.
	session register: transaction.
	self assert: (session isRegistered: transaction).
	self assert: (session isRegistered: customer).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction 
				isRegistered: transaction).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction 
				isRegistered: customer).
	self assert: transaction owner isInstantiated.! !

!UnitOfWorkTest methodsFor: 'tests'!
testRegisterObjectWithProxy
	| transaction |
	transaction := self exampleTransactionWithCustomerProxy.
	session beginUnitOfWork.
	session register: transaction.
	self assert: (session isRegistered: transaction).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction 
				isRegistered: transaction).
	self deny: transaction owner isInstantiated.! !

!UnitOfWorkTest methodsFor: 'tests'!
testRegisterObjectWithProxyThenInstantiate
	| transaction customer |
	transaction := self exampleTransactionWithCustomerProxy.
	session beginUnitOfWork.
	session register: transaction.
	customer := transaction owner getValue.
	session register: transaction.
	self assert: (session isRegistered: transaction).
	self assert: (session isRegistered: customer).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction 
				isRegistered: transaction).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction 
				isRegistered: customer).
	self assert: transaction owner isInstantiated! !

!UnitOfWorkTest methodsFor: 'tests'!
testRegisterObjectWithProxyThenInstantiateAndReregister
	| transaction customer |
	transaction := self exampleTransactionWithCustomerProxy.
	session beginUnitOfWork.
	session register: transaction.
	customer := transaction owner getValue.
	session register: transaction.
	self assert: (session isRegistered: transaction).
	self assert: (session isRegistered: customer).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction 
				isRegistered: transaction).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction 
				isRegistered: customer).
	self assert: transaction owner isInstantiated! !

!UnitOfWorkTest methodsFor: 'tests'!
testRegisterProxy
	| p |
	p := self exampleCustomerProxy.
	session beginUnitOfWork.
	session register: p.
	self deny: (session isRegistered: p).
	self deny: (session privateGetCurrentUnitOfWork privateGetTransaction isRegistered: p).
	self deny: p isInstantiated.! !

!UnitOfWorkTest methodsFor: 'tests'!
testRegisterProxyThenInstantiateAndReregister
	| p c |
	p := self exampleCustomerProxy.
	session beginUnitOfWork.
	session register: p.
	c := p getValue.
	session register: p.
	self assert: (session isRegistered: p).
	self assert: (session isRegistered: c).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction 
				isRegistered: p).
	self assert: (session privateGetCurrentUnitOfWork privateGetTransaction 
				isRegistered: c)! !

!UnitOfWorkTest methodsFor: 'tests'!
testRollbackOneToManyAfterAdd
	| customer t1 t2 transList amount |
	customer := Customer example1.
	t1 := customer transactions first.
	t2 := customer transactions last.
	transList := customer transactions.
	amount := t1 amount.
	session beginUnitOfWork.
	session register: customer.
	20 timesRepeat: [customer addTransaction: (BankTransaction new)].
	customer transactions first amount: 65543.
	session rollbackUnitOfWork.
	self assert: customer transactions == transList.
	self assert: customer transactions size = 2.
	self assert: customer transactions first == t1.
	self assert: customer transactions last == t2.
	self assert: t1 amount == amount.! !

!UnitOfWorkTest methodsFor: 'tests'!
testRollbackOneToManyAfterReplace
	| customer t1 t2 transList |
	customer := Customer example1.
	t1 := customer transactions first.
	t2 := customer transactions last.
	transList := customer transactions.
	session beginUnitOfWork.
	session register: customer.
	customer transactions: OrderedCollection new.
	session rollbackUnitOfWork.
	self assert: customer transactions == transList.
	self assert: customer transactions size = 2.
	self assert: customer transactions first == t1.
	self assert: customer transactions last == t2.! !

!UnitOfWorkTest methodsFor: 'tests'!
testRollbackOneToManyProxy
	| customer t1 t2 transList |
	customer := Customer example1.
	t1 := customer transactions first.
	t2 := customer transactions last.
	transList := customer transactions.
	session beginUnitOfWork.
	session register: customer.
	customer transactions: OrderedCollection new.
	session rollbackUnitOfWork.
	self assert: customer transactions == transList.
	self assert: customer transactions size = 2.
	self assert: customer transactions first == t1.
	self assert: customer transactions last == t2.! !

!UnitOfWorkTest methodsFor: 'tests'!
testRollbackOneToManyWithList
	"Check that dependents aren't being registered for the collection"
	| customer marker |
	"Lists only exist in VW"
	Dialect isVisualWorks ifFalse: [^self].
	marker := Object new.
	customer := Customer example1.
	customer transactions: (customer transactions asList).
	customer transactions addDependent: marker.
	session beginUnitOfWork.
	session register: customer.
	20 timesRepeat: [customer addTransaction: (BankTransaction new)].
	session rollbackUnitOfWork.
	self assert: customer transactions class == List.
	self assert: customer transactions size = 2.
	self should: [customer transactions privateAt: 3] raise: Object subscriptOutOfBoundsSignal.
	self assert: (customer transactions dependents includes: marker).
	self deny: (session isRegistered: marker).! !

!UnitOfWorkTest methodsFor: 'tests'!
testRollbackOneToOne
	| transaction customer |
	transaction := BankTransaction new.
	customer := Customer new.
	transaction owner: customer.
	session beginUnitOfWork.
	session register: transaction.
	transaction owner: Customer new.
	session rollbackUnitOfWork.
	self assert: transaction owner == customer.! !

!UnitOfWorkTest methodsFor: 'tests'!
testRollbackOneToOneWithProxy
	| transaction customerProxy |
	transaction := self exampleTransactionWithCustomerProxy.
	customerProxy := transaction owner.
	session beginUnitOfWork.
	session register: transaction.
	transaction owner: Customer new.
	session rollbackUnitOfWork.
	self assert: transaction owner == customerProxy! !


!UnitOfWorkTest class methodsFor: 'resources'!
resources

	^Array with: SessionResource.! !


!VA55DatabaseAccessor methodsFor: 'executing'!
executeSQLString: aString 
	"^<(OrderedCollection of: ??) | self> This method executes a general SQL command against the connection"

	| resultTable rowCollection |
	self log: aString.
	rowCollection := OrderedCollection new.

	"Changed to get all sql commands to the database via this method"
	(aString copyFrom: 1 to: (6 min: aString size)) asUppercase = 'SELECT' 
		ifFalse: [
			connection 
				executeSQLStatement: aString
				ifError: [:err|self externalDatabaseErrorSignal signalWith: err]
		]
		ifTrue: 
			[
			resultTable := connection 
								resultTableFromQuerySpec: ((Smalltalk at: #AbtQuerySpec) new statement: aString)
								ifError: [:err |self externalDatabaseErrorSignal signalWith: err].
			resultTable isAbtError 
				ifFalse: [
					rowCollection := OrderedCollection new.
					resultTable do: 
							[:eachRow | 
							eachRow isAbtError 
								ifTrue: [self externalDatabaseErrorSignal signalWith: eachRow]
								ifFalse: [rowCollection addLast: OrderedCollection new. 
									(eachRow columnValuesDo: [:eachValue| rowCollection last addLast: eachValue])]]]].
"	self log: 'Done with executeSQLString.'."
	^rowCollection! !

!VA55DatabaseAccessor methodsFor: 'executing'!
externalDatabaseErrorSignal

	^self class externalDatabaseErrorSignal.! !

!VA55DatabaseAccessor methodsFor: 'development'!
todo: aString

	^self! !

!VA55DatabaseAccessor methodsFor: 'initialize'!
initialize
	super initialize.
	isInTransaction := false.! !

!VA55DatabaseAccessor methodsFor: 'login'!
connectionClassForLogin: aLogin 
	aLogin database isOraclePlatform 
		ifTrue: [^Smalltalk at: #AbtOracle8DatabaseManager].
	aLogin database isODBCPlatform 
		ifTrue: [^Smalltalk at: #AbtOdbcDatabaseManager].
	aLogin database isPostgreSQLPlatform 
		ifTrue: [self error: 'PostgreSQL is not yet supported under VA'].
	self error: 'Unknown database platform' , aLogin database printString! !

!VA55DatabaseAccessor methodsFor: 'login'!
isLoggedIn

	^connection notNil! !

!VA55DatabaseAccessor methodsFor: 'login'!
logStream
	^Transcript! !

!VA55DatabaseAccessor methodsFor: 'login'!
loginIfError: aBlock 
	"
		This method creates a NEW database connection.
	"

	| dbMgrClass anAbtDatabaseLogonSpec anAbtDatabaseConnectionSpec aliasNameString baseAliasName |
	self log: 'Login'.
	aliasNameString := baseAliasName := currentLogin connectString.

	"
		We try to generate a unique alias name to get a new connection and to 
		prevent the normal VAST behaviour to reuse a connection
	"
	
	[((Smalltalk at: #AbtDbmSystem) 
		activeDatabaseConnectionWithAlias: aliasNameString) notNil] 
			whileTrue: 
				[aliasNameString := baseAliasName , Time millisecondClockValue printString].


	"
		Get the native VA class responsible for doing the work against the
		three special connections available in VA
	"
	dbMgrClass := self connectionClass.

	"
		Get the logon specification for the database ... do not use
		any server information
	"
	anAbtDatabaseLogonSpec := (Smalltalk at: #AbtDatabaseLogonSpec) 
				id: currentLogin username
				password: currentLogin password
				server: nil.

	"	
		Get the specification for the connection ... 
   "
	anAbtDatabaseConnectionSpec := (Smalltalk at: #AbtDatabaseConnectionSpec) 
				forDbmClass: dbMgrClass
				dataSourceName: currentLogin connectString.
	connection := anAbtDatabaseConnectionSpec 
				connectUsingAlias: aliasNameString
				logonSpec: anAbtDatabaseLogonSpec
				ifError: 
					[:error | 
					"throw away the connection ..."

					connection := nil.
					aBlock value: error]! !

!VA55DatabaseAccessor methodsFor: 'login'!
logout
	self isLoggedIn ifFalse: [^self].
	self log: 'Logout'.
	[connection disconnectIfError: [:ex | self logError: ex]]
		ensure:	[connection := nil].
	self log: 'Logout finished'! !

!VA55DatabaseAccessor methodsFor: 'login'!
showDialog: aString

	(Smalltalk at: #CwMessagePrompter) warn: aString.! !

!VA55DatabaseAccessor methodsFor: 'transactions'!
beginTransaction
	self log: 'Begin Transaction'.
	isInTransaction := true.
	self connection beginUnitOfWork.! !

!VA55DatabaseAccessor methodsFor: 'transactions'!
commitTransaction
	self log: 'Commit Transaction'.
	isInTransaction := false.
	self connection commitUnitOfWorkIfError: 
			[:err |
			self log: 'Commit Transaction failed.'. 
			self connection autoCommit: true.
			self todo: 'Need to handle this error somehow....'.
			"errorBlock value: err"].! !

!VA55DatabaseAccessor methodsFor: 'transactions'!
isInTransaction
	^isInTransaction! !

!VA55DatabaseAccessor methodsFor: 'transactions'!
rollbackTransaction
	self log: 'Rollback Transaction'.
	isInTransaction := false.
	self connection rollbackUnitOfWork.! !


!VA55DatabaseAccessor class methodsFor: 'accessing'!
externalDatabaseErrorSignal

	databaseErrorSignal isNil ifTrue: [self initializeDatabaseErrorSignal ].
	^databaseErrorSignal! !

!VA55DatabaseAccessor class methodsFor: 'initialization'!
initializeDatabaseErrorSignal

	databaseErrorSignal := ((Smalltalk at: #SystemExceptions) at: 'ExError') newChild.
	databaseErrorSignal markReadOnly: false;
		description: 'Database error';
		resumable: false;
		defaultHandler: nil;
		markReadOnly: true! !


!VWDatabaseAccessor methodsFor: 'accessing'!
driverSession

	driverSession isNil
		ifTrue: [
			driverSession := connection getSession.
			(driverSession respondsTo: #maxLongBytes:) ifTrue: [
				driverSession maxLongBytes: 32767]].  "Why does this not default larger for Oracle?"
	^driverSession.! !

!VWDatabaseAccessor methodsFor: 'executing'!
disconnect

	^connection disconnect! !

!VWDatabaseAccessor methodsFor: 'executing'!
executeSQLString: aString 
	| answerStream |
	logging 
		ifTrue: 
			[Transcript
				show: aString;
				cr].
	self driverSession prepare: aString.
	self driverSession execute.
	answerStream := driverSession answer.
	answerStream == #noAnswerStream ifTrue: [^#()].
	answerStream == #noMoreAnswers ifTrue: [^#()].
	^answerStream upToEnd! !

!VWDatabaseAccessor methodsFor: 'executing'!
externalDatabaseErrorSignal

	^connection class externalDatabaseErrorSignal.! !

!VWDatabaseAccessor methodsFor: 'initialize'!
initialize

	super initialize.
	logging := true.! !

!VWDatabaseAccessor methodsFor: 'login'!
connectionClassForLogin: aLogin 
	aLogin database class == OraclePlatform ifTrue: [^'OracleConnection' asQualifiedReference value].
	aLogin database class == #MySQL ifTrue: [^'JdmMysqlConnection' asQualifiedReference value].
	aLogin database class == PostgreSQLPlatform 
		ifTrue: [^'PostgreSQLEXDIConnection' asQualifiedReference value].
	aLogin database class == SQLServerPlatform
		ifTrue: [^'ODBCConnection' asQualifiedReference value].
	self error: 'Unknown database: ' , aLogin database! !

!VWDatabaseAccessor methodsFor: 'login'!
isLoggedIn

	connection isNil ifTrue: [^false].
	^connection isConnected! !

!VWDatabaseAccessor methodsFor: 'login'!
loginIfError: aBlock 
	logging 	ifTrue: [Transcript show: 'Login'; cr].
	connection := self connectionClass new.
	connection username: currentLogin username.
	connection environment: currentLogin connectString.
	self doCommand: [connection connect: currentLogin password] ifError: aBlock.
	logging 
		ifTrue: 
			[Transcript
				show: 'Login finished';
				cr]! !

!VWDatabaseAccessor methodsFor: 'login'!
logout

	self isLoggedIn ifFalse: [^self].
	logging 	ifTrue: [Transcript show: 'Logout'; cr].
	self doCommand: [connection disconnect.].	
	logging 	ifTrue: [Transcript show: 'Logout finished'; cr].! !

!VWDatabaseAccessor methodsFor: 'login'!
showDialog: aString

	(Smalltalk at: #Dialog) warn: aString.! !

!VWDatabaseAccessor methodsFor: 'transactions'!
beginTransaction
	logging ifTrue: 
		[Transcript show: 'Begin Transaction'; cr].
	connection begin.! !

!VWDatabaseAccessor methodsFor: 'transactions'!
commitTransaction

	logging ifTrue: 
		[Transcript show: 'Commit Transaction'; cr].
	connection commit.! !

!VWDatabaseAccessor methodsFor: 'transactions'!
isInTransaction

	^connection inTransactionMode! !

!VWDatabaseAccessor methodsFor: 'transactions'!
rollbackTransaction
	logging ifTrue: 
		[Transcript show: 'Rollback Transaction'; cr].
	connection rollback.! !

Object subclass: #Session
	instanceVariableNames: 'system currentUnitOfWork cache accessor applicationData '
	classVariableNames: 'Sessions '
	poolDictionaries: ''
	category: 'Glorp'!
"Postscript:
First version of a quick and dirty port of glorp to Squeak"
!



More information about the Squeak-dev mailing list