├── .gitignore ├── .project ├── LICENSE ├── README.md └── src ├── .properties ├── BaselineOfPharoADO ├── BaselineOfPharoADO.class.st └── package.st ├── PharoADO-Glorp ├── ADODatabaseDriver.class.st ├── Address.class.st ├── ExampleDescriptorSystem.class.st ├── ExampleDescriptorSystemSQLServer.class.st ├── ExampleObject.class.st ├── ExampleTestOracle.class.st ├── ExampleTestPostgreSQL.class.st ├── ExampleTestSQLServer.class.st ├── Invoice.class.st ├── InvoiceItem.class.st ├── OraclePlatformADO.class.st ├── Person.class.st ├── SQLServerPlatformN.class.st ├── WideString.extension.st └── package.st └── PharoADO ├── ADOClient.class.st ├── ADOClientTests.class.st ├── ADOConnection.class.st ├── ADOConstants.class.st ├── ADOField.class.st ├── ADOFields.class.st ├── ADOObjects.class.st ├── ADORecordset.class.st └── package.st /.gitignore: -------------------------------------------------------------------------------- 1 | # changes file 2 | *.changes 3 | 4 | # system image 5 | *.image 6 | 7 | # Pharo Smalltalk Debug log file 8 | PharoDebug.log 9 | 10 | # Squeak Smalltalk Debug log file 11 | SqueakDebug.log 12 | 13 | # Monticello package cache 14 | /package-cache 15 | 16 | # playground cache 17 | /play-cache 18 | /play-stash 19 | 20 | # Metacello-github cache 21 | /github-cache 22 | github-*.zip 23 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | { 2 | 'srcDirectory' : 'src' 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tomaž Turk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PharoADO 2 | 3 | 4 | PharoADO enables data persistence in Pharo by using ActiveX Data Objects (ADO) on Microsoft Windows and external DBMS. It consists of two packages: 5 | - PharoADO: this package reflects the most used ADO objects - ADOConnection, ADORecordset, ADOFields and ADOField. It also includes ADOClient class to make the communication with the database as simple as possible. 6 | - PharoADO-Glorp implements ADODatabaseDriver to be used as a Glorp database driver. 7 | 8 | ADO comprises a set of Component Object Model (COM) objects to access various data sources. ADO is mature middleware technology and can be used as an abstraction between the application and database layers. It supports a plethora of various DBMSs and data providers. 9 | 10 | Since ADO is based on Component Object Model (COM), PharoADO relies on PharoCOM and other packages in PharoWin32 repository (https://github.com/tesonep/pharo-com). 11 | 12 | This repository is a prototype. Please test it in your own environments, especially by: 13 | - different DBMS endpoints (e.g. SQL Server, Oracle, MySQL, SQLite) 14 | - using various data providers (e.g. ODBC, native clients) 15 | - trying out various database data types. 16 | 17 | Our goal is to provide a reliable technology for data persistence with Pharo, ADO, Glorp and DBMSs and to promote Pharo in this kind of setups. 18 | 19 | Please tell us your experiences on Pharo mailing lists (http://forum.world.st), report issues on GitHub, and contribute by PRs. 20 | 21 | 22 | ## Code loading 23 | 24 | The default group loads both packages (PharoADO and PharoADO-Glorp), together with Glorp and PharoWin32: 25 | 26 | ```smalltalk 27 | Metacello new 28 | baseline: 'PharoADO'; 29 | repository: 'github://eftomi/PharoADO'; 30 | load. 31 | ``` 32 | 33 | If you don't need support for Glorp, you can load just the PharoADO like this: 34 | 35 | ```smalltalk 36 | Metacello new 37 | baseline: 'PharoADO'; 38 | repository: 'github://eftomi/PharoADO'; 39 | load: 'pharo-ado'. 40 | ``` 41 | 42 | 43 | ## Usage 44 | 45 | The usage can be seen from test examples in ADOClientTests (package PharoADO). Examples of Glorp & PharoADO usage are available in ExampleTestOracle and ExampleTestSQLServer within PharoADO-Glorp package. 46 | 47 | Please pay special attention to the connection strings. If possible, try them out firstly by other tools, since diagnostics in PharoADO can be difficult (for now) if you are not versatile with the debugger. If the exception occurs in COMMethod, you can check the ADO error description with: 48 | 49 | ```smalltalk 50 | ((self dispatchInstance propertyNamed: 'Errors') propertyNamed: 'Item' withArguments: { 0 }) propertyNamed: 'Description'. 51 | ``` 52 | 53 | on ADOConnection's eveluator. 54 | 55 | ### ADOClient 56 | 57 | ADOClient enables for the most basic usage of the package. Here's an exaple of a simple SELECT query: 58 | 59 | ```smalltalk 60 | c := ADOClient new 61 | connectString: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;'; 62 | user: ''; 63 | password: ''. 64 | c open. 65 | c query: 'SELECT * FROM PERSON'. 66 | c close. 67 | ``` 68 | 69 | If we issue the above #query: selector, the command is sent to the database by using ADO Connection's Execute() method. The return value is an array of rows, each row is an array of field values. Besides SELECT queries, we can use INSERT INTO, UPDATE, DELETE and DDL commands. The communication with different databases through various data providers can be achieved by different connection strings. 70 | 71 | ### ADOConnection, ADORecordset 72 | 73 | Traditional ADO Connection and Recordset objects can be used in the following way: 74 | 75 | ```smalltalk 76 | conn := ADOConnection createInstance . 77 | conn 78 | open: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;' 79 | user: '' 80 | password: ''. 81 | rst := ADORecordset createInstance . 82 | rst open: 'SELECT * FROM PERSON' 83 | activeConnection: conn 84 | cursorType: ADOpenDynamic 85 | lockType: ADLockOptimistic 86 | options: ADCmdUnspecified. 87 | 88 | rst addNew. 89 | (rst fields item: 2) value: 'John'. 90 | (rst fields item: 3) value: 'Kay'. 91 | (rst fields item: 4) value: 'johnkay@somedomain.org'. 92 | (rst fields item: 5) value: '1979-05-12'. 93 | rst update. 94 | 95 | rst moveFirst. 96 | 97 | [rst eof] whileFalse: [ 98 | 1 to: rst fields count do: [ :idx | 99 | Transcript show: (rst fields item: idx) name; show: ':'; 100 | show: (rst fields item: idx) value; cr]. 101 | Transcript cr. 102 | rst moveNext ]. 103 | 104 | rst close. 105 | conn close. 106 | ``` 107 | 108 | The example is intentionally written in a procedural fashion, to see the resemblance to the traditional usage of Connection and Recordset objects. The database table used in the example is from the Glorp book [Maringolo et al. (2018) Object-Relational Persistence with Glorp]. 109 | 110 | ### PharoADO-Glorp 111 | 112 | The usage of PharoADO-Glorp is pretty much defined by Glorp itself. To use the ADODatabaseDriver, one has to establish Pharo database accessor and declare a proper database platform, like in: 113 | 114 | ```smalltalk 115 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 116 | 117 | login := Login new 118 | database: OraclePlatform new; 119 | connectString: 'Driver={Oracle in instantclient_19_3};dbq=localhost:1521/XE;Uid=c##pharo;Pwd=pharo;'; 120 | username: ''; 121 | password: ''; 122 | yourself. 123 | 124 | session := ExampleDescriptorSystem sessionForLogin: login. 125 | session login. 126 | ``` 127 | 128 | The above is an example for Oracle. SQL Server differs only in the database platform choice: 129 | 130 | ```smalltalk 131 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 132 | 133 | login := Login new 134 | database: SQLServerPlatform new; 135 | connectString: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;'; 136 | username: ''; 137 | password: ''; 138 | yourself. 139 | 140 | session := ExampleDescriptorSystem sessionForLogin: login. 141 | session login. 142 | ``` 143 | 144 | When Glorp writes data onto DBMS, it "serializes" it by means of SQL commands according to the corresponding database platform. When receiving query results, the data transformation is done by ADORecordset object, more precisely by PharoCOM. A diversity of data types that can be used thus depends from their definition in Glorp/DatabasePlatform itself and PharoCOM implementation. 145 | 146 | ### PharoADO database platforms 147 | 148 | Package PharoAdo-Glorp includes two database platforms: 149 | - OraclePlatformADO is a subclass of OraclePlatform and defines decimal data type which is missing in OraclePlatform. 150 | - SQLServerPlatformN is a subclass of SQLServerPlatform and includes support for "National Language Character Set"; in INSERT INTO statemets, it prefixes literal WideStrings with N prefix. To use this, one has to define the field type in DescriptorSystem as 'platform nvarchar width: xx' and set the platform to SQLServerPlatformN. 151 | 152 | ### Miscellaneous 153 | 154 | When using SQL Server Native Client, it's best to enable Multiple Active Result Sets (MARS) in the connection string, like in: 155 | 156 | ``` 157 | Data Source=MSSQL; Initial Catalog=AdventureWorks; Integrated Security=SSPI; MultipleActiveResultSets=True 158 | ``` 159 | 160 | Otherwise, the provider might throw an exception "Cannot create new connection because in manual or distributed transaction mode." (https://docs.microsoft.com/en-us/sql/relational-databases/native-client/features/using-multiple-active-result-sets-mars?view=sql-server-ver15). 161 | -------------------------------------------------------------------------------- /src/.properties: -------------------------------------------------------------------------------- 1 | { 2 | #format : #tonel 3 | } -------------------------------------------------------------------------------- /src/BaselineOfPharoADO/BaselineOfPharoADO.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #BaselineOfPharoADO, 3 | #superclass : #BaselineOf, 4 | #category : #BaselineOfPharoADO 5 | } 6 | 7 | { #category : #baselines } 8 | BaselineOfPharoADO >> baseline: spec [ 9 | 10 | spec 11 | for: #common 12 | do: [ 13 | spec baseline: 'PharoWin32' with: [ spec repository: 'github://tesonep/pharo-com:master/']. 14 | spec baseline: 'Glorp' with: [ spec repository: 'github://pharo-rdbms/glorp:master/']. 15 | spec baseline: 'P3' with: [ spec 16 | loads: #('glorp'); 17 | repository: 'github://svenvc/P3:master/']. 18 | 19 | spec package: 'PharoADO' with: [ spec requires: #('PharoWin32') ]. 20 | spec package: 'PharoADO-Glorp' with: [ spec requires: #('PharoADO' 'Glorp') ]. 21 | 22 | spec group: 'default' with: #('PharoADO-Glorp'). 23 | spec group: 'pharo-ado' with: #('PharoADO'). 24 | spec group: 'ado-glorp-p3' with: #('PharoADO-Glorp' 'P3') ]. 25 | 26 | ] 27 | -------------------------------------------------------------------------------- /src/BaselineOfPharoADO/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #BaselineOfPharoADO } 2 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/ADODatabaseDriver.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ADODatabaseDriver, 3 | #superclass : #DatabaseDriver, 4 | #instVars : [ 5 | 'rowCount' 6 | ], 7 | #category : #'PharoADO-Glorp-Core' 8 | } 9 | 10 | { #category : #executing } 11 | ADODatabaseDriver >> basicExecuteSQLString: aString [ 12 | | result | 13 | result := connection query: aString. 14 | 15 | rowCount := result size. 16 | ^ ((rowCount > 0) ifTrue: [ result ] ifFalse: [ #() ]) readStream 17 | ] 18 | 19 | { #category : #transactions } 20 | ADODatabaseDriver >> beginTransaction [ 21 | connection connection beginTransaction 22 | ] 23 | 24 | { #category : #transactions } 25 | ADODatabaseDriver >> commitTransaction [ 26 | connection connection commitTransaction 27 | ] 28 | 29 | { #category : #accessing } 30 | ADODatabaseDriver >> connect: aLogin [ 31 | connection := self connectionClass new. 32 | connection 33 | connectString: aLogin connectString; 34 | user: aLogin username; 35 | password: aLogin password. 36 | connection connect 37 | ] 38 | 39 | { #category : #accessing } 40 | ADODatabaseDriver >> connectionClass [ 41 | ^ADOClient 42 | ] 43 | 44 | { #category : #testing } 45 | ADODatabaseDriver >> isConnected [ 46 | ^ connection notNil and: [ connection isConnected ] 47 | ] 48 | 49 | { #category : #login } 50 | ADODatabaseDriver >> logout [ 51 | connection close. 52 | ] 53 | 54 | { #category : #transactions } 55 | ADODatabaseDriver >> rollbackTransaction [ 56 | connection connection rollbackTransaction 57 | ] 58 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/Address.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #Address, 3 | #superclass : #ExampleObject, 4 | #instVars : [ 5 | 'street', 6 | 'number', 7 | 'city', 8 | 'zip' 9 | ], 10 | #category : #'PharoADO-Glorp-Examples' 11 | } 12 | 13 | { #category : #accessing } 14 | Address >> city [ 15 | ^ city 16 | ] 17 | 18 | { #category : #accessing } 19 | Address >> city: anObject [ 20 | city := anObject 21 | ] 22 | 23 | { #category : #accessing } 24 | Address >> number [ 25 | ^ number 26 | ] 27 | 28 | { #category : #accessing } 29 | Address >> number: anObject [ 30 | number := anObject 31 | ] 32 | 33 | { #category : #accessing } 34 | Address >> street [ 35 | ^ street 36 | ] 37 | 38 | { #category : #accessing } 39 | Address >> street: anObject [ 40 | street := anObject 41 | ] 42 | 43 | { #category : #accessing } 44 | Address >> zip [ 45 | ^ zip 46 | ] 47 | 48 | { #category : #accessing } 49 | Address >> zip: anObject [ 50 | zip := anObject 51 | ] 52 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/ExampleDescriptorSystem.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ExampleDescriptorSystem, 3 | #superclass : #DescriptorSystem, 4 | #category : #'PharoADO-Glorp-Examples' 5 | } 6 | 7 | { #category : #'as yet unclassified' } 8 | ExampleDescriptorSystem >> classModelForAddress: aClassModel [ 9 | (aClassModel newAttributeNamed: #id) useDirectAccess: true. 10 | aClassModel newAttributeNamed: #street. 11 | aClassModel newAttributeNamed: #zip 12 | ] 13 | 14 | { #category : #'as yet unclassified' } 15 | ExampleDescriptorSystem >> classModelForInvoice: aClassModel [ 16 | (aClassModel newAttributeNamed: #id) useDirectAccess: true. 17 | aClassModel newAttributeNamed: #issueDate. 18 | aClassModel newAttributeNamed: #person type: Person. 19 | aClassModel newAttributeNamed: #address type: Address. 20 | aClassModel newAttributeNamed: #items collectionOf: InvoiceItem 21 | ] 22 | 23 | { #category : #'as yet unclassified' } 24 | ExampleDescriptorSystem >> classModelForInvoiceItem: aClassModel [ 25 | (aClassModel newAttributeNamed: #id) useDirectAccess: true. 26 | aClassModel newAttributeNamed: #invoice type: Invoice. 27 | aClassModel newAttributeNamed: #description. 28 | aClassModel newAttributeNamed: #price 29 | ] 30 | 31 | { #category : #'as yet unclassified' } 32 | ExampleDescriptorSystem >> classModelForPerson: aClassModel [ 33 | (aClassModel newAttributeNamed: #id) useDirectAccess: true. 34 | aClassModel newAttributeNamed: #firstName. 35 | aClassModel newAttributeNamed: #lastName. 36 | aClassModel newAttributeNamed: #email. 37 | aClassModel newAttributeNamed: #birthDate. 38 | aClassModel newAttributeNamed: #addresses collectionOf: Address. 39 | aClassModel newAttributeNamed: #invoices collectionOf: Invoice. 40 | 41 | ] 42 | 43 | { #category : #'as yet unclassified' } 44 | ExampleDescriptorSystem >> descriptorForAddress: aDescriptor [ 45 | | table | 46 | table := self tableNamed: 'ADDRESS'. 47 | aDescriptor table: table. 48 | (aDescriptor newMapping: DirectMapping) 49 | from: #id 50 | to: (table fieldNamed: 'id'). 51 | (aDescriptor newMapping: DirectMapping) 52 | from: #street 53 | to: (table fieldNamed: 'street'). 54 | (aDescriptor newMapping: DirectMapping) 55 | from: #city 56 | to: (table fieldNamed: 'zip'). 57 | ] 58 | 59 | { #category : #'as yet unclassified' } 60 | ExampleDescriptorSystem >> descriptorForInvoice: aDescriptor [ 61 | | table | 62 | table := self tableNamed: 'INVOICE'. 63 | aDescriptor table: table. 64 | (aDescriptor newMapping: DirectMapping) 65 | from: #id 66 | to: (table fieldNamed: 'id'). 67 | (aDescriptor newMapping: DirectMapping) 68 | from: #issueDate 69 | to: (table fieldNamed: 'issueDate'). 70 | (aDescriptor newMapping: OneToOneMapping) 71 | attributeName: #person. 72 | (aDescriptor newMapping: OneToOneMapping) 73 | attributeName: #address. 74 | (aDescriptor newMapping: ToManyMapping) 75 | attributeName: #items; 76 | orderBy: [:each | 77 | (each getTable: 'INVOICEITEM') getField: 'position']; 78 | writeTheOrderField. 79 | ] 80 | 81 | { #category : #'as yet unclassified' } 82 | ExampleDescriptorSystem >> descriptorForInvoiceItem: aDescriptor [ 83 | | table | 84 | table := self tableNamed: 'INVOICEITEM'. 85 | aDescriptor table: table. 86 | (aDescriptor newMapping: DirectMapping) 87 | from: #id 88 | to: (table fieldNamed: 'id'). 89 | (aDescriptor newMapping: DirectMapping) 90 | from: #description 91 | to: (table fieldNamed: 'description'). 92 | (aDescriptor newMapping: DirectMapping) 93 | from: #price 94 | to: (table fieldNamed: 'price'). 95 | (aDescriptor newMapping: OneToOneMapping) 96 | attributeName: #invoice. 97 | ] 98 | 99 | { #category : #'as yet unclassified' } 100 | ExampleDescriptorSystem >> descriptorForPerson: aDescriptor [ 101 | | table linkTable | 102 | table := self tableNamed: 'PERSON'. 103 | aDescriptor table: table. 104 | (aDescriptor newMapping: DirectMapping) 105 | from: #id 106 | to: (table fieldNamed: 'id'). 107 | (aDescriptor newMapping: DirectMapping) 108 | from: #firstName 109 | to: (table fieldNamed: 'firstName'). 110 | (aDescriptor newMapping: DirectMapping) 111 | from: #lastName 112 | to: (table fieldNamed: 'lastName'). 113 | (aDescriptor newMapping: DirectMapping) 114 | from: #email 115 | to: (table fieldNamed: 'email'). 116 | (aDescriptor newMapping: DirectMapping) 117 | from: #birthDate 118 | to: (table fieldNamed: 'birthDate'). 119 | 120 | (aDescriptor newMapping: ToManyMapping) 121 | attributeName: #invoices; 122 | orderBy: [:each | 123 | (each getTable: 'INVOICE') getField: 'issueDate']. 124 | 125 | linkTable := self tableNamed: 'PERSON_ON_ADDRESS'. 126 | (aDescriptor newMapping: ManyToManyMapping) 127 | attributeName: #addresses; 128 | referenceClass: Address; 129 | beExclusive; 130 | join: (Join 131 | from: (table fieldNamed: 'id') 132 | to: (linkTable fieldNamed: 'person_id')). 133 | 134 | ] 135 | 136 | { #category : #'as yet unclassified' } 137 | ExampleDescriptorSystem >> tableForADDRESS: aTable [ 138 | (aTable createFieldNamed: 'id' type: platform serial) bePrimaryKey. 139 | aTable createFieldNamed: 'street' type: (platform varChar: 100). 140 | aTable createFieldNamed: 'zip' type: platform integer. 141 | ] 142 | 143 | { #category : #'as yet unclassified' } 144 | ExampleDescriptorSystem >> tableForINVOICE: aTable [ 145 | | personField addressField | 146 | (aTable createFieldNamed: 'id' type: platform serial) 147 | bePrimaryKey. 148 | aTable createFieldNamed: 'issueDate' type: platform date. 149 | personField := aTable 150 | createFieldNamed: 'person_id' 151 | type: platform integer. 152 | addressField := aTable 153 | createFieldNamed: 'address_id' 154 | type: platform integer. 155 | aTable 156 | addForeignKeyFrom: personField 157 | to: ((self tableNamed: 'PERSON') fieldNamed: 'id'). 158 | aTable 159 | addForeignKeyFrom: addressField 160 | to: ((self tableNamed: 'ADDRESS') fieldNamed: 'id'). 161 | ] 162 | 163 | { #category : #'as yet unclassified' } 164 | ExampleDescriptorSystem >> tableForINVOICEITEM: aTable [ 165 | | invoiceField | 166 | (aTable createFieldNamed: 'id' type: platform serial) bePrimaryKey. 167 | invoiceField := aTable createFieldNamed: 'invoice_id' type: 168 | platform integer. 169 | aTable createFieldNamed: 'description' type: (platform varchar: 150). 170 | aTable createFieldNamed: 'price' type: (platform decimal precision: 10; scale: 2). 171 | aTable createFieldNamed: 'position' type: platform integer. 172 | aTable 173 | addForeignKeyFrom: invoiceField 174 | to: ((self tableNamed: 'INVOICE') fieldNamed: 'id'). 175 | ] 176 | 177 | { #category : #tables } 178 | ExampleDescriptorSystem >> tableForPERSON: aTable [ 179 | (aTable createFieldNamed: 'id' type: platform serial) bePrimaryKey. 180 | aTable createFieldNamed: 'firstName' type: (platform varChar: 100). 181 | aTable createFieldNamed: 'lastName' type: (platform varChar: 100). 182 | aTable createFieldNamed: 'email' type: (platform varchar: 200). 183 | aTable createFieldNamed: 'birthDate' type: platform date. 184 | ] 185 | 186 | { #category : #'as yet unclassified' } 187 | ExampleDescriptorSystem >> tableForPERSON_ON_ADDRESS: aTable [ 188 | | personField addressField | 189 | personField := aTable 190 | createFieldNamed: 'person_id' 191 | type: platform integer. 192 | addressField := aTable 193 | createFieldNamed: 'address_id' 194 | type: platform integer. 195 | personField bePrimaryKey. 196 | addressField bePrimaryKey. 197 | aTable 198 | addForeignKeyFrom: personField 199 | to: ((self tableNamed: 'PERSON') fieldNamed: 'id'). 200 | aTable 201 | addForeignKeyFrom: addressField 202 | to: ((self tableNamed: 'ADDRESS') fieldNamed: 'id'). 203 | ] 204 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/ExampleDescriptorSystemSQLServer.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ExampleDescriptorSystemSQLServer, 3 | #superclass : #DescriptorSystem, 4 | #category : #'PharoADO-Glorp-Examples' 5 | } 6 | 7 | { #category : #'as yet unclassified' } 8 | ExampleDescriptorSystemSQLServer >> classModelForAddress: aClassModel [ 9 | (aClassModel newAttributeNamed: #id) useDirectAccess: true. 10 | aClassModel newAttributeNamed: #street. 11 | aClassModel newAttributeNamed: #zip 12 | ] 13 | 14 | { #category : #'as yet unclassified' } 15 | ExampleDescriptorSystemSQLServer >> classModelForInvoice: aClassModel [ 16 | (aClassModel newAttributeNamed: #id) useDirectAccess: true. 17 | aClassModel newAttributeNamed: #issueDate. 18 | aClassModel newAttributeNamed: #person type: Person. 19 | aClassModel newAttributeNamed: #address type: Address. 20 | aClassModel newAttributeNamed: #items collectionOf: InvoiceItem 21 | ] 22 | 23 | { #category : #'as yet unclassified' } 24 | ExampleDescriptorSystemSQLServer >> classModelForInvoiceItem: aClassModel [ 25 | (aClassModel newAttributeNamed: #id) useDirectAccess: true. 26 | aClassModel newAttributeNamed: #invoice type: Invoice. 27 | aClassModel newAttributeNamed: #description. 28 | aClassModel newAttributeNamed: #price 29 | ] 30 | 31 | { #category : #'as yet unclassified' } 32 | ExampleDescriptorSystemSQLServer >> classModelForPerson: aClassModel [ 33 | (aClassModel newAttributeNamed: #id) useDirectAccess: true. 34 | aClassModel newAttributeNamed: #firstName. 35 | aClassModel newAttributeNamed: #lastName. 36 | aClassModel newAttributeNamed: #email. 37 | aClassModel newAttributeNamed: #birthDate. 38 | aClassModel newAttributeNamed: #addresses collectionOf: Address. 39 | aClassModel newAttributeNamed: #invoices collectionOf: Invoice. 40 | 41 | ] 42 | 43 | { #category : #'as yet unclassified' } 44 | ExampleDescriptorSystemSQLServer >> descriptorForAddress: aDescriptor [ 45 | | table | 46 | table := self tableNamed: 'ADDRESS'. 47 | aDescriptor table: table. 48 | (aDescriptor newMapping: DirectMapping) 49 | from: #id 50 | to: (table fieldNamed: 'id'). 51 | (aDescriptor newMapping: DirectMapping) 52 | from: #street 53 | to: (table fieldNamed: 'street'). 54 | (aDescriptor newMapping: DirectMapping) 55 | from: #city 56 | to: (table fieldNamed: 'zip'). 57 | ] 58 | 59 | { #category : #'as yet unclassified' } 60 | ExampleDescriptorSystemSQLServer >> descriptorForInvoice: aDescriptor [ 61 | | table | 62 | table := self tableNamed: 'INVOICE'. 63 | aDescriptor table: table. 64 | (aDescriptor newMapping: DirectMapping) 65 | from: #id 66 | to: (table fieldNamed: 'id'). 67 | (aDescriptor newMapping: DirectMapping) 68 | from: #issueDate 69 | to: (table fieldNamed: 'issueDate'). 70 | (aDescriptor newMapping: OneToOneMapping) 71 | attributeName: #person. 72 | (aDescriptor newMapping: OneToOneMapping) 73 | attributeName: #address. 74 | (aDescriptor newMapping: ToManyMapping) 75 | attributeName: #items; 76 | orderBy: [:each | 77 | (each getTable: 'INVOICEITEM') getField: 'position']; 78 | writeTheOrderField. 79 | ] 80 | 81 | { #category : #'as yet unclassified' } 82 | ExampleDescriptorSystemSQLServer >> descriptorForInvoiceItem: aDescriptor [ 83 | | table | 84 | table := self tableNamed: 'INVOICEITEM'. 85 | aDescriptor table: table. 86 | (aDescriptor newMapping: DirectMapping) 87 | from: #id 88 | to: (table fieldNamed: 'id'). 89 | (aDescriptor newMapping: DirectMapping) 90 | from: #description 91 | to: (table fieldNamed: 'description'). 92 | (aDescriptor newMapping: DirectMapping) 93 | from: #price 94 | to: (table fieldNamed: 'price'). 95 | (aDescriptor newMapping: OneToOneMapping) 96 | attributeName: #invoice. 97 | ] 98 | 99 | { #category : #'as yet unclassified' } 100 | ExampleDescriptorSystemSQLServer >> descriptorForPerson: aDescriptor [ 101 | | table linkTable | 102 | table := self tableNamed: 'PERSON'. 103 | aDescriptor table: table. 104 | (aDescriptor newMapping: DirectMapping) 105 | from: #id 106 | to: (table fieldNamed: 'id'). 107 | (aDescriptor newMapping: DirectMapping) 108 | from: #firstName 109 | to: (table fieldNamed: 'firstName'). 110 | (aDescriptor newMapping: DirectMapping) 111 | from: #lastName 112 | to: (table fieldNamed: 'lastName'). 113 | (aDescriptor newMapping: DirectMapping) 114 | from: #email 115 | to: (table fieldNamed: 'email'). 116 | (aDescriptor newMapping: DirectMapping) 117 | from: #birthDate 118 | to: (table fieldNamed: 'birthDate'). 119 | 120 | (aDescriptor newMapping: ToManyMapping) 121 | attributeName: #invoices; 122 | orderBy: [:each | 123 | (each getTable: 'INVOICE') getField: 'issueDate']. 124 | 125 | linkTable := self tableNamed: 'PERSON_ON_ADDRESS'. 126 | (aDescriptor newMapping: ManyToManyMapping) 127 | attributeName: #addresses; 128 | referenceClass: Address; 129 | beExclusive; 130 | join: (Join 131 | from: (table fieldNamed: 'id') 132 | to: (linkTable fieldNamed: 'person_id')). 133 | 134 | ] 135 | 136 | { #category : #'as yet unclassified' } 137 | ExampleDescriptorSystemSQLServer >> tableForADDRESS: aTable [ 138 | (aTable createFieldNamed: 'id' type: platform serial) bePrimaryKey. 139 | aTable createFieldNamed: 'street' type: (platform varChar: 100). 140 | aTable createFieldNamed: 'zip' type: platform integer. 141 | ] 142 | 143 | { #category : #'as yet unclassified' } 144 | ExampleDescriptorSystemSQLServer >> tableForINVOICE: aTable [ 145 | | personField addressField | 146 | (aTable createFieldNamed: 'id' type: platform serial) 147 | bePrimaryKey. 148 | aTable createFieldNamed: 'issueDate' type: platform date. 149 | personField := aTable 150 | createFieldNamed: 'person_id' 151 | type: platform integer. 152 | addressField := aTable 153 | createFieldNamed: 'address_id' 154 | type: platform integer. 155 | aTable 156 | addForeignKeyFrom: personField 157 | to: ((self tableNamed: 'PERSON') fieldNamed: 'id'). 158 | aTable 159 | addForeignKeyFrom: addressField 160 | to: ((self tableNamed: 'ADDRESS') fieldNamed: 'id'). 161 | ] 162 | 163 | { #category : #'as yet unclassified' } 164 | ExampleDescriptorSystemSQLServer >> tableForINVOICEITEM: aTable [ 165 | | invoiceField | 166 | (aTable createFieldNamed: 'id' type: platform serial) bePrimaryKey. 167 | invoiceField := aTable createFieldNamed: 'invoice_id' type: 168 | platform integer. 169 | aTable createFieldNamed: 'description' type: (platform varchar: 150). 170 | aTable createFieldNamed: 'price' type: (platform decimal precision: 10; scale: 2). 171 | aTable createFieldNamed: 'position' type: platform integer. 172 | aTable 173 | addForeignKeyFrom: invoiceField 174 | to: ((self tableNamed: 'INVOICE') fieldNamed: 'id'). 175 | ] 176 | 177 | { #category : #tables } 178 | ExampleDescriptorSystemSQLServer >> tableForPERSON: aTable [ 179 | (aTable createFieldNamed: 'id' type: platform serial) bePrimaryKey. 180 | aTable createFieldNamed: 'firstName' type: (platform nvarchar width: 100). 181 | aTable createFieldNamed: 'lastName' type: (platform nvarchar width: 100). 182 | aTable createFieldNamed: 'email' type: (platform varchar: 200). 183 | aTable createFieldNamed: 'birthDate' type: platform date. 184 | ] 185 | 186 | { #category : #'as yet unclassified' } 187 | ExampleDescriptorSystemSQLServer >> tableForPERSON_ON_ADDRESS: aTable [ 188 | | personField addressField | 189 | personField := aTable 190 | createFieldNamed: 'person_id' 191 | type: platform integer. 192 | addressField := aTable 193 | createFieldNamed: 'address_id' 194 | type: platform integer. 195 | personField bePrimaryKey. 196 | addressField bePrimaryKey. 197 | aTable 198 | addForeignKeyFrom: personField 199 | to: ((self tableNamed: 'PERSON') fieldNamed: 'id'). 200 | aTable 201 | addForeignKeyFrom: addressField 202 | to: ((self tableNamed: 'ADDRESS') fieldNamed: 'id'). 203 | ] 204 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/ExampleObject.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ExampleObject, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'id' 6 | ], 7 | #category : #'PharoADO-Glorp-Examples' 8 | } 9 | 10 | { #category : #accessing } 11 | ExampleObject >> id [ 12 | ^ id 13 | ] 14 | 15 | { #category : #accessing } 16 | ExampleObject >> id: anObject [ 17 | id := anObject 18 | ] 19 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/ExampleTestOracle.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ExampleTestOracle, 3 | #superclass : #TestCase, 4 | #category : #'PharoADO-Glorp-Examples' 5 | } 6 | 7 | { #category : #tests } 8 | ExampleTestOracle >> testAddPersons [ 9 | | login session | 10 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 11 | 12 | login := Login new 13 | database: OraclePlatformADO new; 14 | connectString: 'Driver={Oracle in instantclient_19_3};dbq=localhost:1521/XE;Uid=c##pharo;Pwd=pharo;'; 15 | username: ''; 16 | password: ''; 17 | encodingStrategy: #utf8; 18 | yourself. 19 | 20 | session := ExampleDescriptorSystem sessionForLogin: login. 21 | session login. 22 | 23 | "We will create some sample data for our example, and for the Persons we 24 | will save three instances named after famous Smalltalkers, with random addresses, so don’t mail them." 25 | 26 | session inUnitOfWorkDo: [ 27 | { 'Dan'. 'Alan'. 'Adele' } 28 | with: {'Ingalls'. 'Kay'. 'Goldberg'} 29 | do: [ :firstName :lastName | 30 | | person | 31 | person := (Person new 32 | firstName: firstName; 33 | lastName: lastName). 34 | person addresses add: ( 35 | Address new 36 | street: (1 to: 1000) atRandom printString 37 | , ' Random Avenue'; 38 | zip: (1000 to: 9000 by: 100) atRandom; 39 | yourself). 40 | session register: person 41 | ] 42 | ]. 43 | 44 | "Now we created the sample Persons, let’s add an extra address for Alan." 45 | 46 | session inUnitOfWorkDo: [ 47 | | alan | 48 | alan := session 49 | readOneOf: Person 50 | where: [ :each | each firstName = 'Alan' ]. 51 | alan addresses 52 | add: (Address new 53 | street: '1025 Westwood Blv, 2nd Floor, Los Angeles, CA'; 54 | zip: 90024; 55 | yourself) 56 | ]. 57 | 58 | "As you can see, we didn’t have to register the newly created address because 59 | the Person was read inside of a UnitOfWork. If we read the instance again 60 | and inspect its addresses we will find the new instance is there." 61 | 62 | (session 63 | readOneOf: Person 64 | where: [ :each | each firstName = 'Alan' ]) addresses inspect. 65 | 66 | "We can now procceed to create an few instances of Invoice. Let’s create one 67 | invoice for each person in the database with two items describing donations to the Pharo Consortium2 and Association3 with randomized amounts (within a certain range) for each one." 68 | 69 | session inUnitOfWorkDo: [ 70 | (session read: Person) do: [:person | 71 | | invoice | 72 | invoice := Invoice new 73 | issueDate: Date today; 74 | person: person; 75 | address: person addresses atRandom. 76 | invoice 77 | addItem: (InvoiceItem 78 | description: 'Pharo Consortium donation' 79 | price: (1000s to: 4000s by: 1000s) atRandom); 80 | addItem: (InvoiceItem 81 | description: 'Pharo Association donation' 82 | price: (20s to: 100s by: 10s) atRandom); yourself. 83 | session register: invoice. 84 | ] 85 | ]. 86 | 87 | 88 | 89 | 90 | ] 91 | 92 | { #category : #tests } 93 | ExampleTestOracle >> testCreateTables [ 94 | | login session | 95 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 96 | 97 | login := Login new 98 | database: OraclePlatformADO new; 99 | connectString: 'Driver={Oracle in instantclient_19_3};dbq=itchy.miskoti.si:1521/XE;Uid=c##pharo;Pwd=pharo;'; 100 | username: ''; 101 | password: ''; 102 | encodingStrategy: #utf8; 103 | yourself. 104 | 105 | session := ExampleDescriptorSystem sessionForLogin: login. 106 | session login. 107 | 108 | session createTables . 109 | ] 110 | 111 | { #category : #tests } 112 | ExampleTestOracle >> testGroupBy [ 113 | | login session | 114 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 115 | 116 | login := Login new 117 | database: OraclePlatformADO new; 118 | connectString: 'Driver={Oracle in instantclient_19_3};dbq=itchy.miskoti.si:1521/XE;Uid=c##pharo;Pwd=pharo;'; 119 | username: ''; 120 | password: ''; 121 | encodingStrategy: #utf8; 122 | yourself. 123 | 124 | session := ExampleDescriptorSystem sessionForLogin: login. 125 | session login. 126 | 127 | (((SimpleQuery read: InvoiceItem) 128 | retrieve: [ :e | 129 | e invoice person id]; 130 | retrieve: [ :e | e price sum ]; 131 | groupBy: [ :e | 132 | e invoice person id] 133 | ) executeIn: session) inspect. 134 | 135 | (((SimpleQuery read: InvoiceItem) 136 | retrieve: [ :each | each description]; 137 | retrieve: [ :each | each price average ]; 138 | groupBy: [ :each | each description]) executeIn: session) inspect. 139 | 140 | ] 141 | 142 | { #category : #tests } 143 | ExampleTestOracle >> testOrderBy [ 144 | | login session | 145 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 146 | 147 | login := Login new 148 | database: OraclePlatformADO new; 149 | connectString: 'Driver={Oracle in instantclient_19_3};dbq=itchy.miskoti.si:1521/XE;Uid=c##pharo;Pwd=pharo;'; 150 | username: ''; 151 | password: ''; 152 | encodingStrategy: #utf8; 153 | yourself. 154 | 155 | session := ExampleDescriptorSystem sessionForLogin: login. 156 | session login. 157 | 158 | (((SimpleQuery read: InvoiceItem) 159 | orderBy: [:each | each price descending]; 160 | orderBy: [:each | each invoice person lastName] 161 | ) executeIn: session) inspect. 162 | ] 163 | 164 | { #category : #tests } 165 | ExampleTestOracle >> testRead [ 166 | | login session | 167 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 168 | 169 | login := Login new 170 | database: OraclePlatformADO new; 171 | connectString: 'Driver={Oracle in instantclient_19_3};dbq=itchy.miskoti.si:1521/XE;Uid=c##pharo;Pwd=pharo;'; 172 | username: ''; 173 | password: ''; 174 | encodingStrategy: #utf8; 175 | yourself. 176 | 177 | session := ExampleDescriptorSystem sessionForLogin: login. 178 | session login. 179 | 180 | "As usual, you can read the Invoices by doing session read: Invoice, let’s 181 | print how much each Person donated." 182 | 183 | (session read: Invoice) do: [:each | 184 | Transcript 185 | show: each person firstName, ' ', each person lastName; 186 | show: ': ', each totalPrice printString; 187 | cr. 188 | ]. 189 | 190 | Transcript show: ((session read: Invoice) sum: #totalPrice); cr. 191 | 192 | ] 193 | 194 | { #category : #tests } 195 | ExampleTestOracle >> testWhere [ 196 | | login session | 197 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 198 | 199 | login := Login new 200 | database: OraclePlatformADO new; 201 | connectString: 'Driver={Oracle in instantclient_19_3};dbq=itchy.miskoti.si:1521/XE;Uid=c##pharo;Pwd=pharo;'; 202 | username: ''; 203 | password: ''; 204 | encodingStrategy: #utf8; 205 | yourself. 206 | 207 | session := ExampleDescriptorSystem sessionForLogin: login. 208 | session login. 209 | 210 | (((SimpleQuery read: Invoice) 211 | where: [ :each | each person lastName = 'Goldberg' ] 212 | ) executeIn: session) inspect. 213 | 214 | (((SimpleQuery read: Invoice) 215 | where: [:invoice | 216 | invoice items anySatisfy: [ :item | 217 | item price > 200 ]]) executeIn: session) inspect. 218 | ] 219 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/ExampleTestPostgreSQL.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ExampleTestPostgreSQL, 3 | #superclass : #TestCase, 4 | #category : #'PharoADO-Glorp-Examples' 5 | } 6 | 7 | { #category : #tests } 8 | ExampleTestPostgreSQL >> testAddPersons [ 9 | | login session | 10 | PharoDatabaseAccessor DefaultDriver: P3DatabaseDriver . 11 | 12 | login := Login new 13 | database: PostgreSQLPlatform new; 14 | username: 'pharo'; 15 | password: 'pharo'; 16 | connectString: 'localhost:5432_myDB'; 17 | encodingStrategy: #utf8; 18 | yourself. 19 | 20 | session := ExampleDescriptorSystem sessionForLogin: login. 21 | session login. 22 | 23 | "We will create some sample data for our example, and for the Persons we 24 | will save three instances named after famous Smalltalkers, with random addresses, so don’t mail them." 25 | 26 | session inUnitOfWorkDo: [ 27 | { 'Dan'. 'Alan'. 'Adele' } 28 | with: {'Ingalls'. 'Kay'. 'Goldberg'} 29 | do: [ :firstName :lastName | 30 | | person | 31 | person := (Person new 32 | firstName: firstName; 33 | lastName: lastName). 34 | person addresses add: ( 35 | Address new 36 | street: (1 to: 1000) atRandom printString 37 | , ' Random Avenue'; 38 | zip: (1000 to: 9000 by: 100) atRandom; 39 | yourself). 40 | session register: person 41 | ] 42 | ]. 43 | 44 | "Now we created the sample Persons, let’s add an extra address for Alan." 45 | 46 | session inUnitOfWorkDo: [ 47 | | alan | 48 | alan := session 49 | readOneOf: Person 50 | where: [ :each | each firstName = 'Alan' ]. 51 | alan addresses 52 | add: (Address new 53 | street: '1025 Westwood Blv, 2nd Floor, Los Angeles, CA'; 54 | zip: 90024; 55 | yourself) 56 | ]. 57 | 58 | "As you can see, we didn’t have to register the newly created address because 59 | the Person was read inside of a UnitOfWork. If we read the instance again 60 | and inspect its addresses we will find the new instance is there." 61 | 62 | (session 63 | readOneOf: Person 64 | where: [ :each | each firstName = 'Alan' ]) addresses inspect. 65 | 66 | "We can now procceed to create an few instances of Invoice. Let’s create one 67 | invoice for each person in the database with two items describing donations to the Pharo Consortium2 and Association3 with randomized amounts (within a certain range) for each one." 68 | 69 | session inUnitOfWorkDo: [ 70 | (session read: Person) do: [:person | 71 | | invoice | 72 | invoice := Invoice new 73 | issueDate: Date today; 74 | person: person; 75 | address: person addresses atRandom. 76 | invoice 77 | addItem: (InvoiceItem 78 | description: 'Pharo Consortium donation' 79 | price: (1000s to: 4000s by: 1000s) atRandom); 80 | addItem: (InvoiceItem 81 | description: 'Pharo Association donation' 82 | price: (20s to: 100s by: 10s) atRandom); yourself. 83 | session register: invoice. 84 | ] 85 | ]. 86 | 87 | 88 | "As usual, you can read the Invoices by doing session read: Invoice, let’s 89 | print how much each Person donated." 90 | 91 | (session read: Invoice) do: [:each | 92 | Transcript 93 | show: each person firstName, ' ', each person lastName; 94 | show: ': ', each totalPrice printString; 95 | cr. 96 | ] 97 | 98 | 99 | ] 100 | 101 | { #category : #tests } 102 | ExampleTestPostgreSQL >> testCreateTables [ 103 | | login session | 104 | PharoDatabaseAccessor DefaultDriver: P3DatabaseDriver . 105 | 106 | login := Login new 107 | database: PostgreSQLPlatform new; 108 | username: 'pharo'; 109 | password: 'pharo'; 110 | connectString: 'localhost:5432_myDB'; 111 | encodingStrategy: #utf8; 112 | yourself. 113 | 114 | session := ExampleDescriptorSystem sessionForLogin: login. 115 | session login. 116 | 117 | session createTables . 118 | ] 119 | 120 | { #category : #tests } 121 | ExampleTestPostgreSQL >> testGroupBy [ 122 | | login session | 123 | PharoDatabaseAccessor DefaultDriver: P3DatabaseDriver . 124 | 125 | login := Login new 126 | database: PostgreSQLPlatform new; 127 | username: 'pharo'; 128 | password: 'pharo'; 129 | connectString: 'localhost:5432_myDB'; 130 | encodingStrategy: #utf8; 131 | yourself. 132 | 133 | session := ExampleDescriptorSystem sessionForLogin: login. 134 | session login. 135 | 136 | (((SimpleQuery read: InvoiceItem) 137 | retrieve: [ :e | 138 | e invoice person id]; 139 | retrieve: [ :e | e price sum ]; 140 | groupBy: [ :e | 141 | e invoice person id] 142 | ) executeIn: session) inspect. 143 | 144 | (((SimpleQuery read: InvoiceItem) 145 | retrieve: [ :each | each description]; 146 | retrieve: [ :each | each price average ]; 147 | groupBy: [ :each | each description]) executeIn: session) inspect. 148 | 149 | ] 150 | 151 | { #category : #tests } 152 | ExampleTestPostgreSQL >> testOrderBy [ 153 | | login session | 154 | PharoDatabaseAccessor DefaultDriver: P3DatabaseDriver . 155 | 156 | login := Login new 157 | database: PostgreSQLPlatform new; 158 | username: 'pharo'; 159 | password: 'pharo'; 160 | connectString: 'localhost:5432_myDB'; 161 | encodingStrategy: #utf8; 162 | yourself. 163 | 164 | session := ExampleDescriptorSystem sessionForLogin: login. 165 | session login. 166 | 167 | (((SimpleQuery read: InvoiceItem) 168 | orderBy: [:each | each price descending]; 169 | orderBy: [:each | each invoice person lastName] 170 | ) executeIn: session) inspect. 171 | ] 172 | 173 | { #category : #tests } 174 | ExampleTestPostgreSQL >> testRead [ 175 | | login session | 176 | PharoDatabaseAccessor DefaultDriver: P3DatabaseDriver . 177 | 178 | login := Login new 179 | database: PostgreSQLPlatform new; 180 | username: 'pharo'; 181 | password: 'pharo'; 182 | connectString: 'localhost:5432_myDB'; 183 | encodingStrategy: #utf8; 184 | yourself. 185 | 186 | session := ExampleDescriptorSystem sessionForLogin: login. 187 | session login. 188 | 189 | "As usual, you can read the Invoices by doing session read: Invoice, let’s 190 | print how much each Person donated." 191 | 192 | (session read: Invoice) do: [:each | 193 | Transcript 194 | show: each person firstName, ' ', each person lastName; 195 | show: ': ', each totalPrice printString; 196 | cr. 197 | ]. 198 | 199 | Transcript show: ((session read: Invoice) sum: #totalPrice); cr. 200 | 201 | ] 202 | 203 | { #category : #tests } 204 | ExampleTestPostgreSQL >> testWhere [ 205 | | login session | 206 | PharoDatabaseAccessor DefaultDriver: P3DatabaseDriver . 207 | 208 | login := Login new 209 | database: PostgreSQLPlatform new; 210 | username: 'pharo'; 211 | password: 'pharo'; 212 | connectString: 'localhost:5432_myDB'; 213 | encodingStrategy: #utf8; 214 | yourself. 215 | 216 | session := ExampleDescriptorSystem sessionForLogin: login. 217 | session login. 218 | 219 | (((SimpleQuery read: Invoice) 220 | where: [ :each | each person lastName = 'Goldberg' ] 221 | ) executeIn: session) inspect. 222 | 223 | (((SimpleQuery read: Invoice) 224 | where: [:invoice | 225 | invoice items anySatisfy: [ :item | 226 | item price > 200 ]]) executeIn: session) inspect. 227 | ] 228 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/ExampleTestSQLServer.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ExampleTestSQLServer, 3 | #superclass : #TestCase, 4 | #category : #'PharoADO-Glorp-Examples' 5 | } 6 | 7 | { #category : #tests } 8 | ExampleTestSQLServer >> testAddPersons [ 9 | | login session | 10 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 11 | 12 | login := Login new 13 | database: SQLServerPlatformN new; 14 | connectString: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;'; 15 | username: ''; 16 | password: ''; 17 | encodingStrategy: #utf8; 18 | yourself. 19 | 20 | session := ExampleDescriptorSystemSQLServer sessionForLogin: login. 21 | session login. 22 | 23 | "We will create some sample data for our example, and for the Persons we 24 | will save three instances named after famous Smalltalkers, with random addresses, so don’t mail them." 25 | 26 | session inUnitOfWorkDo: [ 27 | { 'Dan'. 'Alan'. 'Boštjan' } 28 | with: {'Ingalls'. 'Kay'. 'Črnilogar Žmavc'} 29 | do: [ :firstName :lastName | 30 | | person | 31 | person := (Person new 32 | firstName: firstName; 33 | lastName: lastName). 34 | person addresses add: ( 35 | Address new 36 | street: (1 to: 1000) atRandom printString 37 | , ' Random Avenue'; 38 | zip: (1000 to: 9000 by: 100) atRandom; 39 | yourself). 40 | session register: person 41 | ] 42 | ]. 43 | 44 | "Now we created the sample Persons, let’s add an extra address for Alan." 45 | 46 | session inUnitOfWorkDo: [ 47 | | alan | 48 | alan := session 49 | readOneOf: Person 50 | where: [ :each | each firstName = 'Alan' ]. 51 | alan addresses 52 | add: (Address new 53 | street: '1025 Westwood Blv, 2nd Floor, Los Angeles, CA'; 54 | zip: 90024; 55 | yourself) 56 | ]. 57 | 58 | "As you can see, we didn’t have to register the newly created address because 59 | the Person was read inside of a UnitOfWork. If we read the instance again 60 | and inspect its addresses we will find the new instance is there." 61 | 62 | (session 63 | readOneOf: Person 64 | where: [ :each | each firstName = 'Alan' ]) addresses inspect. 65 | 66 | "We can now procceed to create an few instances of Invoice. Let’s create one 67 | invoice for each person in the database with two items describing donations to the Pharo Consortium2 and Association3 with randomized amounts (within a certain range) for each one." 68 | 69 | session inUnitOfWorkDo: [ 70 | (session read: Person) do: [:person | 71 | | invoice | 72 | invoice := Invoice new 73 | issueDate: Date today; 74 | person: person; 75 | address: person addresses atRandom. 76 | invoice 77 | addItem: (InvoiceItem 78 | description: 'Pharo Consortium donation' 79 | price: (1000s to: 4000s by: 1000s) atRandom); 80 | addItem: (InvoiceItem 81 | description: 'Pharo Association donation' 82 | price: (20s to: 100s by: 10s) atRandom); yourself. 83 | session register: invoice. 84 | ] 85 | ]. 86 | 87 | 88 | 89 | 90 | ] 91 | 92 | { #category : #tests } 93 | ExampleTestSQLServer >> testCreateTables [ 94 | | login session | 95 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 96 | 97 | login := Login new 98 | database: SQLServerPlatform new; 99 | connectString: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;'; 100 | username: ''; 101 | password: ''; 102 | encodingStrategy: #utf8; 103 | yourself. 104 | 105 | session := ExampleDescriptorSystemSQLServer sessionForLogin: login. 106 | session login. 107 | 108 | session createTables . 109 | ] 110 | 111 | { #category : #tests } 112 | ExampleTestSQLServer >> testGroupBy [ 113 | | login session | 114 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 115 | 116 | login := Login new 117 | database: SQLServerPlatform new; 118 | connectString: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;'; 119 | username: ''; 120 | password: ''; 121 | encodingStrategy: #utf8; 122 | yourself. 123 | 124 | session := ExampleDescriptorSystemSQLServer sessionForLogin: login. 125 | session login. 126 | 127 | (((SimpleQuery read: InvoiceItem) 128 | retrieve: [ :e | 129 | e invoice person id]; 130 | retrieve: [ :e | e price sum ]; 131 | groupBy: [ :e | 132 | e invoice person id] 133 | ) executeIn: session) inspect. 134 | 135 | (((SimpleQuery read: InvoiceItem) 136 | retrieve: [ :each | each description]; 137 | retrieve: [ :each | each price average ]; 138 | groupBy: [ :each | each description]) executeIn: session) inspect. 139 | 140 | ] 141 | 142 | { #category : #tests } 143 | ExampleTestSQLServer >> testOrderBy [ 144 | | login session | 145 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 146 | 147 | login := Login new 148 | database: SQLServerPlatform new; 149 | connectString: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;'; 150 | username: ''; 151 | password: ''; 152 | encodingStrategy: #utf8; 153 | yourself. 154 | 155 | session := ExampleDescriptorSystemSQLServer sessionForLogin: login. 156 | session login. 157 | 158 | (((SimpleQuery read: InvoiceItem) 159 | orderBy: [:each | each price descending]; 160 | orderBy: [:each | each invoice person lastName] 161 | ) executeIn: session) inspect. 162 | ] 163 | 164 | { #category : #tests } 165 | ExampleTestSQLServer >> testRead [ 166 | | login session | 167 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 168 | 169 | login := Login new 170 | database: SQLServerPlatform new; 171 | connectString: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;'; 172 | username: ''; 173 | password: ''; 174 | encodingStrategy: #utf8; 175 | yourself. 176 | 177 | session := ExampleDescriptorSystemSQLServer sessionForLogin: login. 178 | session login. 179 | 180 | "As usual, you can read the Invoices by doing session read: Invoice, let’s 181 | print how much each Person donated." 182 | 183 | (session read: Invoice) do: [:each | 184 | Transcript 185 | show: each person firstName, ' ', each person lastName; 186 | show: ': ', each totalPrice printString; 187 | cr. 188 | ]. 189 | 190 | Transcript show: ((session read: Invoice) sum: #totalPrice); cr. 191 | 192 | ] 193 | 194 | { #category : #tests } 195 | ExampleTestSQLServer >> testWhere [ 196 | | login session | 197 | PharoDatabaseAccessor DefaultDriver: ADODatabaseDriver . 198 | 199 | login := Login new 200 | database: SQLServerPlatform new; 201 | connectString: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;'; 202 | username: ''; 203 | password: ''; 204 | encodingStrategy: #utf8; 205 | yourself. 206 | 207 | session := ExampleDescriptorSystemSQLServer sessionForLogin: login. 208 | session login. 209 | 210 | (((SimpleQuery read: Invoice) 211 | where: [ :each | each person lastName = 'Goldberg' ] 212 | ) executeIn: session) inspect. 213 | 214 | (((SimpleQuery read: Invoice) 215 | where: [:invoice | 216 | invoice items anySatisfy: [ :item | 217 | item price > 200 ]]) executeIn: session) inspect. 218 | ] 219 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/Invoice.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #Invoice, 3 | #superclass : #ExampleObject, 4 | #instVars : [ 5 | 'issueDate', 6 | 'person', 7 | 'address', 8 | 'items' 9 | ], 10 | #category : #'PharoADO-Glorp-Examples' 11 | } 12 | 13 | { #category : #adding } 14 | Invoice >> addItem: anInvoiceItem [ 15 | anInvoiceItem invoice: self. 16 | self items add: anInvoiceItem 17 | ] 18 | 19 | { #category : #accessing } 20 | Invoice >> address [ 21 | ^ address 22 | ] 23 | 24 | { #category : #accessing } 25 | Invoice >> address: anObject [ 26 | address := anObject 27 | ] 28 | 29 | { #category : #initialization } 30 | Invoice >> initialize [ 31 | super initialize. 32 | items := OrderedCollection new. 33 | ] 34 | 35 | { #category : #accessing } 36 | Invoice >> issueDate [ 37 | ^ issueDate 38 | ] 39 | 40 | { #category : #accessing } 41 | Invoice >> issueDate: anObject [ 42 | issueDate := anObject 43 | ] 44 | 45 | { #category : #accessing } 46 | Invoice >> items [ 47 | ^ items 48 | ] 49 | 50 | { #category : #accessing } 51 | Invoice >> items: anObject [ 52 | items := anObject 53 | ] 54 | 55 | { #category : #accessing } 56 | Invoice >> person [ 57 | ^ person 58 | ] 59 | 60 | { #category : #accessing } 61 | Invoice >> person: anObject [ 62 | person := anObject 63 | ] 64 | 65 | { #category : #accessing } 66 | Invoice >> totalPrice [ 67 | ^ self items sum: #price 68 | ] 69 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/InvoiceItem.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #InvoiceItem, 3 | #superclass : #ExampleObject, 4 | #instVars : [ 5 | 'invoice', 6 | 'description', 7 | 'price' 8 | ], 9 | #category : #'PharoADO-Glorp-Examples' 10 | } 11 | 12 | { #category : #'as yet unclassified' } 13 | InvoiceItem class >> description: aString price: aNumber [ 14 | ^ self new 15 | description: aString; 16 | price: aNumber; 17 | yourself 18 | ] 19 | 20 | { #category : #accessing } 21 | InvoiceItem >> description [ 22 | ^ description 23 | ] 24 | 25 | { #category : #accessing } 26 | InvoiceItem >> description: anObject [ 27 | description := anObject 28 | ] 29 | 30 | { #category : #accessing } 31 | InvoiceItem >> invoice [ 32 | ^ invoice 33 | ] 34 | 35 | { #category : #accessing } 36 | InvoiceItem >> invoice: anObject [ 37 | invoice := anObject 38 | ] 39 | 40 | { #category : #accessing } 41 | InvoiceItem >> price [ 42 | ^ price 43 | ] 44 | 45 | { #category : #accessing } 46 | InvoiceItem >> price: anObject [ 47 | price := anObject 48 | ] 49 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/OraclePlatformADO.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #OraclePlatformADO, 3 | #superclass : #OraclePlatform, 4 | #category : #'PharoADO-Glorp-Platforms' 5 | } 6 | 7 | { #category : #types } 8 | OraclePlatformADO >> decimal [ 9 | ^self typeNamed: #decimal ifAbsentPut: [GlorpDecimalType new typeString: 'decimal']. 10 | ] 11 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/Person.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #Person, 3 | #superclass : #ExampleObject, 4 | #instVars : [ 5 | 'firstName', 6 | 'email', 7 | 'lastName', 8 | 'birthDate', 9 | 'addresses', 10 | 'invoices', 11 | 'tags' 12 | ], 13 | #category : #'PharoADO-Glorp-Examples' 14 | } 15 | 16 | { #category : #accessing } 17 | Person >> addresses [ 18 | ^ addresses 19 | ] 20 | 21 | { #category : #accessing } 22 | Person >> addresses: anObject [ 23 | addresses := anObject 24 | ] 25 | 26 | { #category : #accessing } 27 | Person >> birthDate [ 28 | ^ birthDate 29 | ] 30 | 31 | { #category : #accessing } 32 | Person >> birthDate: anObject [ 33 | birthDate := anObject 34 | ] 35 | 36 | { #category : #accessing } 37 | Person >> email [ 38 | ^ email 39 | ] 40 | 41 | { #category : #accessing } 42 | Person >> email: anObject [ 43 | email := anObject 44 | ] 45 | 46 | { #category : #accessing } 47 | Person >> firstName [ 48 | ^ firstName 49 | ] 50 | 51 | { #category : #accessing } 52 | Person >> firstName: anObject [ 53 | firstName := anObject 54 | ] 55 | 56 | { #category : #initialization } 57 | Person >> initialize [ 58 | super initialize. 59 | addresses := OrderedCollection new. 60 | invoices := OrderedCollection new. 61 | ] 62 | 63 | { #category : #accessing } 64 | Person >> invoices [ 65 | ^ invoices 66 | ] 67 | 68 | { #category : #accessing } 69 | Person >> invoices: anObject [ 70 | invoices := anObject 71 | ] 72 | 73 | { #category : #accessing } 74 | Person >> lastName [ 75 | ^ lastName 76 | ] 77 | 78 | { #category : #accessing } 79 | Person >> lastName: anObject [ 80 | lastName := anObject 81 | ] 82 | 83 | { #category : #accessing } 84 | Person >> tags [ 85 | ^ tags 86 | ] 87 | 88 | { #category : #accessing } 89 | Person >> tags: anObject [ 90 | tags := anObject 91 | ] 92 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/SQLServerPlatformN.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #SQLServerPlatformN, 3 | #superclass : #SQLServerPlatform, 4 | #category : #'PharoADO-Glorp-Platforms' 5 | } 6 | 7 | { #category : #constants } 8 | SQLServerPlatformN >> prefixForUnicode [ 9 | ^$N 10 | ] 11 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/WideString.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #WideString } 2 | 3 | { #category : #'*PharoADO-Glorp' } 4 | WideString >> glorpPrintSQLOn: aCommand [ 5 | | platform requiresEscape | 6 | platform := [aCommand platform] on: MessageNotUnderstood do: [:ex | ex return: nil]. 7 | (platform respondsTo: #prefixForUnicode) ifTrue: [ aCommand nextPut: platform prefixForUnicode ]. 8 | aCommand nextPut: $'. 9 | 1 10 | to: self size 11 | do: 12 | [:i | 13 | | char | 14 | char := self at: i. 15 | requiresEscape := platform isNil ifTrue: [false] ifFalse: [platform requiresEscapeFor: char]. 16 | requiresEscape 17 | ifTrue: [aCommand nextPutAll: (aCommand platform escapeFor: char)] 18 | ifFalse: [aCommand nextPut: char]]. 19 | aCommand nextPut: $'. 20 | ] 21 | -------------------------------------------------------------------------------- /src/PharoADO-Glorp/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'PharoADO-Glorp' } 2 | -------------------------------------------------------------------------------- /src/PharoADO/ADOClient.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ADOClient, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'connection', 6 | 'session', 7 | 'settings', 8 | 'properties' 9 | ], 10 | #pools : [ 11 | 'ADOConstants' 12 | ], 13 | #category : #'PharoADO-Core' 14 | } 15 | 16 | { #category : #private } 17 | ADOClient >> clearSession [ 18 | session := nil 19 | ] 20 | 21 | { #category : #'initialize-release' } 22 | ADOClient >> close [ 23 | "Cleanly close my connection with the server" 24 | connection 25 | ifNotNil: [ 26 | [ connection dispatch: 'Close' ] on: Error do: [ ]. 27 | self clearSession. 28 | connection := nil ] 29 | ] 30 | 31 | { #category : #public } 32 | ADOClient >> connect [ 33 | self ensureOpen. 34 | session := 999999 atRandom. 35 | 36 | ] 37 | 38 | { #category : #accessing } 39 | ADOClient >> connectString [ 40 | "Return the password of my database connection. 41 | For no password, nil is used." 42 | 43 | ^ settings at: #connectString ifAbsent: [ nil ] 44 | ] 45 | 46 | { #category : #accessing } 47 | ADOClient >> connectString: aString [ 48 | "Set the password to use when connecting. 49 | Set to nil or don't set to use no password." 50 | 51 | settings at: #connectString put: aString 52 | 53 | ] 54 | 55 | { #category : #accessing } 56 | ADOClient >> connection [ 57 | ^ connection 58 | ] 59 | 60 | { #category : #accessing } 61 | ADOClient >> database [ 62 | "Return the database name I (want to) connect to. 63 | For the default, nil is used." 64 | 65 | ^ settings at: #database ifAbsent: [ nil ] 66 | ] 67 | 68 | { #category : #'initialize-release' } 69 | ADOClient >> database: string [ 70 | "Set the name of the database to connect to. 71 | Set to nil or do not set to use the default." 72 | 73 | settings at: #database put: string 74 | ] 75 | 76 | { #category : #private } 77 | ADOClient >> ensureConnected [ 78 | self isConnected 79 | ifFalse: [ self connect ] 80 | ] 81 | 82 | { #category : #private } 83 | ADOClient >> ensureOpen [ 84 | connection ifNil: [ self open ] 85 | ] 86 | 87 | { #category : #initialization } 88 | ADOClient >> initialize [ 89 | super initialize. 90 | 91 | settings := IdentityDictionary new. 92 | properties := Dictionary new. 93 | ] 94 | 95 | { #category : #testing } 96 | ADOClient >> isConnected [ 97 | "Return true when my connection is in working order (from my end)" 98 | 99 | ^ connection notNil and: [ 100 | (connection isConnected) and: [ 101 | session notNil 102 | ] 103 | ] 104 | ] 105 | 106 | { #category : #'instance creation' } 107 | ADOClient >> open [ 108 | "Open my connection with the server (do not yet #connect)" 109 | | usr pwd | 110 | self close. 111 | usr := ''. 112 | pwd := ''. 113 | (self user notNil) ifTrue: [ usr := self user ]. 114 | (self password notNil) ifTrue: [ pwd := self password ]. 115 | 116 | connection := ADOConnection createInstance. 117 | connection open: self connectString user: usr password: pwd. 118 | 119 | ] 120 | 121 | { #category : #accessing } 122 | ADOClient >> password [ 123 | "Return the password of my database connection. 124 | For no password, nil is used." 125 | 126 | ^ settings at: #password ifAbsent: [ nil ] 127 | ] 128 | 129 | { #category : #accessing } 130 | ADOClient >> password: string [ 131 | "Set the password to use when connecting. 132 | Set to nil or don't set to use no password." 133 | 134 | settings at: #password put: string 135 | ] 136 | 137 | { #category : #accessing } 138 | ADOClient >> query: aString [ 139 | | rst fields data | 140 | self ensureConnected. 141 | rst := connection execute: aString. 142 | 143 | rst ifNil: [ ^ #() ]. 144 | rst state = 0 145 | ifTrue: [ ^ #() ]. 146 | (rst eof and: [ rst bof ]) 147 | ifTrue: [ ^ #() ]. 148 | 149 | fields := rst fields. 150 | data := Array 151 | streamContents: [ :outerOut | 152 | [ rst eof ] 153 | whileFalse: [ outerOut 154 | nextPut: 155 | (Array 156 | streamContents: [ :innerOut | 157 | 1 to: fields count do: 158 | [ :idx | innerOut nextPut: (fields item: idx) value ] ]). 159 | rst moveNext ] ]. 160 | ^ data 161 | 162 | 163 | " rst := ADORecordset createInstance. 164 | rst 165 | open: aString 166 | activeConnection: connection 167 | cursorType: 3 168 | lockType: 1 169 | options: 0. 170 | " 171 | ] 172 | 173 | { #category : #accessing } 174 | ADOClient >> queryEncoding [ 175 | "Return the name of the encoder used" 176 | 177 | ^ 'utf8' 178 | ] 179 | 180 | { #category : #accessing } 181 | ADOClient >> user [ 182 | "Return the user of my database connection. 183 | For the default, nil is used." 184 | 185 | ^ settings at: #user ifAbsent: [ nil ] 186 | ] 187 | 188 | { #category : #accessing } 189 | ADOClient >> user: string [ 190 | "Set the user to use when connecting. 191 | Set to nil or don't set to use the default." 192 | 193 | settings at: #user put: string 194 | ] 195 | -------------------------------------------------------------------------------- /src/PharoADO/ADOClientTests.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ADOClientTests, 3 | #superclass : #TestCase, 4 | #category : #'PharoADO-Tests' 5 | } 6 | 7 | { #category : #tests } 8 | ADOClientTests >> testCreateTable [ 9 | | conn | 10 | conn := ADOConnection createInstance . 11 | conn 12 | open: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;;' 13 | user: '' 14 | password: ''. 15 | 16 | conn execute: 'CREATE TABLE [dbo].[myTable] 17 | ( 18 | [Id] INT NOT NULL PRIMARY KEY IDENTITY, 19 | [Description] NVARCHAR(50) NULL, 20 | [Date] DATE NULL, 21 | [DecimalValue] DECIMAL(18, 2) NULL, 22 | [FloatValue] FLOAT NULL 23 | )'. 24 | conn close. 25 | 26 | ] 27 | 28 | { #category : #tests } 29 | ADOClientTests >> testDelete [ 30 | | conn rst | 31 | conn := ADOConnection createInstance. 32 | conn 33 | open: 34 | 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;AutoTranslate=true;' 35 | user: '' 36 | password: ''. 37 | rst := ADORecordset createInstance. 38 | rst 39 | open: 'SELECT * FROM [dbo].[myTable]' 40 | activeConnection: conn 41 | cursorType: 2 42 | lockType: 3 43 | options: -1. 44 | 45 | rst addNew. 46 | (rst fields item: 2) value: 'This is unimportant record'. 47 | rst update. 48 | (rst fields item: 2) value inspect. 49 | rst movePrevious; moveNext . 50 | rst delete. 51 | 52 | rst close. 53 | conn close 54 | ] 55 | 56 | { #category : #tests } 57 | ADOClientTests >> testInsert [ 58 | | conn rst data | 59 | conn := ADOConnection createInstance. 60 | conn 61 | open: 62 | 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;AutoTranslate=true;' 63 | user: '' 64 | password: ''. 65 | rst := ADORecordset createInstance. 66 | rst 67 | open: 'SELECT * FROM [dbo].[myTable]' 68 | activeConnection: conn 69 | cursorType: 2 70 | lockType: 3 71 | options: -1. 72 | 73 | rst addNew. 74 | data := {'#Id'. 'This is new record'. Date today. 12345.6789s3. 12345.67 }. 75 | 2 to: rst fields count do: [ :idx | (rst fields item: idx) value: (data at: idx) ]. 76 | rst update. 77 | 78 | rst close. 79 | conn close 80 | ] 81 | 82 | { #category : #tests } 83 | ADOClientTests >> testNull [ 84 | | conn rst data | 85 | conn := ADOConnection createInstance . 86 | conn 87 | open: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;;' 88 | user: '' 89 | password: ''. 90 | 91 | rst := conn execute: 'SELECT @@IDENTITY'. 92 | data := (rst fields item: 1) value. 93 | 94 | rst close. 95 | conn close. 96 | 97 | ] 98 | 99 | { #category : #tests } 100 | ADOClientTests >> testRstInsertOracle [ 101 | | conn rst rstNextVal | 102 | conn := ADOConnection createInstance . 103 | conn 104 | open: 'Driver={Oracle in instantclient_19_3};dbq=localhost:1521/XE;Uid=c##pharo;Pwd=pharo;' 105 | user: '' 106 | password: ''. 107 | rst := ADORecordset createInstance . 108 | rstNextVal := ADORecordset createInstance . 109 | rstNextVal open: 'SELECT PERSON_ID_SEQ.NEXTVAL FROM DUAL' activeConnection: conn cursorType: 3 lockType: 1 options: -1. 110 | rst open: 'SELECT * FROM PERSON' activeConnection: conn cursorType: 2 lockType: 3 options: -1. 111 | 112 | rst addNew. 113 | (rst fields item: 1) value: (rstNextVal fields item: 1) value asInteger. 114 | (rst fields item: 2) value: 'John'. 115 | rst update. 116 | rst moveFirst. 117 | [rst eof] whileFalse: [ 118 | 1 to: rst fields count do: [ :idx | 119 | Transcript show: (rst fields item: idx) name; show: ':'; 120 | show: (rst fields item: idx) value; cr]. 121 | Transcript cr. 122 | rst moveNext ]. 123 | 124 | rstNextVal close. 125 | rst close. 126 | conn close. 127 | ] 128 | 129 | { #category : #tests } 130 | ADOClientTests >> testRstInsertSQLServer [ 131 | | conn rst | 132 | conn := ADOConnection createInstance . 133 | conn 134 | open: 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;' 135 | user: '' 136 | password: ''. 137 | rst := ADORecordset createInstance . 138 | rst open: 'SELECT * FROM PERSON' activeConnection: conn cursorType: 2 lockType: 3 options: -1. 139 | 140 | rst addNew. 141 | (rst fields item: 2) value: 'John'. 142 | rst update. 143 | rst moveFirst. 144 | [rst eof] whileFalse: [ 145 | 1 to: rst fields count do: [ :idx | 146 | Transcript show: (rst fields item: idx) name; show: ':'; 147 | show: (rst fields item: idx) value; cr]. 148 | Transcript cr. 149 | rst moveNext ]. 150 | 151 | rst close. 152 | conn close. 153 | ] 154 | 155 | { #category : #tests } 156 | ADOClientTests >> testSelect [ 157 | | conn rst data | 158 | conn := ADOConnection createInstance. 159 | conn 160 | open: 161 | 'Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=SSPI;AutoTranslate=true;' 162 | user: '' 163 | password: ''. 164 | rst := ADORecordset createInstance. 165 | rst 166 | open: 'SELECT * FROM [dbo].[myTable]' 167 | activeConnection: conn 168 | cursorType: 3 169 | lockType: 1 170 | options: -1. 171 | 172 | data := Array streamContents: [ :stream | 173 | [ rst eof ] whileFalse: [ 174 | stream nextPut: ((1 to: rst fields count) collect: [ :idx | (rst fields item: idx) value ]). 175 | rst moveNext. 176 | ]. 177 | ]. 178 | data inspect. 179 | 180 | rst close. 181 | conn close 182 | ] 183 | -------------------------------------------------------------------------------- /src/PharoADO/ADOConnection.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ADOConnection, 3 | #superclass : #ADOObjects, 4 | #category : #'PharoADO-Core' 5 | } 6 | 7 | { #category : #'as yet unclassified' } 8 | ADOConnection class >> createInstance [ 9 | Ole32Lib uniqueInstance initLibrary. 10 | ^self new 11 | dispatchInstance: (COMDispatchInstance createInstanceByName: 'ADODB.Connection') 12 | ] 13 | 14 | { #category : #transactions } 15 | ADOConnection >> beginTransaction [ 16 | dispatchInstance 17 | dispatch: 'BeginTrans' 18 | 19 | ] 20 | 21 | { #category : #'initialize-release' } 22 | ADOConnection >> close [ 23 | dispatchInstance 24 | dispatch: 'Close' 25 | ] 26 | 27 | { #category : #transactions } 28 | ADOConnection >> commitTransaction [ 29 | dispatchInstance 30 | dispatch: 'CommitTrans' 31 | 32 | ] 33 | 34 | { #category : #running } 35 | ADOConnection >> execute: aString [ 36 | | rst | 37 | rst := ADORecordset new. 38 | rst dispatchInstance: ( 39 | dispatchInstance 40 | dispatch: 'Execute' 41 | withArguments: { aString. 0. 16r1 }). 42 | ^rst 43 | ] 44 | 45 | { #category : #testing } 46 | ADOConnection >> isConnected [ 47 | ^ (dispatchInstance propertyNamed: 'State') = 1 48 | ] 49 | 50 | { #category : #protocol } 51 | ADOConnection >> open: aConnectString [ 52 | dispatchInstance 53 | dispatch: 'Open' 54 | withArguments: { aConnectString } 55 | ] 56 | 57 | { #category : #'as yet unclassified' } 58 | ADOConnection >> open: aConnectString user: aUser password: aPassword [ 59 | dispatchInstance 60 | dispatch: 'Open' 61 | withArguments: { aConnectString . aUser . aPassword } 62 | ] 63 | 64 | { #category : #transactions } 65 | ADOConnection >> rollbackTransaction [ 66 | dispatchInstance 67 | dispatch: 'RollbackTrans' 68 | 69 | ] 70 | -------------------------------------------------------------------------------- /src/PharoADO/ADOConstants.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ADOConstants, 3 | #superclass : #SharedPool, 4 | #classVars : [ 5 | 'ADCmdUnspecified', 6 | 'ADLockBatchOptimistic', 7 | 'ADLockOptimistic', 8 | 'ADLockPessimistic', 9 | 'ADLockReadOnly', 10 | 'ADLockUnspecified', 11 | 'ADOpenDynamic', 12 | 'ADOpenForwardOnly', 13 | 'ADOpenKeyset', 14 | 'ADOpenStatic', 15 | 'ADOpenUnspecified' 16 | ], 17 | #category : #'PharoADO-Core' 18 | } 19 | 20 | { #category : #'class initialization' } 21 | ADOConstants class >> initialize [ 22 | self 23 | initializeLockConstants; 24 | initializeOpenConstants. 25 | 26 | ADCmdUnspecified := -1 27 | ] 28 | 29 | { #category : #'class initialization' } 30 | ADOConstants class >> initializeLockConstants [ 31 | ADLockUnspecified := -1. 32 | ADLockReadOnly := 1. 33 | ADLockPessimistic := 2. 34 | ADLockOptimistic := 3. 35 | ADLockBatchOptimistic := 4. 36 | ] 37 | 38 | { #category : #'class initialization' } 39 | ADOConstants class >> initializeOpenConstants [ 40 | ADOpenDynamic := 2. 41 | ADOpenForwardOnly := 0. 42 | ADOpenKeyset := 1. 43 | ADOpenStatic := 3. 44 | ADOpenUnspecified := -1 45 | ] 46 | -------------------------------------------------------------------------------- /src/PharoADO/ADOField.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ADOField, 3 | #superclass : #ADOObjects, 4 | #category : #'PharoADO-Core' 5 | } 6 | 7 | { #category : #accessing } 8 | ADOField >> name [ 9 | ^dispatchInstance 10 | propertyNamed: 'Name' 11 | ] 12 | 13 | { #category : #accessing } 14 | ADOField >> name: aString [ 15 | ^dispatchInstance 16 | propertyNamed: 'Name' 17 | put: aString 18 | ] 19 | 20 | { #category : #evaluating } 21 | ADOField >> value [ 22 | ^dispatchInstance 23 | propertyNamed: 'Value' 24 | ] 25 | 26 | { #category : #accessing } 27 | ADOField >> value: aValue [ 28 | ^dispatchInstance 29 | propertyNamed: 'Value' 30 | put: aValue 31 | ] 32 | -------------------------------------------------------------------------------- /src/PharoADO/ADOFields.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ADOFields, 3 | #superclass : #ADOObjects, 4 | #instVars : [ 5 | 'items', 6 | 'fieldsMap' 7 | ], 8 | #category : #'PharoADO-Core' 9 | } 10 | 11 | { #category : #accessing } 12 | ADOFields >> count [ 13 | ^dispatchInstance 14 | propertyNamed: 'Count' 15 | ] 16 | 17 | { #category : #'private-accessing' } 18 | ADOFields >> fieldsMap [ 19 | fieldsMap 20 | ifNil: [ fieldsMap := OrderedDictionary 21 | withAll: (self items collect: [ :field | field name -> field ]) ]. 22 | ^ fieldsMap 23 | ] 24 | 25 | { #category : #accessing } 26 | ADOFields >> item: anInteger [ 27 | ^ (self items 28 | at: anInteger 29 | ifAbsent: [ nil ]) 30 | ifNil: 31 | [ | field | 32 | field := ADOField new. 33 | field 34 | dispatchInstance: 35 | (dispatchInstance 36 | propertyNamed: 'Item' 37 | withArguments: {(anInteger - 1)}). 38 | self items at: anInteger put: field ] 39 | ] 40 | 41 | { #category : #accessing } 42 | ADOFields >> items [ 43 | 44 | ^items ifNil: [ items := Array new: self count ] 45 | 46 | ] 47 | 48 | { #category : #'accessing-values' } 49 | ADOFields >> valueAt: aString [ 50 | 51 | ^(self fieldsMap at: aString) value 52 | ] 53 | -------------------------------------------------------------------------------- /src/PharoADO/ADOObjects.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ADOObjects, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'dispatchInstance' 6 | ], 7 | #pools : [ 8 | 'ADOConstants' 9 | ], 10 | #category : #'PharoADO-Core' 11 | } 12 | 13 | { #category : #accessing } 14 | ADOObjects >> dispatchInstance [ 15 | ^ dispatchInstance 16 | ] 17 | 18 | { #category : #accessing } 19 | ADOObjects >> dispatchInstance: anObject [ 20 | dispatchInstance := anObject 21 | ] 22 | -------------------------------------------------------------------------------- /src/PharoADO/ADORecordset.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #ADORecordset, 3 | #superclass : #ADOObjects, 4 | #instVars : [ 5 | 'fields' 6 | ], 7 | #category : #'PharoADO-Core' 8 | } 9 | 10 | { #category : #'instance creation' } 11 | ADORecordset class >> createInstance [ 12 | Ole32Lib uniqueInstance initLibrary. 13 | ^self new 14 | dispatchInstance: (COMDispatchInstance createInstanceByName: 'ADODB.Recordset') 15 | ] 16 | 17 | { #category : #actions } 18 | ADORecordset >> addNew [ 19 | dispatchInstance 20 | dispatch: 'AddNew' 21 | 22 | ] 23 | 24 | { #category : #converting } 25 | ADORecordset >> asOrderedDictionary [ 26 | ^OrderedDictionary 27 | withAll: 28 | ((1 to: self fields count) 29 | collect: [ :index | 30 | | item | 31 | item := self fields item: index. 32 | item name -> item value ]) 33 | ] 34 | 35 | { #category : #testing } 36 | ADORecordset >> atEnd [ 37 | ^self eof 38 | ] 39 | 40 | { #category : #testing } 41 | ADORecordset >> bof [ 42 | ^dispatchInstance 43 | propertyNamed: 'BOF' 44 | ] 45 | 46 | { #category : #actions } 47 | ADORecordset >> cancel [ 48 | dispatchInstance 49 | dispatch: 'Cancel' 50 | ] 51 | 52 | { #category : #actions } 53 | ADORecordset >> cancelUpdate [ 54 | dispatchInstance 55 | dispatch: 'CancelUpdate' 56 | ] 57 | 58 | { #category : #actions } 59 | ADORecordset >> close [ 60 | self resetFields. 61 | dispatchInstance dispatch: 'Close' 62 | ] 63 | 64 | { #category : #actions } 65 | ADORecordset >> delete [ 66 | dispatchInstance 67 | dispatch: 'Delete' 68 | ] 69 | 70 | { #category : #testing } 71 | ADORecordset >> eof [ 72 | ^dispatchInstance 73 | propertyNamed: 'EOF' 74 | ] 75 | 76 | { #category : #accessing } 77 | ADORecordset >> fields [ 78 | 79 | fields ifNil: [fields := ADOFields new. 80 | fields dispatchInstance: 81 | (dispatchInstance propertyNamed: 'Fields')]. 82 | ^fields 83 | ] 84 | 85 | { #category : #positioning } 86 | ADORecordset >> moveFirst [ 87 | dispatchInstance 88 | dispatch: 'MoveFirst' 89 | ] 90 | 91 | { #category : #positioning } 92 | ADORecordset >> moveLast [ 93 | dispatchInstance 94 | dispatch: 'MoveLast' 95 | ] 96 | 97 | { #category : #positioning } 98 | ADORecordset >> moveNext [ 99 | dispatchInstance 100 | dispatch: 'MoveNext' 101 | ] 102 | 103 | { #category : #positioning } 104 | ADORecordset >> movePrevious [ 105 | dispatchInstance 106 | dispatch: 'MovePrevious' 107 | ] 108 | 109 | { #category : #querying } 110 | ADORecordset >> open: aString activeConnection: anADOConnection [ 111 | ^ self 112 | open: aString 113 | activeConnection: anADOConnection 114 | cursorType: ADOpenForwardOnly 115 | lockType: ADLockReadOnly 116 | options: ADCmdUnspecified 117 | ] 118 | 119 | { #category : #querying } 120 | ADORecordset >> open: aString activeConnection: aConnection cursorType: aCursorType lockType: aLockType options: anOptions [ 121 | self resetFields. 122 | dispatchInstance 123 | dispatch: 'Open' 124 | withArguments: 125 | {aString. 126 | aConnection dispatchInstance. 127 | aCursorType. 128 | aLockType. 129 | anOptions} 130 | ] 131 | 132 | { #category : #accessing } 133 | ADORecordset >> recordCount [ 134 | ^dispatchInstance 135 | propertyNamed: 'RecordCount' 136 | ] 137 | 138 | { #category : #actions } 139 | ADORecordset >> requery [ 140 | dispatchInstance 141 | dispatch: 'Requery' 142 | ] 143 | 144 | { #category : #'initialize-release' } 145 | ADORecordset >> resetFields [ 146 | fields := nil 147 | ] 148 | 149 | { #category : #accessing } 150 | ADORecordset >> state [ 151 | ^dispatchInstance 152 | propertyNamed: 'State' 153 | ] 154 | 155 | { #category : #actions } 156 | ADORecordset >> update [ 157 | dispatchInstance 158 | dispatch: 'Update' 159 | ] 160 | 161 | { #category : #'accessing-values' } 162 | ADORecordset >> valueAt: fieldName [ 163 | ^self fields valueAt: fieldName 164 | ] 165 | -------------------------------------------------------------------------------- /src/PharoADO/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #PharoADO } 2 | --------------------------------------------------------------------------------