c++ a p i user guide -...

160
C++ A P I User Guide Release 6.3

Upload: vandang

Post on 25-Mar-2018

234 views

Category:

Documents


6 download

TRANSCRIPT

Page 1: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

C++ A P I User Guide

Release 6.3

Page 2: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Copyright

C++ A P I User Guide

ObjectStore Release 6.3 for all platforms, October 2005

© 2005 Progress Software Corporation. All rights reserved.

Progress® software products are copyrighted and all rights are reserved by Progress Software Corporation. This manual is also copyrighted and all rights are reserved. This manual may not, in whole or in part, be copied, photocopied, translated, or reduced to any electronic medium or machine-readable form without prior consent, in writing, from Progress Software Corporation.

The information in this manual is subject to change without notice, and Progress Software Corporation assumes no responsibility for any errors that may appear in this document.

The references in this manual to specific platforms supported are subject to change.

A (and design), Allegrix, Allegrix (and design), Apama, Business Empowerment, DataDirect (and design), DataDirect Connect, DataDirect Connect OLE DB, DirectAlert, EasyAsk, EdgeXtend, Empowerment Center, eXcelon, Fathom,, IntelliStream, O (and design), ObjectStore, OpenEdge, PeerDirect, P.I.P., POSSENET, Powered by Progress, Progress, Progress Dynamics, Progress Empowerment Center, Progress Empowerment Program, Progress Fast Track, Progress OpenEdge, Partners in Progress, Partners en Progress, Persistence, Persistence (and design), ProCare, Progress en Partners, Progress in Progress, Progress Profiles, Progress Results, Progress Software Developers Network, ProtoSpeed, ProVision, SequeLink, SmartBeans, SpeedScript, Stylus Studio, Technical Empowerment, WebSpeed, and Your Software, Our Technology-Experience the Connection are registered trademarks of Progress Software Corporation or one of its subsidiaries or affiliates in the U.S. and/or other countries. AccelEvent, A Data Center of Your Very Own, AppsAlive, AppServer, ASPen, ASP-in-a-Box, BusinessEdge, Cache-Forward, DataDirect, DataDirect Connect64, DataDirect Technologies, DataDirect XQuery, DataXtend, Future Proof, ObjectCache, ObjectStore Event Engine, ObjectStore Inspector, ObjectStore Performance Expert, POSSE, ProDataSet, Progress Business Empowerment, Progress DataXtend, Progress for Partners, Progress ObjectStore, PSE Pro, PS Select, SectorAlliance, SmartBrowser, SmartComponent, SmartDataBrowser, SmartDataObjects, SmartDataView, SmartDialog, SmartFolder, SmartFrame, SmartObjects, SmartPanel, SmartQuery, SmartViewer, SmartWindow, WebClient, and Who Makes Progress are trademarks or service marks of Progress Software Corporation or one of its subsidiaries or affiliates in the U.S. and other countries. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. Any other trademarks or trade names contained herein are the property of their respective owners.

ObjectStore includes software developed by the Apache Software Foundation (http://www.apache.org/). Copyright 2000-2003 The Apache Software Foundation. All rights reserved. The names "Ant," "Xerces," and "Apache Software Foundation" must not be used to endorse or promote products derived from the Products without prior written permission. Any product derived from the Products may not be called "Apache", nor may "Apache" appear in their name, without prior written permission. For written permission, please contact [email protected].

September 2005

Page 3: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Contents

Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

Chapter 1 ObjectStore Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

Persistent Storage: Organizational Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

Transient Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

ObjectStore Processes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

ObjectStore’s Memory Mapping Architecture. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

Memory Mapping and Schema Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

Rawfs Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

Threads Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

Programming Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

Chapter 2 Anatomy of an ObjectStore Application. . . . . . . . . . . . . . . . . . . . . . . . . . 21

An Example Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

bookclub.hh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

main.cc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

Line-by-Line Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

Including ObjectStore Header Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

Defining Names for Database Roots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

Declaring a User-Defined Exception. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

Clustering Related Objects in Persistent Memory. . . . . . . . . . . . . . . . . . . . . . . . . . . 26

Using Cross-Database Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

Deleting Persistent Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

Declaring get_os_typespec() as a Member Function . . . . . . . . . . . . . . . . . . . . . . . 27

Establishing the Page Fault Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

Initializing ObjectStore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

Establishing a TIX Exception Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

Opening the Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Marking the Start of the TIX Exception Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Marking the End of the TIX Exception Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Beginning a Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Retrieving Typespecs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Release 6.3 3

Page 4: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Contents

4

Finding the Root. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Retrieving the Entry-Point Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Signaling a User-Defined Exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Allocating Persistent Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Establishing an Entry Point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

Referencing Persistent Objects in Another Database . . . . . . . . . . . . . . . . . . . . . . . 31

Closing the Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

Releasing Pointers to Transient Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

Chapter 3 Programming with Persistent Storage. . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

Establishing a Page Fault Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

Allocating and Releasing Persistent Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

::operator new() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

How ObjectStore Allocates Persistent Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

Using ::operator new() to Create Transient Objects. . . . . . . . . . . . . . . . . . . . . . . . 36

::operator delete() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

Using Typespecs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

Typespecs for Fundamental Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

Typespecs for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

Constructing a Typespec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

Parameterized Typespecs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

Clustering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

Pointers and Persistent Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

Persistent Pointers in Non-ObjectStore Processes . . . . . . . . . . . . . . . . . . . . . . . . . . 43

Cross-Database Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

Cross-Transaction Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

Illegal Pointer Checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

Applying Control Settings to Persistent Storage . . . . . . . . . . . . . . . . . . . . . . . . . 46

Grouping Clusters in Segments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

Using Segment Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

Managing Lifetimes of ObjectStore Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

Use Counts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

Using the Retain-Release API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

Using delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

Releasing Pointers with os_release_ . . . _pointer Classes. . . . . . . . . . . . . . . . . . 51

Methods Returning Pointers to ObjectStore Objects . . . . . . . . . . . . . . . . . . . . . . . . 52

Chapter 4 Basic Database Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

Creating a Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

Naming the Database: pathname Argument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

Controlling Protection: mode Argument. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

4 C++ A P I User Guide

Page 5: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Contents

Overwriting an Existing Database: if_exists_overwrite Argument . . . . . . . . . . 57

Specifying a Remote Schema Database: schema_database Argument . . . . . 57

Creating a Database: os_database::open() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

Destroying a Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

Destroying a Concurrently Opened Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

Opening a Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

Naming the Database: pathname Argument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

Controlling Access: read_only Argument. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

Creating a Database: create_mode Argument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

Specifying a Remote Schema Database: schema_database Argument . . . . . 61

Multiple Opens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

Opening a Database with create() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

Determining Database Open Status. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

Multiversion Concurrency Control (MVCC) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

Closing a Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

Close and Multiple Opens: force Argument. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

Using the os_close_database Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

Reopening a Closed Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

Finding a Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

Affiliation and Cross-Database Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

Affiliating with Other Databases: deep Argument . . . . . . . . . . . . . . . . . . . . . . . . . . 66

Storing Cross-Database Pathnames: relative Argument . . . . . . . . . . . . . . . . . . . . 66

Specifying a Common Parent Directory: common_parent Argument . . . . . . . 67

Finding All Affiliated Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

Changing Affiliation with osaffiliate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

Database Roots and Entry-Point Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

Step 1. Creating a Database Root . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

Step 2. Associating the Root with an Entry-Point Object . . . . . . . . . . . . . . . . . . . 70

Step 3. Retrieving the Entry-Point Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

Destroying the Database Root . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

Working with Rawfs Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

Rawfs Host Prefix. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

Creating ObjectStore Directories. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

Finding a Rawfs Pathname. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Aliases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Chapter 5 Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Overview of Transactions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Lexical and Dynamic Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

Transaction Commit and Abort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

Rolling Back a Transaction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

Release 6.3 5

Page 6: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Contents

6

Concurrency Control. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

Marking Off a Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

Using Lexical Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

Deadlocks and Automatic Retries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

Nesting Lexical Transactions: an Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

Using Dynamic Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

Beginning a Dynamic Transaction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

Committing a Dynamic Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

Dynamic vs. Lexical Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

Example of a Dynamic Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

Rolling Back to Persistent State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

Aborting the Current Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

Aborting the Top-Level Transaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

Aborting a Specified Transaction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

Explicit Aborts and Lexical Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

Local and Global Transactions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

Two-Phase Commit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

Chapter 6 Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

TIX Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

Predefined Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

User-Defined Exceptions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

Establishing a TIX Exception Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

Handling Multiple Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

Handling Exceptions Outside a Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

Using the TIX Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

Signaling Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

Displaying Additional Error Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

Chapter 7 Database Access Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

Restricting Access with Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

Categories of Users. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

Specifying Levels of Permission . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

Controlling Directory-Level Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

Controlling Database-Level Access. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

Controlling Segment-Level Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

Restricting Access with Schema Keys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

Setting the Database’s Schema Key . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

Setting the Application’s Schema Key. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

Using Schema Key Environment Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

6 C++ A P I User Guide

Page 7: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Contents

Protecting Multiple Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

Schema Keys and ObjectStore Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

Schema Key Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

Chapter 8 Using the Notification Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

Notification Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

Posting a Notification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

Specifying a Range of Locations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

Subscribing and Unsubscribing to Ranges. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

Notification Queuing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

Receiving Notifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

Notification Retrieval Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

Retrieving Thread-Based Notification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

Retrieving Polling-Based Notification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

Retrieving File-Descriptor-Based Notification. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

General Notification Behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

Subscribing and Unsubscribing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

Using Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

Performance Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

Notifications API. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

Network Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

Notification Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

ObjectStore Utilities for Managing Notification . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

Notifications Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

Chapter 9 Schemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

Application Schemas and Schema Source Files . . . . . . . . . . . . . . . . . . . . . . . . . 122

Component Schemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

Component Schemas and Application Schemas . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

Uses for Component Schemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

DLL Loading and Unloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

DLL Load and Unload Reporting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

DLL Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

Creating a DLL Identifier Prefix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

Component Schema Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

Compiler Dope Damage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

Schema Evolution and Component Schemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

Component Schema Examples. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

Database Schemas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

Batch Schema Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

Release 6.3 7

Page 8: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Contents

8

Incremental Schema Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

Batch Installation Compared to Incremental Installation . . . . . . . . . . . . . . . . . . 130

Schema Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

Scalars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

Enumeration Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

Schema Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

Planning Schema Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

Using ossevol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

Postevolution Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

Writing a Schema Evolution Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135

Using osscheq to Verify Schema Changes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

Using osverifydb to Verify Pointers and References . . . . . . . . . . . . . . . . . . . . . . . 137

Validation Checklist. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

Deploying Schema Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

Example of Schema Evolution. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

Using ossevol and a Postevolution Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

Example of a Schema Evolution Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

8 C++ A P I User Guide

Page 9: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Preface

Purpose The C++ A P I User Guide supports ObjectStore Release 6.3. It describes how to use basic features of the ObjectStore C++ Application Programming Interface (API) when you are creating database applications. A companion volume, the Advanced C++ A P I User Guide, provides descriptions of the more complex features of ObjectStore.

Audience This manual assumes that the reader is an experienced C++ programmer.

Assumption Information in this manual assumes that ObjectStore is installed and configured.

How This Book Is OrganizedUnlike the C++ A P I Reference, which provides complete reference information for the ObjectStore C++ API — including the class library, system-supplied globals, macros, and predefined exceptions — this manual focuses on using the API to write ObjectStore applications.

Chapter 1, ObjectStore Concepts, on page 13, discusses fundamental ObjectStore concepts. Although this discussion is not directly relevant to the practical task of writing an ObjectStore application, the concepts themselves — persistence, the organization of an ObjectStore database, and the memory mapping architecture of ObjectStore — underlie many of the ObjectStore features you will use in your program.

Chapter 2, Anatomy of an ObjectStore Application, on page 21, presents an example application that performs basic ObjectStore operations. Included with the listing of the main application and its header file is a line-by-line discussion of those parts of the program that perform ObjectStore operations. Included in the discussion are references to chapters and sections of this manual in which the topic under discussion is treated in more detail.

The remaining chapters discuss the following topics:

• Chapter 3, Programming with Persistent Storage, on page 33, describes how to prepare for working with persistent memory and how to allocate and release storage for persistent data.

• Chapter 4, Basic Database Operations, on page 55, describes common operations that most ObjectStore applications perform on a database, such as opening and closing the database and establishing the entry point into the database.

• Chapter 5, Transactions, on page 75, describes what a transaction is, how to use the ObjectStore API to write a transaction, and how locking and aborting affect transactions.

Release 6.3 9

Page 10: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Preface

• Chapter 6, Exception Handling, on page 89, describes the exceptions that ObjectStore signals when it detects an error condition. It also describes how to handle these exceptions.

• Chapter 7, Database Access Control, on page 101, describes how to set permissions for access to either file or rawfs databases. It also describes how to use schema keys to restrict access to a database’s data and metadata.

• Chapter 8, Using the Notification Service, on page 111, describes how to use the notification API to enable different clients accessing the same database to communicate with each other.

• Chapter 9, Schemas, on page 121, discusses application, component, and database schemas; schema validation; and evolving schemas to reflect changes in the representation of objects stored in a database.

Note For information about the collections facility, see the C++ Collections Guide and Reference.

Notation conventions

This document uses the following conventions

Example Programs The example programs listed in this manual are available online in the following directories:

• $OS_ROOTDIR/examples/doc_demos (UNIX)

• %OS_ROOTDIR%\examples\doc_demos (Windows)

The doc_demos directory consists of subdirectories corresponding to chapters in the documentation that contain example programs. For example, the subdirectory ug2

Convention Meaning

Courier Courier font indicates code, syntax, file names, API names, system output, and the like.

Bold Courier Bold Courier font is used to emphasize particular code.

Italic Courier Italic Courier font indicates the name of an argument or variable for which you must supply a value.

Sans serif Sans serif typeface indicates the names of user interface elements such as dialog boxes, buttons, and fields.

Italic serif In text, italic serif typeface indicates the first use of an important term.

[ ] Brackets enclose optional arguments.

{ a | b | c } Braces enclose two or more items. You can specify only one of the enclosed items. Vertical bars represent OR separators. For example, you can specify a or b or c.

... Three consecutive periods indicate that you can repeat the immediately previous item. In examples, they also indicate omissions.

10 C++ A P I User Guide

Page 11: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Preface

contains the programs that appear in Chapter 2 of this manual. Each subdirectory contains

• Source files for the example programs

• A README.TXT file

• A makefile for building the program

• A doall script (in Windows, doall.bat batch file) that automates building and running the program

The doc_demos directory also contains a README.TXT file that explains how the subdirectories are organized.

Note The example programs are not intended as models of the most effective way to write an ObjectStore application. Rather, their purpose is simply to illustrate features of the ObjectStore API that are described in the manual.

Progress Software Real Time Division on the World Wide WebThe Progress Software Real Time Division Web site (www.progress.com/realtime) provides a variety of useful information about products, news and events, special programs, support, and training opportunities.

Technical Support

To obtain information about purchasing technical support, contact your local sales office listed at www.progress.com/realtime/techsupport/contact, or in North America call 1-781-280-4833. When you purchase technical support, the following services are available to you:

• You can send questions to [email protected]. Remember to include your serial number in the subject of the electronic mail message.

• You can call the Technical Support organization to get help resolving problems. If you are in North America, call 1-781-280-4005. If you are outside North America, refer to the Technical Support Web site at www.progress.com/realtime/techsupport/contact.

• You can file a report or question with Technical Support by going to www.progress.com/realtime/techsupport/techsupport_direct.

• You can access the Technical Support Web site, which includes

- A template for submitting a support request. This helps you provide the necessary details, which speeds response time.

- Solution Knowledge Base that you can browse and query.

- Online documentation for all products.

- White papers and short articles about using Real Time Division products.

- Sample code and examples.

- The latest versions of products, service packs, and publicly available patches that you can download.

- Access to a support matrix that lists platform configurations supported by this release.

- Support policies.

Release 6.3 11

Page 12: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Preface

- Local phone numbers and hours when support personnel can be reached.

Education Services

To learn about standard course offerings and custom workshops, use the Real Time Division education services site (www.progress.com/realtime/services).

If you are in North America, you can call 1-800-477-6473 x4452 to register for classes. If you are outside North America, refer to the Technical Support Web site. For information on current course offerings or pricing, send e-mail to [email protected].

Searchable Documents

In addition to the online documentation that is included with your software distribution, the full set of product documentation is available on the Technical Support Web site at www.progress.com/realtime/techsupport/documentation. The site provides documentation for the most recent release and the previous supported release. Service Pack README files are also included to provide historical context for specific issues. Be sure to check this site for new information or documentation clarifications posted between releases.

Your CommentsReal Time Division product development welcomes your comments about its documentation. Send any product feedback to [email protected]. To expedite your documentation feedback, begin the subject with Doc:. For example:

Subject: Doc: Incorrect message on page 76 of reference manual

12 C++ A P I User Guide

Page 13: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 1ObjectStore Concepts

This chapter introduces the basic concepts you need to understand in order to use ObjectStore successfully. The following topics are discussed:

Persistent Storage: Organizational Overview 14

Transient Instances 15

ObjectStore Processes 16

ObjectStore’s Memory Mapping Architecture 17

Memory Mapping and Schema Information 18

Rawfs Databases 19

Threads Support 19

Programming Interface 20

Release 6.3 13

Page 14: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Persistent Storage: Organizational Overview

Persistent Storage: Organizational Overview Persistent data is data that survives beyond the lifetime of the process that created it. (Transient data, on the other hand, resides in program memory and lasts no longer than the run-time life of the program.) ObjectStore stores persistent data in a database that typically resides on disk drives.

File and rawfs databases

There are two kinds of ObjectStore databases:

• A file database is a regular operating system file.

• A rawfs database resides in an ObjectStore file system managed by an ObjectStore server. Rawfs databases are discussed further in Rawfs Databases on page 19.

Clusters The basic unit of allocation in an ObjectStore database is the cluster. When you use ::operator new() to create a persistent object to be stored in the database, the storage is allocated from a cluster. For information about allocating storage for persistent objects, see ::operator new() on page 34. For information about clusters, see Clustering on page 42.

Normal and huge allocations

ObjectStore allocates storage in units of 64 KB. If the allocation is less than or equal to 64 KB (a normal allocation), it is stored in a normal cluster, which can contain any number of normal allocations, up to a maximum size of 2 GB. If the allocation is greater than 64 KB (a huge allocation), ObjectStore creates a huge cluster containing only that one allocation, as described in How ObjectStore Allocates Persistent Storage on page 35.

Segments Clusters are organized logically into segments. When you create a database, ObjectStore normally creates two segments:

• The schema segment, which ObjectStore uses to hold schema information about the objects stored in the database and the database roots. The schema segment is not directly accessible by the user application.

• The default segment, which consists of clusters that ObjectStore uses when it is allocating persistent storage.

The user can create additional segments by using the os_database::create_segment() method, and specify any of these for allocation instead of the default segment.

The default segment and any additional segments created by the user each have at least one cluster, the default cluster. ObjectStore normally allocates storage from this cluster. However, you can use os_segment::create_cluster() to create additional clusters in a segment and can specify one of those clusters for allocation. Likewise, you can use os_database::create_segment() to create additional segments in the database. Typically, you would create additional segments to hold related clusters, as described in Grouping Clusters in Segments on page 47.

Typically, you are concerned with clusters when you want to control data locality (clustering), and you are concerned with segments when you want to set the logical characteristics (for example, access control) of a group of clusters. For information

14 C++ A P I User Guide

Page 15: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 1: ObjectStore Concepts

about the methods to use when you are creating clusters and segments, see the following in the C++ A P I Reference:

• os_database::create_segment()

• os_segment::create_cluster()

The following figure illustrates the hierarchical relationship among persistent storage, file systems, databases, segments, clusters, and pages.

Transient Instances When you work with a database, segment, or cluster, you must create an instance of one or more of the classes in the ObjectStore C++ API; for example, os_database, os_segment, and os_cluster. Keep in mind that instances of these classes are actually transient, unlike the persistent entity — a database, segment, or cluster — that each instance represents. That is, the instance exists only for the duration of the current process. If you copy it into persistent storage, it is meaningless when it is retrieved by another process.

Cluster

File System

Page

Segment

Database

Persistent Stoage

File System

Release 6.3 15

Page 16: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

ObjectStore Processes

It is the user’s responsibility to manage the lifetimes of transient objects and to delete them when they are no longer needed; see Managing Lifetimes of ObjectStore Objects on page 48.

For information about the classes to use when working with databases, segments, and clusters, see the following in the C++ A P I Reference:

• os_cluster

• os_database

• os_segment

ObjectStore Processes ObjectStore applications require two auxiliary processes for application execution: an ObjectStore server and a cache manager. A server handles access to ObjectStore databases, including storage and retrieval of persistent data. When ObjectStore is installed, the system administrator typically arranges for each server to start when its host machine boots. A single application can use several databases, including databases on different file systems, handled by different servers. Most users never have to concern themselves with starting and stopping servers.

Cache manager A cache manager is started automatically when an ObjectStore application starts. The cache manager is a UNIX daemon (a Windows service) that runs on the same machine as the client application. Its function is to respond to server requests as a stand-in for the client application and manage the application’s client cache. The client cache is the local holding area for data mapped or waiting to be mapped into virtual memory.

If additional ObjectStore applications are started on the same machine, they all use the same cache manager. Although different ObjectStore applications can run on the same machine at the same time, only a single cache manager is ever running on a given machine. As with servers, most users never have to concern themselves with starting or stopping cache managers.

ObjectStore / Single

ObjectStore / Single applications combine the server and cache manager functions into one process. The diagrams that follow point out the distinctions between the ObjectStore and ObjectStore / Single implementations. It is important to remember that although the implementation differs, the functions performed by the server and cache manager are preserved in ObjectStore / Single.

For more information about ObjectStore / Single, see Chapter 6, Working with ObjectStore / Single, of Building ObjectStore C++ Applications.

16 C++ A P I User Guide

Page 17: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 1: ObjectStore Concepts

ObjectStore processes

Application

disk

node

node

nodenode

node

node

network

Cache ManagerCache ManagerCache Manager

Cache Manager

Application

Application

Application Application

node

disk Server

ObjectStore Server

Each site has one or more ObjectStore servers to handle persistent data. Each node running one or more ObjectStore applications has a cache manager to handle application data.

ObjectStore/ Single process

The nonnetworked ObjectStore / Single application provides the same functions as the networked ObjectStore, but the two processes — server and cache manager — have been combined into one process.

ObjectStore’s Memory Mapping Architecture ObjectStore transfers data between database (persistent) memory and program (transient) memory automatically, transparent to the user. ObjectStore achieves a

ObjectStore/Single

Stand-Alone, Single-ProcessServer/Cache ManagerSystem

disk

Application

Single Process

Release 6.3 17

Page 18: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Memory Mapping and Schema Information

combination of transparency and efficiency with a unique memory mapping architecture. All data is stored in an ObjectStore database in its native C++ format.

Page faulting ObjectStore uses page faulting to detect a reference in a running program to persistent data. When a page fault occurs, ObjectStore transfers the page containing the referenced data automatically — possibly together with adjacent pages — across the network to the application’s cache. The page containing the referenced data is then mapped into virtual memory.

Sometimes the referenced data is already in the client cache (because data in the same pages was already used and the required page was not swapped out of the cache), and all that is required is the virtual memory mapping. Other times, the data is already mapped into virtual memory (because data on the same page was already used in the current transaction), and no additional operations are required to access the data. After persistent data has been mapped into virtual memory, access to it is as fast as access to transient data.

Advantages of this architecture

Two advantages of this architecture are

• Persistence is specified on a per-instance basis, independent of type. The same type can have both persistent and nonpersistent instances, and the same function can operate on both persistent and nonpersistent data. Moreover, instances of any built-in C++ type (such as int) can be designated as persistent. This means that your existing routines that you have developed for use with transient data can also be used with persistent data.

• Pointers are processed at memory speeds. After an object has been transferred and mapped into virtual memory, all pointers to it are regular virtual memory pointers and are processed at regular hardware speeds, without any checking for database references.

For more information about ObjectStore’s memory mapping architecture, see Controlling Address Space Reservation in Chapter 1 of the Advanced C++ A P I User Guide.

Memory Mapping and Schema InformationFor ObjectStore to realize the advantages inherent in its memory mapping architecture, it needs to store information in each database about the classes of objects stored there and about the layout of instances of these classes. This schema information allows ObjectStore to identify the locations of pointer fields in each newly retrieved cluster, so it can perform relocation.

ObjectStore stores schema information as C++ objects. Classes themselves are not run-time objects in C++ (they cannot, for example, be values of variables or other expressions). Therefore, ObjectStore must generate representations of classes to manage database memory.

These representations are generated before link time for each application that can store information in a database. This means that at run time, when the application stores an object in a database, a representation of the object’s class is ready to be

18 C++ A P I User Guide

Page 19: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 1: ObjectStore Concepts

added to the database’s schema along with the object itself. If instances of a class are already in the database, the application’s class representation is checked against that of the database to ensure that they agree.

An application’s schema information is generated by ossg, the ObjectStore schema generator. It is stored in two places: a source file and an ObjectStore database. You use ossg when you are building an ObjectStore application.

For more information about schemas, see Chapter 9, Schemas, on page 121. For reference information about ossg, see ossg in Managing ObjectStore. For information about using ossg to generate schema, see Chapter 3, Generating Schemas, of Building ObjectStore C++ Applications.

Rawfs DatabasesYou can store some or all of your databases in ObjectStore file systems managed by ObjectStore servers instead of storing databases as regular files managed by the operating system. Each ObjectStore file system is known as a rawfs (raw file system). For information about setting up and managing a rawfs, see Managing the Rawfs in Chapter 1 of Managing ObjectStore.

Each rawfs provides a separate name space and directory hierarchy. Rawfs directories form hierarchical structures just as operating system directories do. Rawfs directory hierarchies, however, are independent of the operating system directory hierarchies.

Each ObjectStore server can manage a hierarchy of rawfs directories and maintain permission modes, creation dates, owners, and groups for each entry. There can be several independent rawfs directory hierarchies at a given site, each managed by a different server, and the same application can use databases in different hierarchies.

ObjectStore / Single

Currently, ObjectStore / Single supports only file databases, not rawfs databases.

Threads Support If your application uses multiple threads, you can take advantage of the thread locking provided by ObjectStore. Thread locking ensures that ObjectStore does all interlocking between threads to prevent threads from interfering with one another when executing in an ObjectStore library. You are responsible for coding any thread synchronization required by your application while threads are not executing within an ObjectStore library.

Thread locking is enabled by default. If your application does not use threads and you want to disable thread locking, call the following method with a 0 (false) argument:

static void objectstore::set_thread_locking(os_boolean);

Release 6.3 19

Page 20: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Programming Interface

To enable support for threads, call set_threads_locking() with a nonzero (true) argument.

To determine if ObjectStore thread locking is enabled, call

static os_boolean objectstore::get_thread_locking();

This method returns nonzero (true) if thread locking is enabled and 0 (false) if it is disabled.

For more information about multithreaded applications, see Chapter 3, Multithread and Multisession Applications, of the Advanced C++ A P I User Guide.

Programming Interface The ObjectStore C++ application programming interface (API) is designed for the development of C++ applications that require database services. Although some of your interaction with ObjectStore takes place from the shell (you issue commands, for example, to create rawfs directories or generate schemas), most interaction takes place from within programs.

Both ObjectStore and ObjectStore / Single applications are linked with the ObjectStore library. This is a library that provides the C++ interface to the classes whose member functions, data members, and enumerators enable your application to access ObjectStore functionality. The library also includes global functions such as overloadings of ::operator new() that allow dynamic allocation of persistent memory for any type of object.

The class templates feature of C++ is included in the ANSI Standard for C++. If your compiler supports templates, you can use the parameterized versions of some classes in the class library. Using parameterized classes enhances the type safety of your applications.

Note Some functions in the ObjectStore API return an array or a character string. Unless the return value of such a function is declared const, the array or string is allocated from the heap and should be deleted to prevent a memory leak. Refer to the C++ A P I Reference for a full description of the API.

20 C++ A P I User Guide

Page 21: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 2Anatomy of an ObjectStore Application

This chapter illustrates common features of ObjectStore in an example of an ObjectStore application. The first section, An Example Application on page 21, lists the source code for the application and its header file. The second section, Line-by-Line Analysis on page 25, discusses each line in the source listings that is relevant to the ObjectStore API. The line-by-line discussions include references to other chapters and sections of this manual in which a particular feature or operation of ObjectStore is discussed more fully.

An Example Application This section lists the source code for an example application, including its header file (bookclub.hh on page 22) and the main application (main.cc on page 23). Both files are discussed in the following section, Line-by-Line Analysis on page 25.

The program manages two databases for a book club in which a reader selects a book. One database contains reader objects and the other contains book objects. Objects in both databases are organized as singly linked lists.

Compilation produces the application bookclub, which consists of the header file (bookclub.hh), a definitions file (bookclub.cc) that defines the member functions of the classes declared in bookclub.hh, and the main program file (main.cc). The main application, bookclub, assumes that both databases have been created and that they each contain at least one object.

Note The definitions file, bookclub.cc, is not listed because it is not relevant to understanding the ObjectStore API. However, all source code files (including bookclub.cc) needed for building and running the bookclub program are available online in the following directories:

• $OS_ROOTDIR/examples/doc_demos/ug2 (UNIX)

• %OS_ROOTDIR%\doc_demos\ug2 (Windows)

Running the bookclub program

To run the application, the user enters the command

bookclub reader-database book-database

reader-database and book-database are the names of the two databases.

Release 6.3 21

Page 22: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

An Example Application

When the program runs, it prompts the user for the name of a reader as well as a book title and author’s name. If the program does not find the reader’s name in reader-database, it adds a new reader to the database; it does the same for the book. In any case, the program selects the book for the reader.

Following is the transcript from a sample run:

$ bookclub readers.db books.db Enter reader’s name: Tracy Enter author’s name: Twain Enter book title: Huckleberry Finn

The program gives no indication whether it ran successfully, and performs minimal error handling.

The listings include line numbers for ease of referencing. The section following the listings, Line-by-Line Analysis on page 25, discusses the program in detail.

Note The program was not designed with efficiency in mind; unsorted singly linked lists are not the most effective way to organize objects in a database. In fact, the program design was kept simple deliberately, to focus on the basic features of an ObjectStore application.

In the interest of keeping the program simple, the version listed here differs from the online version. The online version is more robust, and avoids the inefficient use of I/O within a transaction and allows for the possibility of transaction retry. Both versions, however, are functionally similar.

bookclub.hh 01 // bookclub.hh: defines the classes used by bookclub program02 #include <iostream>03 #include <string.h>04 #include <ostore/ostore.hh>

05 #define BUFSIZE 10006 #define RDR_ROOT_NAME "readers"07 #define BK_ROOT_NAME "books"

08 // make the user-defined exception err_no_entry_point09 // (defined in bookclub.cc) accessible to other files10 DECLARE_EXCEPTION(err_no_entry_point);

11 // forward declarations of classes12 class Ele;13 class Reader;14 class Book;

15 // global functions defined in bookclub.cc16 ostream &operator<<(ostream& os, Reader& r);17 ostream &operator<<(ostream& os, Book& b);18 char* bc_strdup(char* s1, os_cluster_with with_this);

19 // base class for managing linked lists20 class Ele {21 private: // data member22 Ele* next; // singly linked

22 C++ A P I User Guide

Page 23: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 2: Anatomy of an ObjectStore Application

23 public: // methods24 Ele(Ele** last) { next = *last; *last = this; }25 virtual ~Ele() { next = 0; }26 Ele* get_next() { return next; }27 void set_next(Ele* e) { next = e; }28 Ele* find_item(char* s1, char* s2 = 0);29 virtual char* get_name() = 0;30 virtual int members_match(char* s1, char* s2 = 0) = 0;

31 private:32 // declared but unimplemented functions to prevent33 // inadvertent calls34 Ele();35 Ele(const Ele&);36 Ele& operator=(const Ele&);37 };

38 // information about reader of Book39 class Reader : public Ele {40 private: // data41 char* name; // name of reader42 Book* selection; // book selected

43 public: // methods44 Reader(char* s, Reader** last);45 ~Reader() { delete [ ] name; }

46 // ObjectStore implements this function using the type47 // information in the schema file48 static os_typespec* get_os_typespec();

49 char* get_name() { return name; } 50 Book* get_selection() { return selection; } 51 void set_selection(Book* b) { selection = b; } 52 int members_match(char* n, char* dummy); 53 };

54 // information about book selected for Reader55 class Book : public Ele {56 private: // data57 char* author; // author and ...58 char* title; // title of book

59 public:60 Book(char* a, char* t, Book** last);61 ~Book() { delete [] author; delete [] title; }62 static os_typespec* get_os_typespec();63 char* get_name() { return author; }64 char* get_title() { return title; }65 int members_match(char* n, char* t);66 };

main.cc 01 // main.cc: main application for the bookclub program; reads02 // information about reader and selected book, and adds it to03 // to the reader and book database files named on command line04 #include "bookclub.hh"

Release 6.3 23

Page 24: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

An Example Application

05 int main(int argc, char* argv[])06 {07 Reader* rdr = 0;08 Book* bk = 0;09 char name[BUFSIZE];10 char title[BUFSIZE];

11 OS_ESTABLISH_FAULT_HANDLER { 12 if (argc != 3) { // wrong number of arguments 13 cerr << "USAGE: bookclub <reader-db> <book-db>\n"; 14 return 1; 15 }

16 objectstore::initialize(); 17 os_database *db_rdr, *db_bk;18 // TIX exception handler for problems opening database19 // files.20 TIX_HANDLE (err_database_not_found) {21 // open reader and book databases22 db_rdr = os_database::open(argv[1]);23 db_bk = os_database::open(argv[2]);24 } TIX_EXCEPTION { 25 cerr << "Cannot open " << argv[1] << " and/or ";26 cerr << argv[2] << ". Check that files exist.\n";27 return 1;28 } TIX_END_HANDLE

29 // set up lexical transaction 30 OS_BEGIN_TXN(txn, 0, os_transaction::update) { 31 // get type information for Reader and Book objects32 os_typespec* rdr_type = Reader::get_os_typespec();33 os_typespec* bk_type = Book::get_os_typespec();

34 // get root and entry point for reader database 35 os_database_root* root = db_rdr->find_root(36 RDR_ROOT_NAME);37 Reader* ep_rdr; // pointer to entry point 38 if (!(ep_rdr = (Reader*)root->get_value(rdr_type)))39 err_no_entry_point.signal(40 "%s - use os_database_root::set_value()", argv[1]);

41 // get reader's name42 cout << "Enter reader\'s name: " << flush;43 cin.getline(name, BUFSIZE);44 if (!(rdr = (Reader*)ep_rdr->find_item(name))) {45 // reader not in database, add a new one 46 rdr = new(os_cluster::with(ep_rdr), rdr_type)47 Reader(name, &ep_rdr); 48 // make the new reader the entry point49 root->set_value(rdr, rdr_type);50 }

51 // get root and entry point for books database52 root = db_bk->find_root(BK_ROOT_NAME);53 Book* ep_bk; // pointer to entry point 54 if (!(ep_bk = (Book*)root->get_value(bk_type)))55 err_no_entry_point.signal(56 "%s - use os_database_root::set_value()", argv[2]);

57 // get book info -- author's name and book title

24 C++ A P I User Guide

Page 25: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 2: Anatomy of an ObjectStore Application

58 cout << "Enter author\'s name: " << flush;59 cin.getline(name, BUFSIZE);60 cout << "Enter book title: " << flush;61 cin.getline(title, BUFSIZE);62 // if book is not in database, add it.63 if (!(bk = (Book*)ep_bk->find_item(name, title))) {64 // create a book65 bk = new(os_cluster::with(ep_bk), bk_type)66 Book(name, title, &ep_bk);67 // make the new book the entry point68 root->set_value(bk, bk_type);69 }70 // select book for reader 71 rdr->set_selection(bk); 72 } OS_END_TXN(txn)73 db_rdr->close(); // close both database files74 db_bk->close(); 76 // release pointers to os_database objects 77 db_rdr->release_pointer(); 78 db_bk->release_pointer(); 79 } OS_END_FAULT_HANDLER

80 return 0;81 }

Line-by-Line Analysis This section analyzes each line of bookclub.hh on page 22 and main.cc on page 23 that uses an ObjectStore feature or performs an ObjectStore operation. When the discussion touches on a larger topic, references are included to the chapter or section in this manual in which the topic is treated in more detail.

Including ObjectStore Header Files bookclub.hh, line 4

#include <ostore/ostore.hh>

All programs that perform any ObjectStore operation must include the ObjectStore header file ostore/ostore.hh. In most cases, this is the only ObjectStore header file you must include. However, using certain features of ObjectStore might require that you include other header files. For example, applications that make use of ObjectStore’s collections facility also must include ostore/coll.hh.

Following are the more commonly used ObjectStore header files:

• ostore.hh: required for all ObjectStore applications

• dbutil.hh: required for applications using ObjectStore database utilities

• coll.hh: required for applications that use the collections facility

• manschem.hh: required in schema source files

• mop.hh: required for applications that use the metaobject protocol feature

• schemevol.hh: required for applications that perform schema evolution

• compact.hh: required for applications that use the compactor

Release 6.3 25

Page 26: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Line-by-Line Analysis

The ObjectStore header files are order dependent; for example, the #include directive for ostore.hh must precede those for any other ObjectStore header files. Refer to the descriptions of the ObjectStore classes in Chapter 2, Class Library, of the C++ A P I Reference for the names and order of header files that other classes might require.

Defining Names for Database Roots bookclub.hh, lines 6–7

#define RDR_ROOT_NAME "readers"#define BK_ROOT_NAME "books"

The macros RDR_ROOT_NAME and BK_ROOT_NAME define the strings used to name the database roots that are associated with entry points into the databases. For information about roots and entry points, see Database Roots and Entry-Point Objects on page 69.

Declaring a User-Defined Exception bookclub.hh, line 10

DECLARE_EXCEPTION(err_no_entry_point);

DECLARE_EXCEPTION() is one of several macros that are part of the TIX exception-handling facility. This macro declares a user-defined TIX exception that is defined in another file, making the exception accessible to files other than the one in which it is defined.

The macro for defining the exception is DEFINE_EXCEPTION(). The definitions file (bookclub.cc) uses this macro to define the exception err_no_entry_point, as follows:

DEFINE_EXCEPTION(err_no_entry_point, "No entry-point object for", 0);

User-defined exceptions such as this one are instances of the class objectstore_exception, one of whose members is signal() (see main.cc, lines 39–40).

For information about defining your own exceptions, see User-Defined Exceptions on page 91.

Clustering Related Objects in Persistent Memory bookclub.hh, line 18

char* bc_strdup(char* s1, os_cluster_with with_this);

bc_strdup() is a user-defined global function that is defined in bookclub.cc, as follows:

char *bc_strdup(char* s1, os_cluster_with with_this) { int len = strlen(s1)+1; char* s2 = new(with_this, os_typespec::get_char(), len) char [len]; return strcpy(s2, s1);}

bc_strdup() allocates persistent storage for a character array, copies the contents of s1 to the allocated storage, and returns a pointer to the allocation. The function uses the os_cluster_with overloading of ::operator new() to allocate storage as close

26 C++ A P I User Guide

Page 27: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 2: Anatomy of an ObjectStore Application

as possible to the allocation for the object that contains the string. The call to bc_strdup() uses the os_cluster::with() function to pass the os_cluster_with object, as follows:

bc_strdup(s, os_cluster::with(this));

this points to the object that contains the string.

bc_strdup() optimizes the placement of the storage it allocates by clustering it with the allocation for another entity that is related to the target entity. For another example of clustering, see main.cc, line 46. For information about using this optimization when allocating persistent storage, see Clustering on page 42.

Using Cross-Database Pointers bookclub.hh, line 42

Book* selection; // book selected

This declaration indicates that the program uses cross-database pointers; that is, Reader objects stored in one database contain pointers to Book objects stored in the other database. By default, cross-database pointers are invalid. However, when the databases were created, the Reader database was affiliated with the Book database to enable cross-database pointers. Following is the statement that performed the affiliation:

db_rdr->affiliate(db_bk);

db_rdr points to the readers database and db_bk points to the books database. After this call, pointers in db_rdr can point to objects in db_bk and remain valid outside the process that created them; see, for example, main.cc, line 71.

For information about affiliating databases, see Affiliation and Cross-Database Pointers on page 65.

Deleting Persistent Storage bookclub.hh, line 45

~Reader() { delete [ ] name; }

This statement uses ObjectStore’s override of the delete operator in the implementation of the destructor ~Reader(). The override enables delete to release either persistent or transient storage.

For information about using persistent new and delete, see Allocating and Releasing Persistent Storage on page 34.

Declaring get_os_typespec() as a Member Function bookclub.hh, line 48

static os_typespec* get_os_typespec();

ObjectStore requires type information (called typespecs) for persistently stored objects. You can simplify the retrieval of typespecs for your own classes by declaring get_os_typespec() as a member function in each of your persistent classes, as declared here in the Reader class. (get_os_typespec() is also declared as a member function of the Book class in line 62.) ObjectStore implements the function when you run the ossg utility during the build process to generate schema.

Release 6.3 27

Page 28: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Line-by-Line Analysis

For information about generating and retrieving typespecs, see Using Typespecs on page 37.

Establishing the Page Fault Handler main.cc, line 11 OS_ESTABLISH_FAULT_HANDLER {

OS_ESTABLISH_FAULT_HANDLER is a system-supplied macro that, together with the OS_END_FAULT_HANDLER macro (see line 79), must enclose all code that operates on persistent memory. These macros are used by ObjectStore to handle page faults and to detect references to persistent memory. For more information, see Establishing a Page Fault Handler on page 33.

Initializing ObjectStore main.cc, line 16 objectstore::initialize();

Applications must call this function before performing any ObjectStore operations or before calling any ObjectStore functions except for the following:

• objectstore::propagate_log() (ObjectStore / Single only)

• objectstore::set_application_schema_pathname()

• objectstore::set_cache_file() (ObjectStore / Single only)

• objectstore::set_cache_size() (ObjectStore / Single only)

• objectstore::set_client_name()

If your program calls any of these functions, it must do so before calling objectstore::initialize(). A process can execute initialize() more than once, although the second call has no effect.

Note If your application uses multiple sessions, you must initialize ObjectStore by calling objectstore::initialize_for_sessions(). For more information about initializing ObjectStore, see descriptions of the following in the C++ A P I Reference:

• objectstore::initialize()

• objectstore::initialize_for_sessions()

Establishing a TIX Exception Handler main.cc, line 20 TIX_HANDLE (err_database_not_found) {

TIX_HANDLE() is a system-supplied macro that is supplied with ObjectStore’s exception-handling facility, called TIX. This macro marks the start of code protected by an exception handler. If an err_database_not_found exception occurs in the block of code between this macro and the TIX_EXCEPTION macro (line 24), it is handled in the block between the TIX_EXCEPTION and TIX_END_HANDLE (line 28) macros. Any other exception is not handled.

For information about writing TIX exception handlers, see Establishing a TIX Exception Handler on page 92.

28 C++ A P I User Guide

Page 29: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 2: Anatomy of an ObjectStore Application

Opening the Database main.cc, lines 22–23

db_rdr = os_database::open(argv[1]); db_bk = os_database::open(argv[2]);

The os_database::open() method opens the database, giving your application access to persistently stored objects. The calls to os_database::open() return pointers to transiently allocated objects of the class os_database. It is the user’s responsibility to manage the lifetimes of these objects; see main.cc, lines 77–78, which release the returned pointers — effectively deleting the os_database objects.

For more information about

• os_database::open(), see Opening a Database on page 59

• Memory management issues, see Managing Lifetimes of ObjectStore Objects on page 48

Marking the Start of the TIX Exception Handler main.cc, line 24 } TIX_EXCEPTION {

This TIX macro marks the end of the code block in which the exception err_database_not_found is handled and the start of the handler. The other macros used to delimit the handler are on line 20 and line 28.

Marking the End of the TIX Exception Handler main.cc, line 28 } TIX_END_HANDLE

This macro marks the end of the TIX exception handler. See line 20 and line 24 for the other macros used to delimit the handler.

Beginning a Transaction main.cc, line 30 OS_BEGIN_TXN(txn, 0, os_transaction::update) {

OS_BEGIN_TXN() is a macro that marks the beginning of a lexical transaction. The OS_END_TXN() macro at line 72 marks the end, where the transaction is committed. os_transaction::update is an enumerator that specifies that the transaction allows reads and writes to persistent memory.

For information about lexical transactions, see Using Lexical Transactions on page 77.

Retrieving Typespecs main.cc, lines 32–33

os_typespec* rdr_type = Reader::get_os_typespec(); os_typespec* bk_type = Book::get_os_typespec();

These statements retrieve typespecs for the classes Reader and Book. get_os_typespec() is declared in both classes (see bookclub.hh, line 48 and line 62). ObjectStore implements the function when you run the ossg utility.

For information about typespecs and the get_os_typespec() function, see Using Typespecs on page 37.

Release 6.3 29

Page 30: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Line-by-Line Analysis

Finding the Root main.cc, line 35 os_database_root* root = db_rdr->find_root(RDR_ROOT_NAME);

find_root() returns the database root, which is used in line 38 to retrieve the entry-point object. The root was created when the database was created by a call to os_database::create_root(), as follows:

os_database_root* r_root = db_rdr->create_root( RDR_ROOT_NAME);

For information about database roots, see Database Roots and Entry-Point Objects on page 69.

Retrieving the Entry-Point Object main.cc, line 38 if (!(ep_rdr = (Reader*)root->get_value(rdr_type)))

This line retrieves the entry-point object associated with the root and verifies that an object was returned. get_value() returns 0 if it does not find the entry-point object. The cast is required because get_value() returns a void* pointer. For information about typespecs, see Using Typespecs on page 37.

rdr_type is a typespec that ObjectStore checks against the typespec supplied to set_value() for mismatch errors; see main.cc, line 49, for the call to set_value(). For information about entry-point objects, see Database Roots and Entry-Point Objects on page 69.

Signaling a User-Defined Exception main.cc, lines 39–40

err_no_entry_point.signal( "%s - use os_database_root::set_value()", argv[1]);

This call to signal() triggers the user-defined exception err_no_entry_point, which is declared in bookclub.hh, line 10. All exceptions — predefined as well as user-defined — are descended from the class objectstore_exception, one of whose member functions is signal().

For more information about

• User-defined TIX exceptions, see User-Defined Exceptions on page 91

• objectstore_exception and other TIX classes, see Using the TIX Classes on page 97

Allocating Persistent Storage main.cc, lines 46–47

rdr = new(os_cluster::with(ep_rdr), rdr_type) Reader(name, &ep_rdr);

This statement uses an overloading of ::operator new() to allocate persistent storage for the object rdr. The nonpersistent version of this statement is

rdr = new Reader(name, &ep_rdr);

The placement argument os_cluster::with(ep_rdr) causes new to cluster the allocation for rdr as close as possible to the allocation for ep_rdr, the entry-point object. rdr_type is the typespec returned by get_os_typespec() in line 32.

30 C++ A P I User Guide

Page 31: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 2: Anatomy of an ObjectStore Application

For more information about

• Using ::operator new() to cluster related objects, see Clustering on page 42

• Retrieving typespecs for classes, see Typespecs for Classes on page 39

• Allocating for optimal performance, see Clustering on page 42

Establishing an Entry Point main.cc, line 49 root->set_value(rdr, rdr_type);

This call to set_value() establishes a new entry-point object by associating the root (to which root points) with the object rdr. In this program, Reader objects are front linked in a linked list. This means that each new object becomes the new head of the list — that is, the entry point.

rdr_type is a typespec, which was retrieved in main.cc, line 32. The same typespec is supplied to the call to get_value() in main.cc, line 38. ObjectStore verifies that the typespec supplied as an argument to get_value() matches the one supplied to set_value(). The typespec argument is optional in both cases; if you do not supply it, ObjectStore does not check it.

For more information about

• Establishing entry-point objects, see Step 2. Associating the Root with an Entry-Point Object on page 70

• Retrieving typespecs for classes, see Using Typespecs on page 37

Referencing Persistent Objects in Another Database main.cc, line 71 rdr->set_selection(bk); // select book

This call to the user-defined method Reader::set_selection() writes a cross-database pointer in the readers database to the books database. By default, such pointers are invalid outside a transaction. In this case, however, the databases were previously affiliated by the following statement, which occurs in the application that creates the databases:

db_rdr->affiliate(db_bk);

Affiliation enables pointers in the db_rdr database to reference objects in the db_bk database. See bookclub.hh, line 42, for the declaration of selection, and line 51 for the declaration and definition of set_selection().

For information about affiliating databases, see Affiliation and Cross-Database Pointers on page 65.

Closing the Database main.cc, lines 73–74

db_rdr->close(); // close both database files db_bk->close();

These two lines close both databases. For more information, see Closing a Database on page 63.

Release 6.3 31

Page 32: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Line-by-Line Analysis

Releasing Pointers to Transient Storage main.cc, lines 77–78

db_rdr->release_pointer(); db_bk->release_pointer();

These calls to os_database::release_pointer() release the pointers returned by the calls to os_database::open() in lines 22–23. Because db_rdr and db_bk are the only os_database pointers in use, the effect of releasing them is to delete the os_database objects to which they point. For information about using release_pointer() to manage the lifetimes of certain transient objects, see Using the Retain-Release API on page 50.

32 C++ A P I User Guide

Page 33: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3Programming with Persistent Storage

Writing a program that uses persistently stored data (that is, data stored in an ObjectStore database) is similar in many respects to writing one that uses transiently stored data (that is, data that exists only while your program is running). When persistent data arrives in your program, it is already in the same format as the transient data. This means (for example) that you can use the same pointers and perform the same pointer operations with persistent data as you can with transient data. The decision to allocate persistent or transient storage is independent of the type of value you want to store; any type of value can be stored in either persistent or transient storage.

However, certain things must be done differently when you are working with persistent data. For example, ObjectStore overloads ::operator new() for use when you are allocating persistent storage. This chapter describes how to create and use persistently stored data. It covers the following topics:

Establishing a Page Fault Handler 33

Allocating and Releasing Persistent Storage 34

Using Typespecs 37

Clustering 42

Pointers and Persistent Memory 43

Applying Control Settings to Persistent Storage 46

Grouping Clusters in Segments 47

Managing Lifetimes of ObjectStore Objects 48

For more specialized topics (for example, persistent unions), see Chapter 1, Advanced Persistence, of the Advanced C++ A P I User Guide.

Establishing a Page Fault Handler As explained in ObjectStore’s Memory Mapping Architecture on page 17, ObjectStore uses page faulting to detect program references to persistent memory. To do this, ObjectStore must establish a fault handler to detect page faults at the top of every program stack in your application.

Release 6.3 33

Page 34: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Allocating and Releasing Persistent Storage

To establish the fault handler, add the following macros to your application:

• OS_ESTABLISH_FAULT_HANDLER, which marks the start of the handler

• OS_END_FAULT_HANDLER, which marks the end of the handler

These two macros must enclose all code that performs any ObjectStore operation. One exception to this rule is objectstore::initialize(), which can be called before the OS_ESTABLISH_FAULT_HANDLER macro. However, because there is no performance benefit to not using the macros, the simplest approach is to enclose all ObjectStore code within the macros. Your main() or WinMain() function would look like the following example:

int main(int argc, char* argv[]) { OS_ESTABLISH_FAULT_HANDLER { // your code } OS_END_FAULT_HANDLER }

The use of braces to enclose the code is not required, but some text editors might object if you do not use them.

If your application uses threads, you also must insert the macros at the beginning and end of any thread that performs ObjectStore operations.

For detailed reference information about the fault-handler macros, see the following in the C++ A P I Reference:

• OS_ESTABLISH_FAULT_HANDLER

• OS_END_FAULT_HANDLER

Allocating and Releasing Persistent Storage ObjectStore overrides the new and delete operators to allow their use for allocating and releasing persistent or transient storage. The following sections describe how to use these operators.

For detailed descriptions of ObjectStore’s overrides of both operators, see ::operator new() and ::operator delete() in the C++ A P I Reference.

::operator new() ObjectStore overrides ::operator new() for use when allocating persistent storage. In addition, ObjectStore provides overloadings of ::operator new() to specify where to allocate the storage and the type of object to be allocated. These overloadings use the placement syntax of the C++ ::operator new().

Syntax The following is the generalized syntax for the overloadings of ::operator new():

void* ::operator new ( object_size, where, typespec [ , how_many ] )

Arguments object_size is the size_t argument that specifies the size of the entity to create. It is optional and is supplied by the compiler by default.

34 C++ A P I User Guide

Page 35: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

where can be a pointer to an os_database, os_segment, or os_cluster object. This argument specifies the database, segment, or cluster (respectively), where you want ObjectStore to allocate. For more information, see How ObjectStore Allocates Persistent Storage on page 35. where can also be an os_cluster_with object; see Clustering on page 42.

To allocate storage in the same database, segment, or cluster as a previously allocated object, you can use os_database::of(obj ), os_segment::of(obj ), or os_cluster::of(obj ) as the where argument. All three functions take a pointer to the stored object (obj) as the argument and return a pointer to the database, segment, or cluster in which obj was stored. os_cluster::with() can also be used with new to supply a pointer to a cluster, but it has somewhat different semantics; see Clustering on page 42.

typespec is a pointer to an os_typespec object that ObjectStore uses to determine the type and layout of the object. For more information about this argument, see Using Typespecs on page 37.

how_many is used only when you are allocating an array. It is a signed 32-bit integer of type os_int32 that specifies the number of elements in the array.

Examples In its simplest form, ::operator new() requires only two arguments, where and typespec, as in the following example. The example assumes that the arguments this_cluster and obj_ts are defined elsewhere in the program.

obj_type *obj = new(this_cluster, obj_ts) obj_type;

obj will be stored in the cluster to which this_cluster points. obj_ts is the typespec for obj.

In the next example, ::operator new() creates an array of ten elements of obj_type objects. Note that this overloading uses the same typespec argument as the previous example used; there is no os_typespec specifically for arrays. The how_many argument (10) identifies the number of elements in the array.

obj_type *some_objs = new(this_cluster, obj_ts, 10) obj_type[10];

Note Before using ::operator new() to allocate persistent storage, you must have created a database, opened it, and begun a transaction. For information on creating and opening databases, see Chapter 4, Basic Database Operations, on page 55. For information about transactions, see Chapter 5, Transactions, on page 75.

How ObjectStore Allocates Persistent Storage As explained in Persistent Storage: Organizational Overview on page 14, the cluster is the unit of allocation in a database. This means that, regardless of whether you specify a database, segment, or cluster for the where argument to ::operator new(), ObjectStore always allocates from a cluster. If you specify a pointer to a cluster as the where argument, ObjectStore allocates from that cluster. If you specify a pointer to a segment or database as the where argument, ObjectStore usually allocates from the default cluster.

Recall that a database has at least one segment (the default segment) and that each segment has at least one cluster (the default cluster). Depending on whether you

Release 6.3 35

Page 36: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Allocating and Releasing Persistent Storage

specify a pointer to a database, segment, or cluster as the first argument to ::operator new(), ObjectStore allocates as follows:

If you specify a pointer to a database, storage is allocated from the default cluster of the default segment. If the default cluster is full, ObjectStore signals err_cluster_full. If the allocation is huge (greater than 64 KB), the newly allocated object will not be created in the specified cluster. Rather, ObjectStore creates another cluster (called a huge cluster) in the same segment as the specified cluster and allocates from that cluster. Note that a normal cluster can contain any number of normal-sized (less than or equal to 64 KB) allocations; a huge cluster can contain only one huge allocation.

• If you specify a pointer to a segment, storage is allocated from the segment’s default cluster. If the default cluster is full, ObjectStore signals err_cluster_full. If the allocation is huge (that is, greater than 64 KB), ObjectStore creates a huge cluster in the specified segment and allocates from that cluster.

• If you specify a pointer to a cluster, ObjectStore tries to allocate from that cluster. If the cluster is full, ObjectStore signals err_cluster_full. If the allocation is huge (that is, greater than 64 KB), the newly allocated object will not be created in the specified cluster. Rather, ObjectStore creates a new huge cluster in the same segment as the specified cluster and allocates from that newly created cluster.

For information about how ObjectStore allocates when you use the os_cluster::with() overloading of ::operator new(), see Clustering on page 42.

Using ::operator new() to Create Transient Objects If your application requires both transient and persistent versions of an object, you can use ::operator new() to create either one.

To allocate transient storage for an object, you must first get a pointer to the transient database, segment, or cluster, using one of the following methods:

• os_database::get_transient_database()

• os_segment::get_transient_segment()

• os_cluster::get_transient_cluster()

Each method returns a pointer to (respectively) an os_database, os_segment, or os_cluster object, which can be used to represent the transient entity. This pointer can then be specified as the where argument to ::operator new(), resulting in a transient allocation. You can also use os_database::of(obj ), os_segment::of(obj ), or os_cluster::of(obj ) as the where argument. Each of these methods returns a pointer to the transient database, segment, or cluster — provided that obj is transient.

Use one of the following methods to determine whether the database, segment, or cluster is transient or persistent:

• os_database::is_transient_database()

• os_segment::is_transient_segment()

• os_cluster::is_transient_cluster()

36 C++ A P I User Guide

Page 37: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

Each method returns nonzero (true) if the implicit this argument is transient; if it is persistent, the method returns 0 (false).

To determine whether an object has been allocated persistent or transient storage, pass the address of the object to objectstore::is_persistent(). This method returns nonzero (true) if the address points to persistent storage and 0 (false) if it points to transient storage.

::operator delete() ObjectStore overrides ::operator delete() to release either transient or persistent storage. ObjectStore makes the determination at run time, based on whether the object for deletion is transient or persistent. As with ::operator new(), before using ::operator delete() to release persistent storage, you must have created a database, opened it, and begun a transaction. For information on creating and opening databases, see Chapter 4, Basic Database Operations, on page 55. For information about transactions, see Chapter 5, Transactions, on page 75.

If you want to provide your own overloading of ::operator delete() to perform application-specific transient deallocation processing, you must register your delete function, using the function objectstore::set_transient_delete_function(). For more information, see objectstore::set_transient_delete_function() in the C++ A P I Reference.

Examples The following example releases the storage referenced by the pointer obj:

delete obj;

You also can use ::operator delete() to release storage allocated for arrays by including brackets, as provided by the standard C++ syntax. The following example releases storage allocated for an array of objects:

delete [ ] some_objs;

Note For information about deleting transient objects, see Managing Lifetimes of ObjectStore Objects on page 48. For reference information about ::operator delete(), see ::operator delete() in the C++ A P I Reference.

Using TypespecsWhen you use ::operator new() to allocate persistent storage, one of the arguments you must supply is a pointer to an os_typespec object; for information about the syntax, see ::operator new() on page 34. This argument, called a typespec, identifies the type and layout of the allocation.

There are three ways to get a typespec:

• For fundamental types (for example, char and int), call one of the static member functions of the class os_typespec.

Release 6.3 37

Page 38: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Using Typespecs

• For each class defined by your application, declare get_os_typespec() as a static member function of the class and then call the function when you need a typespec for the class.

• Call the constructor for an os_typespec object, passing it the name of the type for which you want the typespec.

The following sections describe each of these approaches.

Note Typespecs can help prevent type mismatch errors when you are retrieving entry-point objects; see Step 3. Retrieving the Entry-Point Object on page 72.

Typespecs for Fundamental Types ObjectStore provides special functions for retrieving typespecs for the fundamental types that are built into the C++ language, such as char and int. These functions are static members of the class os_typespec. The first time such a function is called by a process, it allocates the typespec and returns a pointer to it. Subsequent calls to the function in the same process do not result in further allocation; instead, a pointer to the same os_typespec object is returned.

The following table lists the fundamental types and member functions of the class os_typespec that return a typespec for each type. The table also lists the ObjectStore portable types (for example, os_int32) and the member functions that return them.

Type for Which You Want a Typespec

Function Returning the Typespec

char os_typespec::get_char()

double os_typespec::get_double()

float os_typespec::get_float()

int os_typespec::get_int()

long, long int os_typespec::get_long()

long double os_typespec::get_long_double()

os_int16 os_typespec::get_os_int16()

os_int32 os_typespec::get_os_int32()

os_int64 os_typespec::get_os_int64()

os_signed_int8 os_typespec::get_os_signed_int8()

os_unsigned_int8 os_typespec::get_os_unsigned_int8()

os_unsigned_int16 os_typespec::get_os_unsigned_int16()

os_unsigned_int32 os_typespec::get_os_unsigned_int32()

os_unsigned_int64 os_typespec::get_os_unsigned_int64()

short, short int os_typespec::get_short()

signed char os_typespec::get_signed_char()

signed int os_typespec::get_signed_int()

38 C++ A P I User Guide

Page 39: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

Example The following example illustrates how to use os_typespec::get_char() to pass a typespec to ::operator new() when you are allocating persistent storage for a character array. Following is the class declaration:

class Part { public: char *descrip; int id;

Part(char* s, int n); };

Following is the definition of the constructor that allocates the array:

Part::Part(char* s, int n) { int len = strlen(s)+1; descrip = new(os_cluster::with(this), os_typespec::get_char(), len) char[len]; strcpy(descrip, s);

id = n; }

The first argument to ::operator new() clusters the allocation for the string with the allocation for the Part object (see Clustering on page 42). The second argument supplies the typespec by calling os_typespec::get_char(). The third argument supplies the number of elements in the array.

Typespecs for ClassesIf you can make changes to source code and add a member to a class, the simplest way to retrieve a typespec for that class is to add the following member function to the class declaration:

static os_typespec *get_os_typespec();

You can use any appropriate access specifier (public, private, or protected) for your application. The ObjectStore schema generator (ossg) implements this function by

signed long, signed long int

os_typespec::get_signed_long()

signed short, signed short int

os_typespec::get_signed_short()

unsigned char os_typespec::get_unsigned_char()

unsigned int os_typespec::get_unsigned_int()

unsigned long, unsigned long int

os_typespec::get_unsigned_long()

unsigned short, unsigned short int

os_typespec::get_unsigned_short()

void* pointer os_typespec::get_pointer()

Type for Which You Want a Typespec

Function Returning the Typespec

Release 6.3 39

Page 40: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Using Typespecs

supplying a body automatically. At run time, you can call your_class::get_os_typespec() for a pointer to a typespec for your_class. The return value is static and reusable; your application incurs no expense by calling it and does not have to delete the return value.

For reference information about get_os_typespec(), see os_typed_pointer_void in the C++ A P I Reference.

Following is the class definition of Part, now modified from the version listed in Typespecs for Fundamental Types on page 38, to use get_os_typespec():

class Part { public: char *descrip; int id;

static os_typespec *get_os_typespec(); Part(char* s, int n); };

The following statement uses Part::get_os_typespec() to supply a typespec to ::operator new():

Part *a_part = new(db, Part::get_os_typespec()) Part("wrench", 111);

The first argument, db, is the where argument that specifies the database where a_part will be stored. The second argument is the typespec, supplied by the call to Part::get_os_typespec().

For other examples of Part::get_os_typespec(), see the programs listed in Step 2. Associating the Root with an Entry-Point Object on page 70 and Step 3. Retrieving the Entry-Point Object on page 72.

If it is not possible to change the source code by adding get_os_typespec() as a member function of a class, you must construct an os_typespec object, as described in the next section.

Constructing a Typespec The simplest way to get a typespec is to call one of the member functions of the class os_typespec or to add get_os_typespec() as a member function of the class for which you need the typespec. Both approaches are described in Typespecs for Fundamental Types on page 38 and Typespecs for Classes on page 39, respectively.

However, it is not always possible to use these methods. For example, you might not have access to the source code with the class declarations and, therefore, cannot add get_os_typespec(). In this case, you must construct a typespec by invoking the os_typespec constructor and passing a string with the name of the class. Following is the signature for the constructor:

os_typespec::os_typespec(char* "type_name");

type_name is the name (in quotation marks, no embedded spaces) of a class or fundamental type (such as int and char). See the description of os_typed_pointer_void in the C++ A P I Reference for detailed information.

The following statement constructs a typespec:

40 C++ A P I User Guide

Page 41: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

os_typespec *part_ts = new os_typespec("Part");

After you have created an os_typespec for a particular type (the type emp_typespec in the example above), you can use it repeatedly to create all of the program’s new instances of that type.

There are two disadvantages to constructing a typespec:

• The application incurs a slight start-up overhead the first time it uses a constructed typespec.

• It is the user’s responsibility to delete any typespec that has been created with new.

For these reasons, you should reuse constructed typespecs whenever possible. For example, the following code is legal but highly inefficient because it creates a new typespec with each iteration of the for loop:

// legal but inefficient for (int i=1; i < 100000; i++) { os_typespec part_ts("Part"); new(db, &part_ts) Part (i); }

Following is the preferable way to code this loop:

os_typespec part_ts ("Part");for (int i=1; i<100000; i++) new(db, &part_ts) Part (i);

Note Typespecs must be allocated transiently; do not create a typespec with any of the persistent overloadings of ::operator new().

Parameterized TypespecsThe get_os_typespec() function is particularly useful when you want to create a parameterized typespec for a class template. (For information about get_os_typespec(), see Typespecs for Classes on page 39.) For example, suppose you need a parameterized class that defines a function to allocate a persistent instance of that class and an instance of the parameter. The class might be declared in the following way:

template <class T> class PT { . . . void foo(os_database *db) { // allocate PT<T> persistently new(db, "PT<???>") PT<T>(); // allocate type T persistently new(db, "???") T(); } };

This method does not work, however, because there is no way when you are coding to know the information to fill in for ???. T cannot be used because the C++ template facility does not instantiate it properly. It is inside a string.

The solution is to declare a get_os_typespec() member function for the parameterized class and for each class that will serve as parameter.

class PT_parm {

Release 6.3 41

Page 42: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Clustering

public: static os_typespec *get_os_typespec(); };

template <class T> class PT { static os_typespec *get_os_typespec();

void foo(os_database *db) {

// allocate new PT<T> persistently new(db, PT<T>::get_os_typespec()) PT<T>();

// allocate type T persistently // (assuming it is a suitable class) new(db, T::get_os_typespec()) T(); } };

Clustering Clustering is a common optimization for reducing the number of disk transfers needed to access persistently stored objects. The technique is to allocate contiguous (or nearly contiguous) storage for objects that are accessed together frequently. When designing an application with clustering in mind, the programmer asks: “If my application accesses this object, what other objects is it likely to access along with it?”

Consider, for example, the following class declaration:

class A_class { // some members ... char* s; };

If an A_class object has been allocated persistent storage and you intend to allocate some persistent storage for s, clustering would require allocating s as close as possible to the allocation for the A_class object.

You achieve optimal clustering in ObjectStore applications by allocating related objects in the same cluster. (As explained in Persistent Storage: Organizational Overview on page 14, a cluster is the basic physical unit of allocation in an ObjectStore application.) ObjectStore has overloaded ::operator new() to give you control over the placement of objects in persistent storage. The two that are relevant to clustering are

void* ::operator new ( os_cluster::of(obj ), typespec )

void* ::operator new ( os_cluster::with(obj ), typespec )

obj is a pointer to a previously allocated object that you want clustered with the new allocation. Both methods are static, so you do not need an instance of os_cluster to call them.

The difference between them is that the os_cluster::of() overloading of new allocates storage in the same cluster as obj, whereas the os_cluster::with() overloading allocates as close as possible to obj. os_cluster::with() returns an

42 C++ A P I User Guide

Page 43: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

automatically allocated os_cluster_with object. As explained in os_cluster::with() and os_cluster_with in the C++ A P I Reference, this object contains a pointer to the cluster in which obj is stored. The effect of the with() overloading, however, is to allocate as close as possible to obj.

With either overloading, if the cluster is full (the maximum size of a cluster is 2 GB) or contains a huge allocation (greater than 64 KB), the allocation fails and ObjectStore signals err_cluster_full. If the allocation is huge, ObjectStore creates a huge cluster in the same segment as the specified cluster of obj and allocates from that cluster.

Note The os_cluster::of() method used to overload ::operator new() returns a pointer to a transient os_cluster object that should be deleted when the object is no longer needed. For information about managing the lifetimes of this and other transient objects, see Managing Lifetimes of ObjectStore Objects on page 48. For information about os_cluster::of(), see the C++ A P I Reference.

Pointers and Persistent Memory Using pointers in an ObjectStore application is in many ways no different from using them in a program that works only with transient memory. When you use ::operator new() to allocate persistent storage, you can use the pointer it returns in much the same way that you use pointers to transient memory. However, there are some precautions and restrictions regarding the use of persistent pointers. The following sections explain under what circumstances you can and cannot use persistent pointers, and how to control ObjectStore’s checking for illegal pointers.

Persistent Pointers in Non-ObjectStore Processes It is illegal to pass a pointer to persistent memory to a non-ObjectStore process.

In an ObjectStore application, when a pointer to persistent memory is dereferenced, a hardware fault occurs. As described in Establishing a Page Fault Handler on page 33, the fault-handler macros enable ObjectStore to handle the fault and retrieve the intended data. However, ObjectStore’s fault handler is circumvented when you pass a persistent pointer to one of the following processes and the process dereferences the pointer:

• A system call such as read or ReadFile

• A library function such as fread that might ultimately use such a system call

• A function that itself traps faulting memory references, such as lstrlen on Windows

As a result of the attempted dereference, the system call returns an error indication.

Using os_with_mapped

To ensure that a persistent pointer is accessible during a system call, create an automatic os_with_mapped object, specifying the starting address and size of the range you want to pass to a system call and whether you intend to update the object.

Release 6.3 43

Page 44: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Pointers and Persistent Memory

Within the scope of the os_with_mapped object, the referenced range is available to the system call.

For reference information about os_with_mapped, see os_with_mapped in the C++ A P I Reference.

The following is an example of the way to invoke the constructor for os_with_mapped, just before calling the read system call. Braces are used to call attention to the scope of the mybuf variable.

{ os_with_mapped mybuf(persistent_buffer, buffer_size, 1); read(fd, persistent_buffer, buffer_size); }

The constructor for os_with_mapped ensures that the necessary pages are available and mapped with appropriate access rights, and that those pages are wired into the client cache until the destructor is run. The constructor signals an exception if you run out of room in the cache, if you attempt to wire a page more than 250 times, or if you fail to obtain a page because of deadlock.

The destructor allows the pages to be moved out of the cache.

Technical Support recommends using os_with_mapped whenever you pass a persistent pointer to system calls or library functions that might call system calls or handle memory access violations. There is little overhead to os_with_mapped, so calling it frequently does not degrade performance.

Mapping large ranges, having many simultaneous mappings, or keeping mappings in effect for a long time is not recommended because this can interfere with cache replacement and lead to a cache-is-full exception.

Restriction An os_with_mapped object cannot be used across transaction boundaries, including nested transactions.

Cross-Database Pointers To help boost the performance of certain kinds of applications, ObjectStore’s default for new databases is to disallow cross-database, or external, pointers. By default, if a pointer to persistent memory in one database is assigned to persistent memory in a different database, that pointer is valid only until the end of the outermost transaction in which the assignment occurred. To use cross-database pointers, you must affiliate the databases, as described in Affiliation and Cross-Database Pointers on page 65.

Cross-Transaction Pointers Two types of pointers are normally valid only while in a transaction:

• Persistent-to-transient pointers: pointers stored in an ObjectStore database that reference transient storage

• Transient-to-persistent pointers: pointers used by your application to reference persistent storage

44 C++ A P I User Guide

Page 45: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

Persistent-to-transient pointers are always invalid outside a transaction and will trigger an exception; see Illegal Pointer Checking on page 45. Transient-to-persistent pointers are normally invalid outside the boundaries of a transaction. However, ObjectStore provides an API for retaining persistent addresses across transaction boundaries. For more information, see the following in the C++ A P I Reference:

• os_address_space_marker

• objectstore::retain_persistent_addresses()

For information about retaining transient-to-persistent pointers across transaction boundaries, see the following sections in Chapter 1 of the Advanced C++ A P I User Guide:

• Retaining the Validity of a Specified Variable

• Retaining and Releasing the Validity of All Pointers

Illegal Pointer Checking ObjectStore checks for two kinds of illegal pointers:

• Pointers that reference objects in an unaffiliated database; see Cross-Database Pointers on page 44

• Persistent-to-transient pointers — pointers in persistent memory that reference transient data; see Cross-Transaction Pointers on page 44

Default detection behavior

If ObjectStore detects an illegal pointer when it is relocating a page from the client cache to the database, the default behavior is to signal an exception. (In general, ObjectStore relocates pages to the database when a transaction commits.) However, you can change the default behavior and cause ObjectStore to set the illegal pointer to null. To do so, call the following method with a nonzero (true) argument:

static void set_null_illegal_pointers(os_boolean);

Setting the argument to 0 (false) specifies the default behavior, which is to signal an exception. The change in behavior is in effect only for the current session.

To retrieve the current behavior, call the following method:

static os_boolean get_null_illegal_pointers();

Both set_null_illegal_pointers() and get_null_illegal_pointers() are defined by four classes, as described in the C++ A P I Reference:

• objectstore::get_null_illegal_pointers() and objectstore::set_null_illegal_pointers()

• os_cluster::get_null_illegal_pointers() and os_cluster::set_null_illegal_pointers()

• os_database::get_null_illegal_pointers() and os_database::set_null_illegal_pointers()

• os_segment::get_null_illegal_pointers() and os_segment::set_null_illegal_pointers()

Release 6.3 45

Page 46: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Applying Control Settings to Persistent Storage

By defining both methods in the four classes, ObjectStore enables you to set or retrieve the behavior in specific databases, segments, or clusters; for more information, see Applying Control Settings to Persistent Storage on page 46.

Correcting illegal pointers

When you are debugging a database, an illegal pointer might already have been written to the database that you want to correct. To prevent ObjectStore from signaling an exception when it detects an illegal pointer while relocating pages from the database to the client cache, call the following method:

static void objectstore::set_always_null_illegal_pointers( os_boolean);

Calling this method allows you to correct an illegal pointer. Note that this method is defined by the objectstore class only. For more information, see objectstore::set_always_null_illegal_pointers() in the C++ A P I Reference.

Other illegal pointers

Other illegal pointers that can corrupt a database include

• Pointers to deleted objects (dangling pointers)

• Incorrectly typed pointers

However, ObjectStore does not check for such pointers.

Applying Control Settings to Persistent Storage

When programming with persistent storage, you can specify or retrieve various control settings at different levels of granularity. These levels (from highest to lowest) are represented by the ObjectStore classes listed in the following table:

The control settings that are applicable at each of these levels and the methods you invoke to specify or retrieve a setting are listed in the next table. Note that the

Class Level of Granularity

objectstore All databases in the current session

os_database A specific database

os_segment A specific segment in a database

os_cluster A specific cluster in a segment

46 C++ A P I User Guide

Page 47: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

methods are available in get/set pairs: the get version retrieves the setting and the set version specifies it.

For detailed information about these methods and the settings they retrieve or specify, see Chapter 2, Class Library, of the C++ A P I Reference. For information about using get_null_illegal_pointers() and set_null_illegal_pointers(), see Illegal Pointer Checking on page 45.

The following illustrates how these methods work when specified at different levels:

Suppose that you want to set the fetch policy for all clusters in a specific segment of a specific database. The desired result will be that any operation on a cluster causes ObjectStore to attempt to fetch the whole cluster in a single client/server interaction. To achieve this result, you would make a call similar to the following example, where pseg is a pointer to the segment for which you want to set the policy:

pseg->set_fetch_policy(os_fetch_cluster);

If the fetch policy for the database containing this segment was previously set to os_fetch_segment, the call would override that setting only for the segment referenced by pseg. If you were subsequently to call the os_database::set_fetch_policy() method, the new policy would apply to all segments in the database, including the one referenced by pseg.

Note that calling the objectstore::set_fetch_policy() method overrides the fetch policy currently in effect for all databases in the current session, including all segments and clusters in those databases.

For information about fetch policy, see Setting Data Fetch Policies in Chapter 1 of the Advanced C++ A P I User Guide.

Grouping Clusters in Segments The segment is the unit of persistent storage represented by the class os_segment. (To see how the segment fits in the hierarchical organization of persistent storage,

Method Control Setting

get_allocation_strategy() set_allocation_strategy()

Strategy when allocating storage for an object: shorter wait time or better contiguity

get_fetch_policy() set_fetch_policy()

Fetch quantum when operating on a persistent object: segment, cluster, stream, or page

get_lock_option() set_lock_option()

Locking behavior when accessing a database: read lock or write lock at different levels

get_null_illegal_pointers() set_null_illegal_pointers()

Error indication when an illegal pointer is detected: signal an exception or set the pointer to 0

Release 6.3 47

Page 48: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Managing Lifetimes of ObjectStore Objects

refer to Persistent Storage: Organizational Overview on page 14.) If the cluster is the level for grouping objects for optimum performance (as described in Clustering on page 42), the segment is the level for grouping clusters that are logically related. That is, segments allow you to group clusters (and the objects they contain) according to certain shared characteristics.

Segment-level access

For example, you might want to restrict access to one group of related objects in a database and not to others. Preparatory to setting any permissions, you would create a “restricted” segment, allocate the objects from this segment, then use the os_segment_access class to establish appropriate access permissions. For information about using the Segment-Level Access API, see Controlling Segment-Level Access on page 103.

Segment reference policies

You can also specify segment reference policies at the segment level. Segment reference policies allow highly efficient segment-level data reorganizations (for example, garbage collection and compaction) that do not affect pointers to the data from other segments. For more information, see Setting Segment Reference Policies in Chapter 1 of the Advanced C++ A P I User Guide.

Using Segment Comments Comments are strings that you write to or read from a segment. They are persistently stored with the segment and provide a useful and humanly readable way to identify its contents. When you use the ossize utility to display information about a database, its output includes any segment comments.

To specify or retrieve a comment, use the following methods:

char* os_segment::set_comment() const;

void os_segment::get_comment(const char* comment );

comment is a null-terminated string of indefinite length.

Comments are applicable only at the segment level. For more information, see the following in the C++ A P I Reference:

• os_segment::get_comment()

• os_segment::set_comment()

For more information about the ossize utility, see ossize in Managing ObjectStore.

Managing Lifetimes of ObjectStore Objects Certain methods in the ObjectStore API return pointers to transient objects (ObjectStore objects), including objects of the following classes:

• os_cluster

• os_database

• os_database_root

• os_segment

48 C++ A P I User Guide

Page 49: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

The ObjectStore objects of these four classes represent persistent entities — clusters, databases, database roots, and segments — that allow you to call ObjectStore methods on the persistent entities.

Lifetime issues ObjectStore objects are not automatic variables. Unless they are deleted, their lifetimes can last until the application finishes executing. It is, therefore, the user's responsibility to manage their lifetimes. If you want to reuse the storage allocated for an ObjectStore object, you must ensure that it is deleted when it is no longer needed.

Some ObjectStore objects are easily managed because they are seldom needed except for a well-defined purpose. For example, an os_server object is usually created for a single purpose and can be deleted after the purpose has been achieved.

Other ObjectStore objects are more frequently used but have a clearly marked period of usefulness. For example, an os_transaction object is generally used for managing a dynamic transaction. After the transaction has committed, the object can be deleted.

For objects of the classes os_cluster, os_database, os_database_root, and os_segment, it is less clear when to delete them. Because of the special relationship between these objects and the persistent entities they represent, an application is likely to use them frequently and repeatedly, and in different parts of the application. One part of an application can pass an ObjectStore pointer to another part and not know when it can safely delete the object.

The following sections provide information about managing the lifetimes of ObjectStore objects of the classes os_cluster, os_database, os_database_root, and os_segment:

• Use Counts

• Using the Retain-Release API

• Using delete

• Releasing Pointers with os_release_ . . . _pointer Classes

• Methods Returning Pointers to ObjectStore Objects

Use Counts ObjectStore helps to control the proliferation of transient objects of the classes os_cluster, os_database, os_database_root, and os_segment by maintaining a use count. Instead of creating a new ObjectStore object each time the application calls a method on the same persistent entity, ObjectStore first checks to see if it has already created such an object. If it has, it returns a pointer to that object and increments the use count. The use count thus represents the number of pointers to a given object that are currently in use.

To get the benefit of the use count for help in managing ObjectStore objects, the application must use the Retain-Release API, as described in the next section.

Release 6.3 49

Page 50: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Managing Lifetimes of ObjectStore Objects

Using the Retain-Release API The retain-release API consists of two methods, retain_pointer() and release_pointer(), which are defined for these classes:

• os_cluster

• os_database

• os_database_root

• os_segment

Using release_pointer()

Call release_pointer() when you no longer need a pointer to an object of one of these classes. This method releases the pointer by decrementing the use count of pointers that have been returned to the application for the same object. When the count reaches 0, release_pointer() deletes the object.

In the following example, db points to a database for which the code retrieves a pointer to an os_segment object that represents the default segment:

os_segment* pseg = db->get_default_segment(); . . . // operations on the segment referenced by pseg . . . pseg->release_pointer();

If pseg is the only outstanding pointer returned against the default segment, the call to release_pointer() deletes the object; otherwise, it decrements the count of pointers to this object that the application is currently using.

Note The release_pointer() function is also defined for the tix_exception class. For mor information, see tix_handler on page 99.

Using retain_pointer()

The retain_pointer() method has a more limited use. It increments the use count to ensure that a pointer to an ObjectStore object is retained even when another part of the application releases it. For example, an application might pass an ObjectStore pointer to a constructor that copies it into an object. To avoid the risk of releasing the copy’s pointer when the original is released, the constructor can call retain_pointer(). The destructor would call release_pointer() to release the copy.

Calling retain_pointer() is also useful in multithreaded applications when one thread (Thread1) passes an ObjectStore pointer to another (Thread2). Thread2 would call retain_pointer() so that Thread1 could release the pointer without the risk of releasing Thread2’s copy. Thread2 would call release_pointer() when it finishes with the copy.

Using delete ObjectStore provides overloadings of delete for use when deallocating persistent or transient storage. (For information about using delete to deallocate persistent storage, see ::operator delete() on page 37.)

When you use delete on an ObjectStore object, it deallocates the object without regard to its use count. It is, therefore, essential that before deleting an object, your

50 C++ A P I User Guide

Page 51: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

application know that the object is no longer used anywhere else in the application. After the object is deleted, any pointers to the object are invalid.

Releasing Pointers with os_release_ . . . _pointer Classes ObjectStore provides the classes listed in the following table for releasing pointers to the indicated ObjectStore objects:

Each class enables you to release a pointer when a variable of the class goes out of scope. After obtaining an ObjectStore pointer, you declare an automatic variable of the appropriate class and pass the pointer to the variable’s constructor. When the variable goes out of scope, its destructor releases the pointer.

The same release mechanism used by the release_pointer() method is also used here. When the pointer is released, its use count is decremented. When the count reaches 0, the ObjectStore object referenced by the pointer is deleted. For information about use counts and releasing pointers, see Using the Retain-Release API on page 50.

Using an os_release_. . . _pointer class to release a pointer respects an application’s modularity. It allows one part of an application to obtain and release a pointer without having to know if the object it points to is still in use elsewhere in the application.

In the following example, seg is an os_segment pointer and is used to call get_default_cluster() and obtain pc, a pointer to the segment’s default cluster. pc is then passed to the constructor for auto_var, an automatic variable of the class os_release_cluster_pointer. At the end of the block in which auto_var is declared, the destructor releases pc. If pc is the only pointer to the os_cluster object in use, the object itself is deleted.

{ os_cluster* pc = seg->get_default_cluster(); os_release_cluster_pointer auto_var(pc); . . . // operations on the cluster . . . } // auto_var’s destructor releases pc

For more information about get_default_cluster(), see os_segment::get_default_cluster() in the C++ A P I Reference.

Class Pointer Type

os_release_cluster_pointer os_cluster

os_release_database_pointer os_database

os_release_root_pointer os_database_root

os_release_segment_pointer os_segment

Release 6.3 51

Page 52: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Managing Lifetimes of ObjectStore Objects

Methods Returning Pointers to ObjectStore Objects The following methods return pointers to ObjectStore objects and are likely to be called frequently and repeatedly:

• The of() method of os_database, os_segment, and os_cluster

• os_database::get_all_databases()

• os_segment::get_all_clusters()

• os_database::get_all_roots()

• os_database::get_all_segments()

Unless an ObjectStore application is careful to capture and release the pointers returned by these methods, it runs the risk of a memory leak.

os_cluster::of() For example, an application can call os_cluster::of() or os_segment::of() in a highly iterative loop as the argument to ::operator new(), as in the following:

for ( . . . ) { new_obj = new(os_cluster::of(old_obj), obj_type) . . . ; . . . }

The trouble with this loop is that, at each iteration, the call to os_cluster::of() returns a pointer that the application discards, possibly creating a memory leak. How serious this problem is depends on several factors. If old_obj points to an object that is in the same cluster at each iteration, the resulting leak is minor. However, if old_obj points to an object in a different cluster at each iteration, many different os_cluster objects are returned and the leak could be severe, especially if the number of clusters is large.

There are several ways to handle this problem. One is to assign the return value from os_cluster::of() explicitly, then call os_cluster::release_pointer(), as follows:

for ( . . . ) { os_cluster* diff_cluster = os_cluster::of(old_obj); new_obj = new(diff_cluster, obj_type) . . . ; . . . diff_cluster->release_pointer(); // release the os_cluster pointer }

Another approach is to leave the allocation loop as originally coded and arrange to delete the os_cluster objects in some other part of your program. This approach only works if your program can determine the os_clusters that were returned and when it is safe to delete them.

A third approach is to avoid the creation of os_cluster objects by using the os_cluster::with() overloading of ::operator new(). os_cluster_with() returns an automatic variable that is destroyed whenever it goes out of scope.

Following is the same example, coded to use os_cluster::with():

for ( . . . ) { new_obj = new(os_cluster::with(old_obj), obj_type) . . . ; . . .

52 C++ A P I User Guide

Page 53: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 3: Programming with Persistent Storage

}

For more information about using os_cluster::with(), see Clustering on page 42.

get_all...() methods

Other methods that risk memory leaks include the get_all. . .() methods defined by os_database and os_segment, for example, os_segment::get_all_clusters(). These methods set up a table of pointers, each of which points to an ObjectStore object that, if not deleted, can stay allocated for the life of the application. For more information, see the following in the C++ A P I Reference:

• os_database::get_all_databases()

• os_segment::get_all_clusters()

• os_database::get_all_roots()

• os_database::get_all_segments()

Release 6.3 53

Page 54: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Managing Lifetimes of ObjectStore Objects

54 C++ A P I User Guide

Page 55: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4Basic Database Operations

When you create a persistent object, you create it in a particular database. This chapter discusses operations that you must perform on a database to store and access persistent data there. It covers the following topics:

Creating a Database 55

Destroying a Database 59

Opening a Database 59

Closing a Database 63

Finding a Database 64

Affiliation and Cross-Database Pointers 65

Database Roots and Entry-Point Objects 69

Working with Rawfs Databases 74

Creating a Database You create a file or rawfs database by calling

static os_database *os_database::create( const char *pathname, os_int32 mode = 0664, os_boolean if_exists_overwrite = 0, os_database *schema_database = 0);

This function is static and so requires no implicit this argument. If the call is successful, the function returns a pointer to an object of type os_database, which represents the database. You can use this pointer in all subsequent operations on the database. You can call this function from either inside or outside a transaction. Once created, the database is open and can be used for storing persistent data.

Note The os_database pointer returned by create() points to a transient object, unlike the database it stands for. If you copy the pointer to persistent storage, it is meaningless when retrieved by another process. If you want to record the identity of a database in persistent storage, you should either record the database’s pathname, use a reference, or use a cross-database pointer (see Affiliation and Cross-Database Pointers on page 65).

Release 6.3 55

Page 56: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Creating a Database

The following sections provide additional information about using create(), including its arguments:

• Naming the Database: pathname Argument

• Controlling Protection: mode Argument

• Overwriting an Existing Database: if_exists_overwrite Argument

• Specifying a Remote Schema Database: schema_database Argument

• Creating a Database: os_database::open()

For reference information about the create() method, see os_database::create() in the C++ A P I Reference.

Naming the Database: pathname Argument pathname is the name you want the new database to have. When you create a database file, the pathname consists of an operating system pathname. (This book uses UNIX-style database pathnames with slashes ( / ) as separators, but your operating system might use a different style of pathname.) The following creates the database parts in the directory /ken:

os_database *db1 = os_database::create( "/ken/parts" );

ObjectStore considers local network mount points when interpreting the pathname, so the pathname can refer to a database on a foreign host. (An ObjectStore server must be running on any host containing an ObjectStore database.) If you want to refer to a database file on a foreign UNIX host for which there is no local mount point, you can use a server host prefix — the name of the foreign host followed by a colon (:), as in oak:/foo/bar.

If you supply the pathname of a database that already exists, ObjectStore signals err_database_exists (but see Overwriting an Existing Database: if_exists_overwrite Argument on page 57).

Note The database pathname syntax differs for rawfs databases; see Rawfs Host Prefix on page 74.

Controlling Protection: mode Argument The mode argument specifies a protection mode, which is an octal value that combines one or more of the values in the following table. Note that, because the value is octal, it must begin with 0.

mode Value Protection

0400 Read by user

0200 Write by user

0040 Read by group

0020 Write by group

0004 Read by others

0002 Write by others

56 C++ A P I User Guide

Page 57: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

mode defaults to 0664 (read/write permission for user and group; read permission for all others). The following example creates the database named parts and gives read/write permission to everyone:

os_database *db1 = os_database::create("parts", 0666);

Overwriting an Existing Database: if_exists_overwrite Argument You can direct os_database::create() to overwrite any existing database with the same name instead of signaling an exception. To do this, specify a nonzero value (true) as the value of the defaulted argument if_exists_overwrite. Specify 0 (false) for the default behavior: do not overwrite an existing database. If no database of the specified name exists, if_exists_overwrite has no effect.

The following example sets if_exists_overwrite to 1 (true), causing create() to overwrite parts, if it already exists. Note that the mode argument must be specified, if only to specify (as in this example) the default.

os_database *db1 = os_database::create("parts", 0664, 1);

Specifying a Remote Schema Database: schema_database Argument

Every ObjectStore database has associated schema information — information about the classes of objects the database contains, as described in Database Schemas on page 129. When you create a database, if the schema_database argument is 0, schema information is stored in the database you are creating. If schema_database is nonzero, the argument is interpreted as a pointer to another, previously created database that will be used as the schema database for the database specified by pathname. ObjectStore puts all the schema information for the new database in the specified schema database; the new database itself will contain no schema information.

If the specified schema database is not already open, it is opened for update. If it is open for read-only, the newly created database cannot accept any updates that require schema information because no schema information can be written to the schema database.

The new database’s schema database can also contain user data (that is, data other than schema information). However, the schema database must store its own schema locally. If the schema for the user data in schema_database is stored remotely, err_schema_database is signaled.

For additional information about schema databases, see os_database::get_schema_database() and os_database::set_schema_database() in the C++ A P I Reference.

Creating a Database: os_database::open()Typically, you call os_database::create() to create a database. However, under certain circumstances, you can call os_database::open() to create a database. For

Release 6.3 57

Page 58: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Creating a Database

more information, see Creating a Database: create_mode Argument on page 60. For information about open(), see Opening a Database on page 59.

58 C++ A P I User Guide

Page 59: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

Destroying a Database To destroy a database, call

void os_database::destroy();

This function takes no arguments (other than the implicit this argument) and has no return value. The following example creates the database named tmp and then destroys it:

os_database* db1 = os_database::create("tmp"); . . . db1–>destroy();

If the database is open at the time of the call, destroy() closes it before destroying it.

Destroying a Concurrently Opened Database Destroying a database can affect any other session that has the database opened. Such a session might be unable to access some of the database’s data, even if it has already accessed the database successfully earlier in the same transaction.

Data already cached in the client cache continues to be accessible, but attempts to access other data cause ObjectStore to signal err_database_not_found. Attempts to open the database also provoke err_database_not_found.

For reference information about the destroy() method, see os_database::destroy() in the C++ A P I Reference.

Opening a Database To open a previously created database, call one of the following overloadings of os_database::open():

static os_database *os_database::open( const char *pathname, os_boolean read_only = 0, os_int32 create_mode = 0);

static os_database *os_database::open( const char *pathname, os_boolean read_only, os_int32 create_mode, os_database *schema_database);

void os_database::open(os_boolean read_only = 0);

The first two overloadings are static, which means that they do not require the implicit this argument. Each returns a pointer to the opened database. For information about the third overloading, see Reopening a Closed Database on page 64.

Release 6.3 59

Page 60: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Opening a Database

The following sections provide additional information about using open(), including its arguments:

• Naming the Database: pathname Argument

• Controlling Access: read_only Argument

• Creating a Database: create_mode Argument

• Specifying a Remote Schema Database: schema_database Argument

• Multiple Opens

• Opening a Database with create()

• Determining Database Open Status

• Multiversion Concurrency Control (MVCC)

For reference information about the open() method, see os_database::open() in the C++ A P I Reference.

Naming the Database: pathname Argument pathname is the name of the database you want to open. If no database with that name exists, an err_database_not_found exception is signaled. However, you can override this behavior and force open() to create a database named pathname if none exists; see Creating a Database: create_mode Argument on page 60.

For information about the conventions used by ObjectStore to form a pathname, see Naming the Database: pathname Argument on page 56 for the os_database::create() function.

Note The regular file-system database pathname syntax differs for rawfs databases; see Rawfs Host Prefix on page 74.

Controlling Access: read_only Argument The read_only argument is a defaulted argument. When 0 (false, the default), read_only specifies that the database is open for read/write access. Setting read_only to nonzero (true) specifies that the database is open for read-only access.

The following statement opens the database parts for read-only access:

os_database *db1 = os_database::open("parts", 1);

If you attempt to write to a database that has been opened for read-only access, an err_opened_read_only exception is signaled.

Creating a Database: create_mode Argument By default, open() triggers the exception err_database_not_found if it cannot open the specified database. You can override this behavior with the create_mode argument. If this argument is set to nonzero and no database named pathname exists, open() creates a new database with that name. When create_mode is nonzero, it is interpreted as a protection mode. To specify the protection mode, combine one or

60 C++ A P I User Guide

Page 61: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

more of the values listed in the following table. Note that, because the value is octal, it must begin with 0.

By default, create_mode is 0, causing open() to signal an exception if the database does not exist.

The following statement tries to open the database named parts. If none exists, it creates one for read/write access by the user and group only:

os_database* db1 = os_database::open("parts", 0, 0660);

Specifying a Remote Schema Database: schema_database Argument

schema_database is a defaulted argument that has meaning only if the specified database does not exist and the create_mode argument is nonzero. In this case, if schema_database is nonzero, it is a pointer to the schema database for the newly created database. This means that ObjectStore installs in the schema database all schema information for the data stored in the new database; the new database itself will have no schema information in it. The schema_database argument has no meaning if the specified database exists or if the create_mode argument is 0.

For more information about this argument, see the discussion of the same argument for the os_database::create() function in Specifying a Remote Schema Database: schema_database Argument on page 57.

Multiple Opens An application can open the same database more than once without closing it so long as each call to open() specifies the same mode as the initial call. (The open modes are MVCC, multidatabase MVCC, read-only, and update.) If the second call requests a different mode, err_db_cannot_change_open is signaled.

Open count Each time a database is opened, ObjectStore increments an open count. Calling close() decrements the count. Only when the count reaches 0 does the database become closed. This behavior respects an application’s modularity by allowing one part to close a database without affecting its open state in another part.

To force a database to close regardless of its open count, call

os_database::close(1);

create_mode Value

Protection

0400 Read by user

0200 Write by user

0040 Read by group

0020 Write by group

0004 Read by others

0002 Write by others

Release 6.3 61

Page 62: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Opening a Database

For information about close(), see Closing a Database on page 63.

Opening a Database with create() A database is opened automatically when you create it with os_database::create(). For more information about the create() method, see Creating a Database on page 55.

Determining Database Open StatusYou can check a database’s open mode (MVCC, multidatabase MVCC, read-only, or update) by calling one of the following:

• os_database::is_open()

• os_database::is_open_multi_db_mvcc()

• os_database::is_open_mvcc()

• os_database::is_open_single_db_mvcc()

• os_database::is_open_read_only()

• os_database::is_open_update()

The is_open() function returns a nonzero integer (true) if the specified database is open for read-only, MVCC, multidatabase MVCC, or update; otherwise, it returns 0 (false). The is_open_mvcc() function returns a nonzero integer (true) if the specified database is open for either single-database MVCC or multidatabase MVCC; otherwise it returns 0 (false). The other three functions return true only if the database is open for the mode indicated by the name of the function.

Multiversion Concurrency Control (MVCC)Multiversion concurrency control (MVCC) allows you to perform nonblocking reads of a database. You can open a database in single-database MVCC or multidatabase MVCC modes. To open a database in MVCC mode, call one of the following:

static os_database *os_database::open_mvcc( const char *pathname);

void os_database::open_mvcc();

static os_database *os_database::open_multi_db_mvcc( const char *pathname);

void os_database::open_multi_db_mvcc();

For more information, see Multiversion Concurrency Control (MVCC) in Chapter 2 of the Advanced C++ A P I User Guide. For information about the open_mvcc() and open_multi_db_mvcc() methods, see os_database::open_mvcc()and os_database::open_multi_db_mvcc() in the C++ A P I Reference.

62 C++ A P I User Guide

Page 63: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

Closing a Database To close a database, call

void os_database::close(os_boolean force = 0);

If force is nonzero (true), the database is closed even if its open count is nonzero; that is, it has been opened more than once without successive closes. The default value is 0 (false), which causes close() to decrement the open count and close the database only when the count reaches 0. For more information, see Close and Multiple Opens: force Argument on page 63.

When a database is closed, its data is inaccessible. The call to close() can occur inside or outside a transaction. However, if you close the database inside a transaction, the database is not actually closed until after the transaction commits.

Calling close() on a database that is already closed has no effect.

For reference information about the close() method, see os_database::close() in the C++ A P I Reference.

Close and Multiple Opens: force Argument As explained in Multiple Opens on page 61, calling close() on an open database does not necessarily close the database. If you have called open() more than once on the same database, you must call close() an equal number of times for the database to become closed.

However, you can force a database to close regardless of the number of times it has been opened by calling close() with the force argument set to nonzero (true). By default, this argument is 0 (false).

The following shows the effect of the force argument:

os_database* db = os_database::open("parts"); db = os_database::open("parts"); db->close(); // the "parts" database is still open db = os_database::open("parts"); db->close(1); // the database is closed

Note that, even though there are more opens than closes in this example, after the call to close(1), the database is closed and its open count is reset to 0.

Using the os_close_database Class The os_close_database class allows you to close a database even when an exception occurs while it is open. To use this class, declare an automatic variable of the class, giving its constructor a pointer to the open database, as in the following example:

os_database* db = os_database::open("notes"); os_close_database auto_dbvar(db);

Release 6.3 63

Page 64: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Finding a Database

When auto_dbvar goes out of scope (for example, because the function in which it was declared returns), its destructor calls os_database::close(). The destructor is also invoked if an exception occurs before the function can return, bringing the database to the state it was in before entering the scope of the variable.

For reference information about the os_close_database class, see os_close_database in the C++ A P I Reference.

Reopening a Closed Database You can reopen a closed database by calling

void os_database::open(os_boolean read_only = 0);

read_only is a Boolean value that specifies whether the database is reopened for read/write access (nonzero, the default) or read-only (0). The implicit this argument that is used to reopen the database is the same one that was returned by the original open() function.

Consider the following example. A database named parts is opened for processing in update mode, closed, and then reopened in read-only mode for additional processing:

os_database* db1 = os_database::open("parts"); // original open OS_BEGIN_TXN(tx1, 0, os_transaction::update) { // do some processing } OS_END_TXN(tx1) db1–>close(); // first close . . . db1->open(1); // reopen in read-only mode OS_BEGIN_TXN(tx2, 0, os_transaction::read_only){ // read-only processing } OS_END_TXN(tx2) db1–>close(); // second close

For more information about the open() method, see Opening a Database on page 59.

Finding a Database By pathname If you have the pathname of a database and want to retrieve the pointer to it without

actually opening it, use the function

static os_database* os_database::lookup( const char *pathname, os_int32 create_mode);

If the named database exists, lookup() returns a pointer to the database but without opening it. If the database is already open, lookup() returns a pointer to the open database. If no such database exists and the create_mode argument is either 0 or is unspecified, an err_database_not_found exception is signaled. If create_mode is nonzero, however, ObjectStore attempts to create the database, just as os_database::open() does when create_mode is specified as an argument. create_

64 C++ A P I User Guide

Page 65: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

mode has the same meanings it has for open(); see Creating a Database: create_mode Argument on page 60.

By pointer If you have a pointer to a database and want to find its pathname, call

char* os_database::get_pathname() const;

This function returns a char*, the pathname of the database pointed to by the implicit this argument. The return value is newly allocated on the heap and so must be explicitly deleted to prevent a memory leak.

For an example of the way to use this function, see Finding All Affiliated Databases on page 68. For more information about lookup() and get_pathname(), see the following in the C++ A P I Reference:

• os_database::lookup()

• os_database::get_pathname()

Affiliation and Cross-Database Pointers As mentioned in Cross-Database Pointers on page 44, such pointers become, by default, invalid at the end of a transaction. If you want persistent pointers in one database to reference objects stored in another, you must affiliate the databases by using the following method:

void os_database::affiliate( os_database* target_db, os_boolean deep = 0, os_boolean relative = 1, const char* common_parent );

Calling affiliate() with just the target_db argument enables the this database to contain cross-database pointers to target_db. After target_db is affiliated with the this database, the current process and all subsequent processes can dereference any pointers in the this database that reference target_db.

After the following statement executes,

db1->affiliate(db2);

you can store pointers in db1 to db2. If you also want to store pointers in db1 to other databases, you call affiliate() for each of those databases. Likewise, if you want to store pointers in db2 to db1, you must call

db2->affiliate(db1);

Calling affiliate() starts and commits a transaction, if one is not already in progress. In general, you should call affiliate() outside a transaction. Calling it inside a transaction sets a write lock on the header of the database until the transaction commits.

Also, when you dereference a cross-database pointer, the target database is opened automatically if it is not already open.

Release 6.3 65

Page 66: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Affiliation and Cross-Database Pointers

The following sections provide additional information about using affiliate(), including its arguments:

• Affiliating with Other Databases: deep Argument

• Storing Cross-Database Pathnames: relative Argument

• Specifying a Common Parent Directory: common_parent Argument

• Finding All Affiliated Databases

• Changing Affiliation with osaffiliate

For reference information about the affiliate() method, see os_database::affiliate() in the C++ A P I Reference.

Affiliating with Other Databases: deep Argument The os_database::affiliate() method has as its second, defaulted argument a Boolean value called deep. By default, this argument is set to 0 (false). If set to nonzero (true), it enables the this database to affiliate not only with the target database but also with all databases with which the target is affiliated. Setting deep to true enables the this database to store pointers to objects in the target database as well as in any databases with which it is affiliated.

For example, if db2 contains pointers to db3, the following statement

db1->affiliate(db2, 1);

enables you to store pointers in db1 to both db2 and db3. Note, however, that db1 picks up only the affiliations that db2 has at the time of the call to affiliate(). If db2 subsequently affiliates with other databases, db1 does not also become affiliated with them without another call to affiliate().

Storing Cross-Database Pathnames: relative Argument The os_database::affiliate() method has as its third, defaulted argument a Boolean value called relative. When set to nonzero (true, the default), this argument specifies that cross-database pointers are to be stored according to the relative pathname of the database containing the referenced data. For example, consider a database with pathname /thx/parts that contains a pointer to data in a database with pathname /thx/engineers. Because these two databases have a common parent directory (/thx), the path to engineers is stored as "engineers".

Resolution by relative pathname

The following illustrates relative pathnames. Suppose that you copy the two databases /thx/parts and /thx/engineers to the directory /odi. The copies thus have the pathnames /odi/parts and /odi/engineers. If the affiliation is established with relative set to nonzero (the default), the copy of the cross-

66 C++ A P I User Guide

Page 67: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

database pointer in /odi/parts refers to data in /odi/engineers and not to the original database, /thx/engineers.

Resolution by absolute pathname

If relative is set to 0, affiliation is stored according to the absolute pathname of the target database. When the resolution is by absolute pathname, the copy of the pointer in the /odi/parts database points to data in the original database, /thx/engineers.

Determining the resolution

To determine the pathname used to reach the target database, use the following method:

char* os_database::get_path_to(os_database* target_db) const;

If the affiliation is by absolute pathname, get_path_to() returns the fully qualified path to target_db, including the server prefix. If the affiliation is by relative pathname, get_path_to() returns the path that is combined with the path of this. The return value is a newly allocated string that must be explicitly deleted.

For reference information about the get_path_to() method, see os_database::get_path_to() in the C++ A P I Reference.

Specifying a Common Parent Directory: common_parent Argument

As explained in Storing Cross-Database Pathnames: relative Argument on page 66, the algorithm used to resolve a cross-database pointer by relative pathname finds the lowest directory (the common parent) that the two pathnames have in common. For

Original databases Copy databases

/odi/parts

/odi/engineers

/thx/parts

/thx/engineers

Original databases Copy databases

/thx/parts /odi/parts

/odi/engineers/thx/engineers

Release 6.3 67

Page 68: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Affiliation and Cross-Database Pointers

example, if a pointer is stored in / A / B / C / db1 and refers to data in / A / B / D / db2, the lowest common directory is A / B, so the relative pathname of the referent directory is . . / D / db2.

You can override this algorithm by calling os_database::affiliate() with its fourth argument, common_parent, set to a different common parent. For example, if the string "/A" is passed as the common_parent argument, the affiliation is stored as ../../D/db2. The common_parent argument is optional and, if supplied, has meaning only if the argument relative is set to nonzero (true); see Storing Cross-Database Pathnames: relative Argument on page 66.

Finding All Affiliated Databases To find all target databases with which a database is affiliated, use the following method:

void os_database::get_affiliated_databases( os_int32 &n_databases, os_database**& databases ) const;

You can display all the target databases for any specific database by combining this method with os_database::get_pathname() in a for loop, as follows:

char* p; db1->get_affiliated_databases(n, db_vector); for (os_int32 i = 0; i < n; i--) { p = db_vector[i]->get_pathname(); cout << p << endl; delete [] p; } delete [] db_vector;

Note that, as indicated in the example, both the db_vector argument to get_affiliated_databases() and the return value of get_pathname() must be explicitly deleted to prevent a memory leak.

You can also use the ossize utility to display the pathnames of databases with which the target database is affiliated; see ossize in Managing ObjectStore. For reference information about the get_affiliated_databases() method, see os_database::get_affiliated_databases() in the C++ A P I Reference.

Changing Affiliation with osaffiliate You can change a database’s affiliation with the osaffiliate utility; see osaffiliate in Managing ObjectStore. You can also change a database’s affiliation programmatically, using the os_database::change_affiliation() function; see os_database::change_affiliation() in the C++ A P I Reference.

Both change_affiliation() and osaffiliate replace the path to an affiliated database with the path to the target database. Making this replacement is especially useful when you have copied a database that is affiliated with another database and you want to change the affiliation from the original database to its copy. Neither osaffiliate nor change_affiliation() modifies or validates the user’s persistent pointers.

68 C++ A P I User Guide

Page 69: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

Database Roots and Entry-Point Objects After you have allocated an object in persistent storage and stored data there, how do you retrieve the data in subsequent processes? ObjectStore provides a retrieval mechanism based on the database root and entry-point objects.

A database root is a persistent entity that is stored in your database. Its sole purpose is to enable you to associate it with a user-defined object that is also stored in your database. After the association has been established, the current process or any other process can retrieve the associated object and use it as the entry point to navigate to other objects in the database. For example, if a database consists of a linked list of objects, you need only retrieve the topmost object in the list to retrieve the remaining objects. The topmost object is the one you would associate with the root.

Typically, a database has a relatively small number of objects designated as entry-point objects, each of which allows access to a large network or collection of related objects.

The process of retrieving the entry-point object involves three steps:

1 Create a root, using os_database::create_root().

2 Associate the root with a persistent object that you want to use as the entry point into your database, using os_database_root::set_value().

3 Use the root to retrieve the object, using os_database_root::get_value().

The following sections describe each step. The last section, Destroying the Database Root on page 73, describes how to destroy the root.

For reference information about the methods for performing operations on roots, see the following in the C++ A P I Reference:

• os_database::create_root()

• os_database_root::get_value()

• os_database_root::set_value()

Step 1. Creating a Database Root To create a database root, use the following method:

os_database_root* os_database::create_root(const char* name);

create_root() returns a pointer to a transient object of the class os_database_root. This pointer can be used for subsequent operations on the persistent root.

The name argument is stored along with the root in the this database. You use it to look up the database root by name, so you should record it in all applications that access the root. ObjectStore copies the name you supply into persistent storage, so you can pass a transiently allocated string.

You can create a root only in persistent storage. Supplying a transient database to create_root() causes ObjectStore to signal err_database_not_open. You cannot

Release 6.3 69

Page 70: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Database Roots and Entry-Point Objects

control the clustering of database roots in persistent storage. ObjectStore always stores the roots of each database together in a special segment.

For an example program that creates a root, see Step 2. Associating the Root with an Entry-Point Object on page 70.

Naming roots A database can have more than one root, but each root must have a unique name. If you try calling create_root() with a name you previously used in the same database, err_root_exists is signaled.

Getting a list of roots

You can get a list of all roots in the this database by calling

void os_database::get_all_roots( os_int32 max_to_return, os_database_root_p* roots, os_int32& n_return );

The roots are returned in roots, which is an array of pointers to os_database_root objects. You must allocate (and delete) this array. Use os_database::get_n_roots() to determine the size of the array to allocate. max_to_return is the number of elements you have allocated for the array. n_return is the number of pointers returned in the array. If the array is not large enough to contain all the pointers, n_return equals max_to_return.

For reference information about the get_n_roots() method, see os_database::get_n_roots() in the C++ A P I Reference.

Getting the name of a root

After you get the list of pointers to roots, you can use

char* os_database_root::get_name();

to get the name of each root in the list. The return value is a newly allocated string that must be deleted to prevent a memory leak.

For reference information about the get_name() method, see os_database_root::get_name() in the C++ A P I Reference.

Step 2. Associating the Root with an Entry-Point Object After you have created a database root, you must associate it with the entry-point object by calling

void os_database_root::set_value( void* entry_point_object, os_typespec* typespec = 0 );

entry_point_object is a pointer to the object that serves as the entry point into your database. For example, entry_point_object could be a pointer to the head of a linked list or the first element in an array. The object must have been allocated previously with persistent new for storage in your database; see Allocating and Releasing Persistent Storage on page 34. If entry_point_object points to transient memory or memory in another database, the exception err_invalid_root_value is signaled.

70 C++ A P I User Guide

Page 71: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

typespec is a defaulted argument. If this argument is nonzero, it is interpreted as a typespec — that is, a pointer to an os_typespec object — and is persistently stored in your database. typespec represents the type of the entry-point object and is used by ObjectStore to check that it matches the typespec you supply as an argument to os_database_root::get_value() in Step 3. Retrieving the Entry-Point Object on page 72.

If you specify 0 for the typespec argument, or do not specify it, ObjectStore does not check for matching typespecs. If you make multiple calls to set_value() to associate the same entry-point object with the same root, the typespec for the last call is the one ObjectStore stores. For additional information about typespecs, see Using Typespecs on page 37.

Example The sample program does the following:

• Creates the database parts (line 6).

• Retrieves the typespec for Part — the class of the object to be stored in the database (line 8).

• Allocates persistent storage for the object a_part, constructs the object, and stores it in the database (line 9).

• Creates a root (a_root) named "db_root" (line 10).

• Associates the root with the object (line 11). The typespec argument (part_ts) is stored in the database so that, later, it can be compared with the typespec argument supplied with the call to get_value(). For the companion program that calls get_value(), see Step 3. Retrieving the Entry-Point Object on page 72.

• Closes the database (line 13).

Following is the program:

01 #include "part.h"

02 int main(void)03 {04 OS_ESTABLISH_FAULT_HANDLER { 05 objectstore::initialize(); 06 os_database *db = os_database::create("parts");07 OS_BEGIN_TXN(txn, 0, os_transaction::update) {08 os_typespec* part_ts = Part::get_os_typespec();09 Part *a_part = new(db, part_ts) Part("wrench", 111);10 os_database_root *a_root = db->create_root("db_root");11 a_root->set_value(a_part, part_ts);12 } OS_END_TXN(txn)13 db->close();14 } OS_END_FAULT_HANDLER15 return 0;16 }

For information about the method get_os_typespec() that is called in line 8 to retrieve the typespec for the user-defined class Part, see Typespecs for Classes on page 39. The example declaration of Part that is listed there is used by this program.

Available online This example program is available online in

Release 6.3 71

Page 72: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Database Roots and Entry-Point Objects

• $OS_ROOTDIR/examples/doc_demos/ug4/set_part.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug4\set_part.cpp (Windows)

Step 3. Retrieving the Entry-Point Object After a root is associated with an entry-point object, the current process or any subsequent process can retrieve the object by calling

void* os_database_root::get_value(os_typespec* typespec = 0);

get_value() returns a pointer to the entry-point object. This is the same object you associated with the root in Step 2. Associating the Root with an Entry-Point Object on page 70. get_value() returns a void* pointer, so the return value requires a cast. The following statement retrieves the entry-point object that is associated with a_root and assigns the pointer by get_value() to a_part:

Part* a_part = (Part*)a_root->get_value(part_type);

Typespec argument

get_value() takes a defaulted argument, typespec. If a nonzero value is specified for typespec, it is interpreted as a typespec and is matched against the typespec specified in the call to os_database_root::set_value(); see Step 2. Associating the Root with an Entry-Point Object on page 70. If the two typespecs do not match, ObjectStore signals an err_typespec_mismatch exception. For additional information about typespecs, see Using Typespecs on page 37.

get_value() verifies only that the typespec supplied to it matches the stored typespec; it does not check the type of the entry-point object itself.

Getting the root If you do not already have the database root, before you can retrieve the associated entry-point object, you must retrieve the root by calling

os_database_root* os_database::find_root(const char* name);

find_root() uses name to look up the root in the database (name is a string that is assigned as the root’s name in Step 1. Creating a Database Root on page 69). find_root() returns a pointer to the root; if there is no root named name in the this database, find_root() returns 0. You should check the return value before using it to retrieve the entry-point object.

You can also get the root by calling

static os_database_root* root = os_database_root::find( const char* name, os_database* db);

os_database::find_root() and os_database_root::find() differ only in that find() takes a pointer to the database as its second argument. (find() is a static member function and therefore requires no this argument.) Both methods perform the same function.

For reference information about find_root() and find(), see the following in the C++ A P I Reference:

• os_database::find_root()

• os_database_root::find()

72 C++ A P I User Guide

Page 73: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

Example The example program illustrates how to use both os_database::find_root() and os_database_root::get_value() to retrieve the entry-point object in the database named parts. (This parts database was created with the program listed in Step 2. Associating the Root with an Entry-Point Object on page 70.)

The program does the following:

• Opens the database parts (line 6).

• Finds the root named "db_root" (line 8).

• Retrieves the Part object associated with the root and checks that the typespec specified in the call to get_value() (line 9) matches the stored typespec. The stored typespec was supplied by the call to set_value() in the program listed in Step 2. Associating the Root with an Entry-Point Object on page 70; see line 11.

• Closes the database (line 12).

Following is the program:

01 #include "part.h"

02 int main(void)03 {04 OS_ESTABLISH_FAULT_HANDLER { 05 objectstore::initialize(); 06 os_database *db = os_database::open("parts");07 OS_BEGIN_TXN(txn, 0, os_transaction::read_only) {08 os_database_root *a_root = db->find_root("db_root");09 Part* a_part = (Part*)(a_root->get_value(Part::get_os_typespec()));10 cout << a_part->descrip << " (" << a_part->id << ")\n";11 } OS_END_TXN(txn)12 db->close();13 } OS_END_FAULT_HANDLER14 return 0;15 }

After retrieving the entry-point object, you can navigate through the database and access other objects stored there.

For information about the method get_os_typespec() that is called in line 9 to retrieve the typespec for the user-defined class Part, see Typespecs for Classes on page 39. The example declaration of Part that is listed there is used by this program.

Available online This example program is available online in

• $OS_ROOTDIR/examples/doc_demos/ug4/get_part.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug4\get_part.cpp (Windows)

Destroying the Database Root To give an object a different name or to stop using it as an entry point, you can destroy its associated root by calling

void os_database_root::destroy();

Release 6.3 73

Page 74: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Working with Rawfs Databases

This function destroys both the persistent database root and the persistent name but not the associated entry-point object. For reference information about the destroy() method, see os_database_root::destroy() in the C++ A P I Reference.

Frequently, the only way to retrieve an entry-point object is through its associated root. Therefore, be careful to retrieve such an object before destroying its root. Note, too, that an entry-point object can have more than one root associated with it.

The following code destroys the root stored in the database parts:

os_database_root *a_root = db->find_root("db_root"); a_root->destroy();

Note destroy() deletes the persistent root, not the transient os_database_root object. To delete the transient object, use the delete operator. For more information about deleting transient ObjectStore objects, see Managing Lifetimes of ObjectStore Objects on page 48.

Working with Rawfs Databases In addition to file databases, which are regular operating system files contained in operating system directory hierarchies, ObjectStore supports rawfs databases, which are contained in ObjectStore directory hierarchies and managed by ObjectStore servers.

For more information about rawfs databases, see Managing the Rawfs in Chapter 1 of Managing ObjectStore.

Rawfs Host PrefixPathname arguments in the os_database functions described in preceding sections (create(), open(), and lookup()) can designate rawfs databases by including a rawfs host prefix of the form host-name:: (for example, beech::/foo/bar). In the prefix, host-name names the machine running the ObjectStore server managing the desired ObjectStore directory hierarchy.

The following example specifies that a newly created database named /ken/new/parts is to be stored in the hierarchy managed by the ObjectStore server running on the host named beech:

os_database *db1 = os_database::create("beech::/ken/new/parts");

Creating ObjectStore DirectoriesThe directory portion of the pathname argument that designates a rawfs database must be the name of an ObjectStore directory. Unlike databases, which must be created from within a program, ObjectStore directories can be created either from within a program (with the function os_dbutil::mkdir()) or with the ObjectStore utility osmkdir. For more information, see osmkdir in Managing ObjectStore.

74 C++ A P I User Guide

Page 75: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 4: Basic Database Operations

Pathnames of ObjectStore directories and databases use forward slashes (/ ) regardless of the operating system being used, and they always begin with a forward slash.

Finding a Rawfs PathnameIf a database is in a rawfs, you can call os_database::get_pathname() to retrieve its pathname. The returned pathname will include the rawfs host prefix. For more information, see os_database::get_pathname() in the C++ A P I Reference.

AliasesYou can use alternative names (aliases) when specifying hosts, but when you use the utility osls to determine a database’s host machine, the host is identified with its complete, or canonical, name. For more information, see osls in Managing ObjectStore.

Release 6.3 75

Page 76: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Working with Rawfs Databases

76 C++ A P I User Guide

Page 77: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 5Transactions

A transaction refers to the execution of a sequence of statements that operate on persistent data as a logical unit; that is, the operations performed by the statements individually are performed all together or not at all. This chapter describes the basic properties and types of transactions and explains how to write transactions by using the ObjectStore API.

The following topics are discussed:

Overview of Transactions 75

Using Lexical Transactions 77

Using Dynamic Transactions 81

Locking 83

Rolling Back to Persistent State 84

Local and Global Transactions 87

Two-Phase Commit 88

Note For information about more advanced topics concerning transactions, see Chapter 2, Advanced Transactions, of the Advanced C++ A P I User Guide.

Overview of Transactions Before a program can access persistent data, it must start a transaction. A transaction is the portion of a program whose changes to persistent data are atomic; that is, either all the changes are recorded or none of them. You mark the beginning and end of transaction by using either macros or function calls that are provided by the ObjectStore API.

Access to persistent data must always take place within a transaction. If you attempt to access persistent data outside a transaction, err_no_trans is signaled. However, statements that create, destroy, open, or close a database can occur inside or outside a transaction; see Chapter 4, Basic Database Operations, on page 55.

While a transaction is in progress, the program’s actions can include reads and writes to persistent objects. The program can either commit or abort the transaction at any time.

Release 6.3 75

Page 78: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Overview of Transactions

Lexical and Dynamic Transactions ObjectStore supports two types of transactions:

• Lexical transactions. A lexical transaction requires that the transaction be contained in a lexical context that has a static scope. Lexical transactions are simple to use and restart automatically in the event of a deadlock. Note that lexical transactions are also referred to as stack transactions because the scope of the beginning and end of the transaction must occur within the same stack frame.

• Dynamic transactions. A dynamic transaction occurs within boundaries that can be dynamically defined, depending on run-time conditions. Dynamic transactions are more difficult to use but provide greater flexibility and control over the transaction. They often are the only choice for multithreaded applications.

Both types of transactions are described in Using Lexical Transactions on page 77 and Using Dynamic Transactions on page 81.

Transaction Commit and AbortTransactions can terminate successfully or unsuccessfully. When they terminate successfully, they commit. All changes to persistent memory are made permanent and visible to other processes. A transaction is not considered to have terminated successfully until all its changes are recorded safely on stable storage. After a transaction commits, failures such as server crashes or network failures cannot erase the transaction’s changes.

When transactions terminate unsuccessfully, they abort; all changes are undone and rolled back to the pretransaction state.

A transaction abort can take several forms:

• Explicit aborts, which result from calls to member functions of the class os_transaction, such as abort().

• Aborts that result from deadlock conditions. A simple deadlock occurs when transaction A holds a lock on a page that transaction B is waiting to access. At the same time, transaction B holds a lock on a page that transaction A is waiting to access. Neither process can proceed until the other does, resulting in a deadlock. Other forms of deadlock are analogous.

• Aborts that result from a nonlocal transfer of control out of the scope of a lexical transaction. This kind of abort can occur when an exception is signaled within a lexical transaction. Such an abort can be handled only if the lexical transaction is enclosed in a TIX exception handler and the handler block is outside the transaction. For information about exception handlers, see Establishing a TIX Exception Handler on page 92.

• Aborts that result from a system failure, such as a network failure.

Rolling Back a Transaction If the transaction in which changes were made is aborted, the changes are undone or rolled back to the pretransaction state. After an abort, your program sees persistent memory as it appeared just before the aborted transaction started.

76 C++ A P I User Guide

Page 79: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 5: Transactions

From the point of view of other processes, the ability to roll back a transaction to its pretransaction state means that the code within a transaction executes either all at once or not at all. That is, other processes do not see the intermediate results.

For information about aborting a transaction explicitly in order to perform a rollback, see Rolling Back to Persistent State on page 84.

Concurrency ControlAn important function of a transaction is its support for concurrent database access. This support prevents one process’s updates from interfering with another process’s reads or updates. Concurrency control prevents this interference by ensuring that transactions have the following properties:

• A transaction’s changes to persistent data are private and invisible to other processes until the entire transaction completes successfully.

• Other processes’ changes to persistent data are invisible to a transaction.

ObjectStore implements concurrency control by using strict two-phase locking, as described in Locking on page 83.

Marking Off a Transaction When marking off transaction boundaries in your code, you must balance two opposing considerations:

• If a transaction includes too much, it can negatively affect the performance of other concurrent applications.

• If a transaction includes too little, other concurrent applications can interfere with the application containing the transaction, causing the application to produce incorrect results. In addition, a large number of short transactions can increase overhead as much as one long transaction.

Dynamic transactions offer the most flexibility in setting up optimal boundaries for a transaction.

Using Lexical TransactionsTo demarcate a lexical transaction, use the following macros:

OS_BEGIN_TXN(identifier,exception**,transaction-type ) { } OS_END_TXN(identifier )

Note There must be no white space anywhere in the argument list. The enclosing braces are not required, but some source code editors might complain if you do not include them.

The arguments to the macros are described in the following paragraphs.

Arguments identifier is a transaction tag. The tag must be the same in both macros. Different transactions in the same function must use different tags. The tags are used to construct statement labels and therefore have the same scope as labels in C++.

Release 6.3 77

Page 80: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Using Lexical Transactions

exception** specifies a location in which ObjectStore stores 0 at the beginning of a transaction. If the transaction aborts, exception** points to the exception. To access the exception, you must declare a pointer of type tix_exception and supply its address as the argument. If you do not use the exception** argument, specify 0.

The following code example illustrates the exception** argument to the OS_BEGIN_TXN() macro:

tix_exception* abort_reason_exception; OS_BEGIN_TXN(txn1, &abort_reason_exception, os_transaction::update){

After the transaction has terminated, you can examine abort_reason_exception to find out whether the transaction aborted. If the pointer is nonzero after the transaction’s scope is exited, an exception occurred. See Explicit Aborts and Lexical Transactions on page 86 for more information.

Transaction types: update and read-only

transaction-type is one of the following enumerators:

• os_transaction::update specifies a transaction in which updates to persistent memory are allowed.

• os_transaction::read_only specifies a transaction in which any attempt to update persistent memory signals the exception err_no_query_trans.

For reference information about the lexical transaction macros, see the following in the C++ A P I Reference:

• OS_BEGIN_TXN()

• OS_END_TXN()

Deadlocks and Automatic Retries When an abort occurs in a lexical transaction because of a deadlock, the transaction is retried automatically until either it completes successfully or the maximum number of retries is exhausted. By default, the maximum retry count for any lexical transaction in a given process is 10. You can retrieve and change the current retry count; see objectstore::get_transaction_max_retries() or objectstore::set_transaction_max_retries() in the C++ A P I Reference. If you change the retry count, the new count is effective for the current session only.

Note If a deadlock occurs in a dynamic transaction, the transaction is not retried automatically and the deadlock must be handled by the programmer. For more information, see Using Dynamic Transactions on page 81.

78 C++ A P I User Guide

Page 81: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 5: Transactions

Example of a Lexical Transaction The following program uses a lexical transaction to write objects to a persistently stored database. The program prompts the user for a note and a priority number, then stores both in the database that the user has specified on the command line. Objects are stored in a linked list, the most recent object becoming the new head of the list, that is, the entry-point object.

Following is the program:

// main.cc: Note program - main file // Adds a note and a priority number to specified database #include "note.hh"

note* head; // head of linked-list of notes

main(int argc, char** argv) { char buff[BUFSIZE], buff2[BUFSIZE]; int note_priority;

OS_ESTABLISH_FAULT_HANDLER { if(argc!=2) { // check for name of database cout << "Usage: " << argv[0] << " <database>" << endl; return 1; }

objectstore::initialize(); // open database; create one if it doesn't exist os_database *db = os_database::open(argv[1], 0, 0644);

OS_BEGIN_TXN(t1,0,os_transaction::update) { os_database_root *root_head = db->find_root("head"); if (!root_head) // if there’s no root, create one root_head = db->create_root("head"); head = (note *)root_head->get_value(note::get_os_typespec()); cout << "Enter a note: " << flush; // Prompt for new note cin.getline(buff, sizeof(buff));

// Prompt for priority cout << "Enter note priority: " << flush;

cin.getline(buff2, sizeof(buff2)); note_priority = atoi(buff2);

Release 6.3 79

Page 82: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Using Lexical Transactions

// allocate note object and make it the new entry point head = new(db, note::get_os_typespec()) note(buff, head, note_priority); root_head->set_value(head, note::get_os_typespec());

} OS_END_TXN(t1) db->close(); // close database } OS_END_FAULT_HANDLER return 0; }

Available online This example program is available online in

• $OS_ROOTDIR/examples/doc_demos/ug5/main.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug5\main.cpp (Windows)

Nesting Lexical Transactions: an Example You can nest lexical transactions by including one set of transaction macros inside another, as follows:

OS_BEGIN_TXN(arg-list) { // outer transaction OS_BEGIN_TXN(arg-list) { // inner transaction // transaction block } OS_END_TXN(arg) // end of inner transaction } OS_END_TXN(arg) // end of outer transaction

One reason to nest transactions is to allow temporary writes to persistent memory that you want to discard at the end of the transaction. This type of nested transaction is an abort-only transaction. To set up an abort-only transaction, nest an update transaction inside a read-only transaction.

The following example of an abort-only transaction replaces the transaction portion of the code in the program listed in Example of a Lexical Transaction on page 79. The statements in the inner transaction are the same as those in the original program, except for the addition of os_transaction::abort(), which rolls back the inner transaction. (See os_transaction::abort() in the C++ A P I Reference.) The outer transaction provides a read-only context for the inner transaction that avoids having to wait for a write lock.

// a nested, abort-only transactionOS_BEGIN_TXN(txn1,0,os_transaction::read_only) { OS_BEGIN_TXN(txn2,0,os_transaction::update) { os_database_root *root_head = db->find_root("head"); if(!root_head) root_head = db->create_root("head"); head = (note *)root_head->get_value(note::get_os_typespec());

cout << "Enter a new note: " << flush; cin.getline(buff, sizeof(buff));

cout << "Enter a note priority: " << flush; cin.getline(buff2, sizeof(buff2)); note_priority = atoi(buff2);

head = new(db, note::get_os_typespec()) note(buff, head, note_priority);

80 C++ A P I User Guide

Page 83: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 5: Transactions

root_head->set_value(head, note::get_os_typespec()); // roll back the transaction os_transaction::abort(); } OS_END_TXN(txn2) } OS_END_TXN(txn1)

Available online This example program is available online in

• $OS_ROOTDIR/examples/doc_demos/ug5/txn_nest.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug5\txn_nest.cpp (Windows)

Using Dynamic Transactions You demarcate a dynamic transaction by using two methods:

static os_transaction* os_transaction::begin( os_int32 transaction_type = os_transaction::update);

static void os_transaction::commit( os_transaction* txn = os_transaction::get_current());

The statements executed between begin() and commit() are all considered within the same transaction. The following sections describe how to use these functions.

For reference information about begin() and commit(), see the following in the C++ A P I Reference:

• os_transaction::begin()

• os_transaction::commit()

Beginning a Dynamic Transaction begin() returns a pointer to an os_transaction object that represents the transaction of the current session. You use this object as the implicit this argument when calling other member functions of the class os_transaction.

begin() takes one argument, transaction_type, which specifies the transaction type and can be one of the following enumerators:

• os_transaction::update specifies a transaction in which you can both read and update persistent memory.

• os_transaction::read_only specifies a transaction in which any attempt to update persistent memory signals the exception err_no_query_trans.

It is the user’s responsibility to delete the os_transaction object after the transaction has terminated.

Committing a Dynamic Transaction Call os_transaction::commit() to commit a transaction. If you do not specify the txn argument, it defaults to the current transaction. If the call to commit() occurs in

Release 6.3 81

Page 84: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Using Dynamic Transactions

a nested transaction, the current transaction is the innermost transaction in which the call occurs. To commit an outer transaction, specify the txn argument.

Dynamic vs. Lexical Transactions The following are the advantages and disadvantages of the two types of transactions:

• Dynamic transactions are not retried automatically in the event of a deadlock; see Deadlocks and Automatic Retries on page 78. Instead, you must provide your own checking and handling of deadlocks.

• In both lexical and dynamic transactions, an explicit abort (for example, a call to os_transaction::abort()) aborts the current transaction and returns control to the statement immediately following the transaction block. In a dynamic transaction, processing continues with the statement following the abort(). In a lexical transaction, processing resumes with the statement following the transaction — that is, the statement after the OS_END_TXN() macro. A dynamic transaction requires the user to handle the explicit abort.

• Calling os_transaction::begin() to start a dynamic transaction allocates an os_transaction object that can cause a memory leak if it is not explicitly deleted.

• Dynamic transactions are better suited for multithreaded applications, especially those that start a transaction in one thread and end it in another thread.

Example of a Dynamic Transaction The following example of a dynamic transaction replaces the lexical transaction portion of the code in the program listed in Example of a Lexical Transaction on page 79. Because the boundaries of a dynamic transaction are defined at run time, this version of the program can decide whether to continue with the transaction, commit it, or abort and discard all changes to persistent memory; see the if-else statements near the end of the transaction.

// start of transactionos_transaction::begin(os_transaction::update);

os_database_root *root_head = db->find_root("head");if(!root_head) // if there's no root, create one root_head = db->create_root("head");head = (note *)root_head->get_value(note::get_os_typespec());

loop: cout << "Enter a new note: " << flush; // Prompt for new note cin.getline(buff, sizeof(buff));

//Prompt for priority cout << "Enter a note priority: " << flush; cin.getline(buff2, sizeof(buff2)); note_priority = atoi(buff2);

// allocate note object and make it the new entry point head = new(db, note::get_os_typespec()) note(buff, head, note_priority); root_head->set_value(head, note::get_os_typespec());

82 C++ A P I User Guide

Page 85: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 5: Transactions

cout << "Do you want to commit, write another note, or abort"; cout << " (c/w/a)? " << flush; cin.getline(buff, sizeof(buff)); if (buff[0] == 'c') os_transaction::commit(); // commit transaction else if (buff[0] == 'a') os_transaction::abort(); // abort transaction else goto loop; // get another note // end of transaction

Available online This example program is available online in

• $OS_ROOTDIR/examples/doc_demos/ug5/dyn_txn.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug5\dyn_txn.cpp (Windows)

LockingLike most database systems, ObjectStore tries to interleave the operations of different processes’ transactions to maximize concurrent usage of resources. When scheduling the operations, ObjectStore conforms to a strict two-phase locking discipline (except in the case of multiversion concurrency control; see Multiversion Concurrency Control (MVCC) in Chapter 2 of the Advanced C++ A P I User Guide). This discipline has been proven correct in that it guarantees serializability; that is, it guarantees that the results of the scheduling are the same as the results of some noninterleaved schedule of the transactions’ operations.

Waiting for locks Generally speaking, when you access data in the database, you are given exclusive access to that data for the duration of the transaction in which the access takes place; that is, when you access data, that data is locked. The data is not unlocked until the end of the transaction.

Read locks and write locks

Locking differs depending on whether you are reading or writing data. When a session reads persistent data, the page on which the data resides is read locked; other sessions cannot write to that page. They can read it, but writing is delayed until the outermost transaction of the locking session completes and the lock is released. When a session writes to persistent data, the page on which the data resides is write locked; other sessions are prevented from reading or writing to that page.

Aborts and locks

If an abort occurs during a transaction, the lock is released. If the transaction is nested, all locks acquired during the nested transaction are released, all changes in the nested transaction are rolled back, and the database is restored to its state before the innermost nested transaction that was aborted.

Lock timeouts You can set a timeout for read- and write-lock attempts by calling objectstore::set_lock_timeout() to limit the amount of time your application waits to acquire a lock. To retrieve the current timeout value, call objectstore::get_lock_timeout(). When the timeout is exceeded, an exception is signaled. Handling the exception allows you to continue with alternative processing and to make a later attempt to acquire the lock.

Release 6.3 83

Page 86: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Rolling Back to Persistent State

For information about setting and retrieving timeout values, see objectstore::set_lock_timeout() and objectstore::get_lock_timeout() in the C++ A P I Reference.

Lock probes To determine whether a specified address is read locked, write locked, or unlocked, see objectstore::get_lock_status() in the C++ A P I Reference.

Explicit lock acquisition

Normally, ObjectStore performs locking automatically and transparently to the user. You can, however, explicitly lock a specified page range for reading or writing; see objectstore::acquire_lock() in the C++ A P I Reference.

Rolling Back to Persistent StateYou can force a rollback by calling one of the following:

static os_transaction::abort( os_transaction* txn = os_transaction::get_current());

static void os_transaction::abort_top_level();

Calling either of these functions causes an explicit abort. The following sections describe how to use these functions to cause an explicit abort and how to check for an explicit abort in a lexical transaction.

For reference information about abort() and abort_top_level(), see the following in the C++ A P I Reference:

• os_transaction::abort()

• os_transaction::abort_top_level()

Aborting the Current TransactionTo force a rollback to the persistent memory state at the beginning of the current transaction, call os_transaction::abort(). This function is static and therefore requires no implicit this argument. If the explicit abort occurs in a nested transaction, the current transaction is the most deeply nested transaction within which the call to abort() occurs.

When an explicit abort occurs in a dynamic transaction, program control passes to the statement immediately following the abort(). In a lexical transaction, program control passes to the statement immediately following the current transaction block. In either case, changes to persistent data are rolled back to the state as of the beginning of the transaction. All locks acquired during the transaction are released, including when the explicit abort occurs in a nested transaction.

For example programs that call os_transaction::abort() to cause an explicit abort, see the following sections:

• Example of a Dynamic Transaction on page 82

• Nesting Lexical Transactions: an Example on page 80

84 C++ A P I User Guide

Page 87: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 5: Transactions

Aborting the Top-Level TransactionWhen you call os_transaction::abort() with no arguments, only the innermost transaction is aborted. You can, however, abort the outermost transaction by calling os_transaction::abort_top_level() with no arguments. This function, like os_transaction::abort(), is static and therefore requires no implicit this argument.

Aborting a Specified TransactionYou also can call os_transaction::abort() with an argument that specifies the transaction you want to abort. The argument is a pointer to a transaction, an instance of os_transaction. To get a pointer to the current transaction (the innermost transaction in which control currently resides), call os_transaction::get_current(), which returns the pointer. To get a pointer to the transaction in which the transaction specified by the this argument is directly nested, call os_transaction::get_parent(), which returns the pointer.

For reference information about get_current() and get_parent(), see the following in the C++ A P I Reference:

• os_transaction::get_current()

• os_transaction::get_parent()

The following example aborts a transaction one level up from the current transaction:

os_transaction *child_txn, *parent_txn; if (child_txn = os_transaction::get_current()) if (parent_txn = child_txn->get_parent()) os_transaction::abort(parent_txn);

Release 6.3 85

Page 88: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Rolling Back to Persistent State

Explicit Aborts and Lexical Transactions When a lexical transaction is aborted because of a call to os_transaction::abort(), control passes to the first statement outside the aborted transaction, that is, the statement just after the OS_END_TXN() macro. At this point, your application can determine whether the transaction was aborted explicitly by examining the second argument to the OS_BEGIN_TXN() macro. To access this argument, supply the address of a tix_exception pointer variable as the argument. If the transaction is aborted for any reason, ObjectStore sets this pointer to the exception that caused the abort.

In general, an exception that aborts a transaction also aborts the program. To examine the pointer argument to the OS_BEGIN_TXN() macro, you must enclose the entire transaction in a TIX exception handler; see Establishing a TIX Exception Handler on page 92.

However, an explicit-abort exception, err_explicit_abort, aborts only the transaction, not the program. This means that you can handle an explicit abort without establishing a TIX exception handler. To determine whether an explicit abort did occur, check the tix_exception pointer argument for nonzero. If it is 0, an exception did not occur. If it is nonzero, compare the pointer to the global address of err_explicit_abort and handle it accordingly.

The following code segment illustrates how to check for an explicit abort in a lexical transaction:

tix_exception* my_exception; OS_BEGIN_TXN(txn1,&my_exception,os_transaction::update) { // transaction block } OS_END_TXN(txn1) if (my_exception && (my_exception == &err_explicit_abort)) cout << "Transaction was explicitly aborted" << endl;

For reference information about tix_exception, see tix_exception in the C++ A P I Reference.

86 C++ A P I User Guide

Page 89: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 5: Transactions

Local and Global Transactions For applications that use multiple threads in a single session, transactions can be either global or local. Lexical transactions — that is, transactions that begin with the OS_BEGIN_TXN() macro — are always local. Dynamic transactions — that is, transactions that begin with a call to os_transaction::begin() — are local by default, but you can make them global by specifying the os_transaction::global enumerator in the call to os_transaction::begin().

The following example starts a global transaction in update mode:

os_transaction::begin(os_transaction::update, os_transaction::global);

A thread enters a local transaction by calling os_transaction::begin() or OS_BEGIN_TXN(). A thread enters a global transaction when it calls os_transaction::begin() and specifies os_transaction::global, or when another thread of the same session calls os_transaction::begin() and specifies os_transaction::global. When one thread enters a global transaction, all other threads in the same session automatically enter the same transaction.

Two threads cannot be in a local transaction at the same time. Local transactions synchronize access to the ObjectStore run time by serializing the transactions of the different threads — that is, by making the transactions run one after another without overlapping. After one thread starts a local transaction, if another thread attempts to start a transaction or enter the ObjectStore run time, it is blocked by a mutex lock until the local transaction completes.

Note Transactions occurring in different sessions are completely independent of each other.

Global transactions allow for a somewhat higher degree of concurrency. After one thread enters the ObjectStore run time, if another thread attempts to enter the ObjectStore run time, it is blocked until control in the first thread exits from the run time.

To determine whether a transaction is local or global, call os_transaction::get_scope(), as described in os_transaction::get_scope() in the C++ A P I Reference. For information about os_transaction::begin(), see os_transaction::begin() in the C++ A P I Reference. For information about using threads with ObjectStore, see Chapter 3, Multithread and Multisession Applications, in the Advanced C++ A P I User Guide.

Release 6.3 87

Page 90: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Two-Phase Commit

Two-Phase Commit Two-phase commit is the process by which transactions that use multiple ObjectStore servers commit in a consistent manner. The majority of transactions do not use multiple servers and are unaffected by two-phase commit.

Transactions that write-access data on multiple servers commit in two phases: a voting phase and a decision phase. First, all servers vote on a transaction, indicating whether they are able to commit it. Servers always vote yes unless there is a reason that makes committing impossible, such as running out of disk space. If all the votes are yes, the decision is made to commit and all servers are told to commit.

Before voting yes, a server stores all information it needs to commit the transaction into the transaction log. A crash after this point will not prevent the transaction from committing.

Problems can still occur before the transaction completes. For example, a server might crash in the middle of the commit process, after it has voted but before it receives a decision. Or a server could crash or be restarted between the vote and decision phases. In both cases, the server must communicate with another server to determine the outcome of the transaction, and the coordination control is passed to one of the servers. (During normal processing, the client process is the coordinator of the transaction.)

When a server is restarted after a multiserver transaction was in the process of committing when the crash occurred (for example, after a power failure), it prints a message such as the following, either into the log file or to standard output:

020716 095939.062 LOG 0008 There are 1 multi-server transactions sending outcome notification. 020716 095939.062 LOG 0009 There are 2 multi-server transactions awaiting outcome notification. 020716 095940.062 Server started

The message indicates that during the restart process, the server noticed that a multiserver transaction was in the process of committing when the server crashed. The first line specifies the number of incomplete transactions for which this server is the coordinator — in the example, 1. The second line specifies the total number of incomplete transactions on this server — in the example, 2.

The pages written in this transaction are unavailable during the commit phases and the restart processing. However, during two-phase commit restart processing, it is possible for other requests to the servers to be processed after they are started up. If one of those requests is for a page that is being recovered as part of a multiserver transaction that was committing, the client program must wait for the transaction to complete and release the lock. After the recovery processing is complete for the corresponding two-phase commit, the page becomes free to use again.

If a transaction remains in the decision phase for more than 15 minutes after the server is restarted, the phase times out and the transaction is aborted.

88 C++ A P I User Guide

Page 91: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 6Exception Handling

ObjectStore does not support the use of C++ exception handling for error conditions that occur during an ObjectStore operation. Instead, ObjectStore provides the TIX exception-handling facility, which includes an error-reporting mechanism and services for handling ObjectStore exceptions.

This chapter discusses the following topics:

TIX Exceptions 89

Establishing a TIX Exception Handler 92

Using the TIX Classes 97

Note For detailed descriptions of all components of the TIX exception-handling facility, see the following chapters in the C++ A P I Reference:

• Chapter 2, Class Library

• Chapter 4, System-Supplied Macros

• Chapter 5, Predefined TIX Exceptions

TIX Exceptions TIX exceptions are part of the API that ObjectStore uses to report and handle error conditions during an ObjectStore operation. These exceptions include system-supplied predefined exceptions, as well as any defined by the user. The following sections describe both predefined and user-defined exceptions.

Predefined Exceptions The predefined TIX exceptions are instances of the class tix_exception. These exceptions are used to signal illegal ObjectStore operations, such as attempting to write to persistent memory during a read-only transaction. The predefined exceptions are derived from objectstore_exception and have access to its member functions, including the signal() method, which is described in Signaling Exceptions on page 97. For a descriptive list of the predefined TIX exceptions, see Chapter 5, Predefined TIX Exceptions, of the C++ A P I Reference.

The predefined exceptions are organized into groups. Each group has a parent exception; the exceptions within the group are referred to as child exceptions. Parent exceptions can be used where you would use child exceptions, but they are most

Release 6.3 89

Page 92: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

TIX Exceptions

useful when specified in a TIX exception handler to catch a group of child exceptions; see Handling Multiple Exceptions on page 93.

Parent exceptions are organized hierarchically, as illustrated in the following diagram. Note the following relationships among the parent exceptions:

• Every TIX exception is a descendent of all_exceptions.

• Every TIX exception signaled by ObjectStore is itself a child of err_objectstore, which is a child of all_exceptions.

• Every TIX exception signaled from the remote procedure call (RPC) mechanism, which ObjectStore uses for all its network communications, is a child of err_rpc, which is a child of err_objectstore.

all_exceptions

err_objectstore

err_rpc

General exceptions err_coll

err_mop

err_schema_evolution

err_osbr

err_allocator_framework

90 C++ A P I User Guide

Page 93: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 6: Exception Handling

User-Defined Exceptions TIX exceptions that you define are instances of the class objectstore_exception, which provides functions for signaling the exception; see Signaling Exceptions on page 97. ObjectStore provides two macros for defining your own TIX exceptions:

DEFINE_EXCEPTION(name, "message", pointer-to-parent ); DECLARE_EXCEPTION(name );

The DEFINE_EXCEPTION() macro performs the definition, and the DECLARE_EXCEPTION() macro makes the definition available to other program files. Both macros must end in a semicolon (;).

Arguments name is the identifier for the exception you want to create.

message is the error message to display when the exception is signaled. As shown in the syntax statement, this argument must be enclosed in quotation marks.

pointer-to-parent is the address of a parent exception, which thus becomes the new exception’s parent. For information about parent exceptions, see Predefined Exceptions on page 89. Supply 0 for this argument if you do not want your exception to have a parent.

Defining and declaring

If you use the exception in only one file, you can dispense with the DECLARE_EXCEPTION() macro altogether and just include the DEFINE_EXCEPTION() macro in the program file that uses the exception. Otherwise, include DECLARE_EXCEPTION() in the definitions file and DEFINE_EXCEPTION() in a header file that is included by program files that want to use the exception.

Signaling Predefined TIX exceptions are signaled internally by ObjectStore. How do you signal a user-defined exception? It is triggered explicitly by the user application when it detects the condition for which the exception was defined. User-defined (as well as predefined) exceptions are instances of the class objectstore_exception, which gives them access to the function objectstore_exception::signal(). When you call this function, specify the exception identifier as the implicit this argument; the effect is to signal the exception.

For reference information about signal(), see objectstore_exception::signal() in the C++ A P I Reference.

Example The example program discussed in Chapter 2, Anatomy of an ObjectStore Application, on page 21, shows defining, declaring, and using a user-defined TIX exception. Following is a summary of the process.

The exception is defined in the definitions file, bookclub.cc:

DEFINE_EXCEPTION(err_no_entry_point, "No entry-point object for", 0);

To make the exception err_no_entry_point available to other parts of the program, it is declared in the header file, bookclub.hh, line 10:

DECLARE_EXCEPTION(err_no_entry_point);

One of the program files that includes bookclub.hh is the main application file, main.cc, which also uses the exception. In lines 38–39, an if statement verifies that

Release 6.3 91

Page 94: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Establishing a TIX Exception Handler

the return value from get_value() was nonzero, indicating that the entry-point object was retrieved; see Retrieving the Entry-Point Object on page 30. If get_value() returns 0 (no entry-point object was retrieved), the program calls signal(), as follows:

err_no_entry_point.signal( "%s - use os_database_root::set_value()", argv[1]);

This call to signal() triggers the exception. signal() also performs printf-like formatting for displaying additional information about the error condition; see Signaling Exceptions on page 97.

In this example, the exception is not handled and the program aborts. However, you can handle user-defined as well as predefined exceptions in a TIX exception handler, as described in Establishing a TIX Exception Handler on page 92.

Establishing a TIX Exception Handler ObjectStore provides four macros for establishing TIX exception handlers:

TIX_HANDLE (exception ) TIX_HANDLE_IF (flag, exception ) TIX_EXCEPTION TIX_END_HANDLE

exception is the name of the exception you want to handle. It can be one of the following:

• Predefined exception; see Predefined Exceptions on page 89.

• User-defined exception; see User-Defined Exceptions on page 91.

• Parent exception; see Predefined Exceptions on page 89.

The only difference between TIX_HANDLE() and TIX_HANDLE_IF() is that the latter establishes the handler only if flag evaluates to nonzero (true); otherwise, both macros behave the same way. Therefore, in the discussion that follows, only TIX_HANDLE() is mentioned.

An exception handler generally requires two statement blocks: the block in which the exception can occur and the block that actually handles the exception. When establishing a TIX exception handler, use TIX_HANDLE() and TIX_END_HANDLE to enclose both blocks and TIX_EXCEPTION to separate them:

TIX_HANDLE(exception ) { // exception block } TIX_EXCEPTION { // handler block } TIX_END_HANDLE

The braces enclosing the statement blocks are not required, but some source editors might complain if you do not use them.

If the exception is signaled during execution of the statements in the exception block, control passes from the statement that caused the exception to the first statement in

92 C++ A P I User Guide

Page 95: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 6: Exception Handling

the handler block. Otherwise, control passes from the last statement in the exception block to the first statement following TIX_END_HANDLE. For a simple example, see the exception handler used in main.cc, lines 20–28, listed in An Example Application on page 21.

If the specified exception is signaled but there is no handler block, control passes directly to the first statement following the TIX_END_HANDLE macro, as if the exception were handled. See Handling Exceptions Outside a Handler on page 95.

For reference information about the TIX exception handler macros, see the following in the C++ A P I Reference:

• TIX_HANDLE()

• TIX_HANDLE_IF()

• TIX_EXCEPTION

• TIX_END_HANDLE

Handling Multiple Exceptions You can nest TIX exception handlers to handle more than one exception in the same statement block. The structure of a nested exception handler looks like the following:

TIX_HANDLE(exception1) { TIX_HANDLE(exception2) { . . . TIX_HANDLE(exceptionN) { // exception block } TIX_EXCEPTION { // handler block for exceptionN } TIX_END_HANDLE } TIX_EXCEPTION { // handler block for exception2 } TIX_END_HANDLE } TIX_EXCEPTION { // handler block for exception1 } TIX_END_HANDLE

You can also handle an entire group of exceptions. As explained in Predefined Exceptions on page 89, the TIX exceptions are organized in groups by the parent exception. To handle a family of exceptions, specify the name of the parent in the TIX_HANDLE() macro, as follows:

TIX_HANDLE(parent-exception) { // exception block } TIX_EXCEPTION { // handler block for all child exceptions under parent-exception } TIX_END_HANDLE

You might want to handle some child exceptions specifically and others generically. To do so, create a nested exception handler and specify the parent exception in the outer nest and each of the child exceptions you want to handle in the inner nests, as follows:

TIX_HANDLE(parent-exception ) { TIX_HANDLE(child-exception1 ) { TIX_HANDLE(child-exception2 ) {

Release 6.3 93

Page 96: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Establishing a TIX Exception Handler

. . . TIX_HANDLE(child-exceptionN ) { // exception block } TIX_EXCEPTION { // handler block for child-exceptionN } END_TIX_HANDLE } TIX_EXCEPTION { // handler block for child-exception2 } TIX_END_HANDLE } TIX_EXCEPTION { // handler block for child-exception1 } TIX_END_HANDLE } TIX_EXCEPTION { // handler block for all other children of parent-exception } TIX_END_HANDLE

Example The following example illustrates a nested TIX exception handler that handles two child exceptions (err_database_not_found and err_invalid_pathname) and the parent exception (err_objectstore). Both exceptions are children of the parent exception. The program expects the user to supply the name of the database file, stuff.db, on the command line. The program signals and handles an exception if the user supplies the wrong name or no name.

#include <ostore/ostore.hh> #include <fstream.h>

main(int argc, char **argv) { objectstore::initialize(); TIX_HANDLE (err_objectstore) { // parent exception // the next two exceptions are descended from err_objectstore TIX_HANDLE (err_database_not_found) { // bad name TIX_HANDLE (err_invalid_pathname) { // no argument (null) os_database* db = os_database::open(argv[1]); db->close(); cout << "The database named " << argv[1] << " exists.\n"; } TIX_EXCEPTION { cout << "Usage: " << argv[0] << " <database name>\n"; } TIX_END_HANDLE } TIX_EXCEPTION { cout << "Sorry, no such database. Try again.\n"; } TIX_END_HANDLE } TIX_EXCEPTION { // get a full report on the exception cout << tix_handler::get_report(); } TIX_END_HANDLE }

Following is the output from a sample run:

$ tix_nest Usage: tix_nest <database name> $ tix_nest stuff Sorry, no such database. Try again. $ tix_nest stuff.db The database named stuff.db exists.

Available online This example program is available online in

94 C++ A P I User Guide

Page 97: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 6: Exception Handling

• $OS_ROOTDIR/examples/doc_demos/ug6/tix_nest.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug6\tix_nest.cpp (Windows)

Handling Exceptions Outside a Handler Occasionally, it might not be possible or convenient to handle an exception inside the handler, either because the application must continue with normal processing before handling the exception or because the handler is too restrictive to allow proper handling. In such instances, you can write a TIX exception handler without the handler block.

As mentioned in Establishing a TIX Exception Handler on page 92, the handler block can be omitted from a TIX exception handler. If an exception occurs in a handler that does not have a handler block, program control passes from the statement in the exception block that caused the exception to the first statement outside the handler. This behavior effectively clears the exception.

To illustrate, consider a handler that prompts the user for a file name if the application fails to open either of two database files with the supplied names. The handler can never prompt for the names of both files, even if the names are both wrong. The reason is that the exception generated by the failed attempt to open the first file would prevent the application from trying to open the second file.

The solution is to enclose the statements that open the database files each in its own handler, without supplying a handler block for either one. Without the handler block, the application tries opening both files, even if it fails to open the first. The code to handle the error conditions follows the second handler, where the application tests whether the files were opened successfully.

Release 6.3 95

Page 98: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Establishing a TIX Exception Handler

Example The following example program illustrates this type of exception handler:

// no_hndlr.cc: Uses a handler-less TIX exception handler // to handle exception signaled if one or the other or both of // the databases specified on command line do not exist #include <ostore/ostore.hh> #include <fstream.h> main(int argc, char** argv) { OS_ESTABLISH_FAULT_HANDLER { if(argc!=3) { // check for two command-line arguments cout << "Usage: " << argv[0] << " <db1> <db2>\n"; return 1; } objectstore::initialize(); // set both os_database pointers to 0 so we can check later // to see which database we were able to open, if any os_database *db1 = 0, *db2 = 0; TIX_HANDLE (err_database_not_found) { db1 = os_database::open(argv[1]); // open first db } TIX_EXCEPTION { // no handler code } TIX_END_HANDLE TIX_HANDLE (err_database_not_found) { db2 = os_database::open(argv[2]); // open second db } TIX_EXCEPTION { // no handler code } TIX_END_HANDLE // check if files were opened; null means the open failed if (!db1 || !db2) { cerr << "Cannot open "; if (db2) cerr << argv[1]; // failed to open first db else if (db1) cerr << argv[2]; // failed to open second db else // couldn't open either one cerr << argv[1] << " or " << argv[2]; cerr << endl; return 1; } else // both databases exist cout << "Opened " << argv[1] << " and " << argv[2] << endl; } OS_END_FAULT_HANDLER return 0; }

Following is the output from a sample run; it assumes that stuff and nonsense do not exist and that stuff.db and nonsense.db do exist:

$ no_hndlr stuff nonsense.db Cannot open stuff $ no_hndlr stuff.db nonsense Cannot open nonsense $ no_hndlr stuff nonsense Cannot open stuff or nonsense

Available online This example program is available online in

• $OS_ROOTDIR/examples/doc_demos/ug6/no_hndlr.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug6\no_hndlr.cpp (Windows)

96 C++ A P I User Guide

Page 99: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 6: Exception Handling

Using the TIX Classes ObjectStore provides the following classes for handling TIX exceptions:

• basic_undo

• objectstore_exception

• os_deadlock_exception

• os_lock_timeout_exception

• tix_context

• tix_exception

• tix_handler

These classes fill out the functionality of TIX exception handling, providing services such as explicit signal control, debugging tools, and error-message formatting. The following sections describe the more commonly used of these features.

For detailed information about the TIX classes and their member functions, see the following in the C++ A P I Reference:

• basic_undo

• objectstore_exception

• os_deadlock_exception

• os_lock_timeout_exception

• tix_context

• tix_exception

• tix_handler

Signaling Exceptions All TIX exceptions — predefined as well as user-defined — are instances of objectstore_exception, one of whose member functions is signal(). This function enables you to signal explicitly any TIX exception, including any that you define. Note that, unless you call signal() inside a TIX exception handler, the program never returns from the signaled exception.

The signal() function is useful when the application can detect an error condition before ObjectStore can. Instead of performing superfluous processing, it signals the exception. For example, an application can detect that a string that holds the name of a database is empty. Instead of trying to open the file, it can signal err_invalid_pathname or any other appropriate exception, as follows:

if (!*name) err_invalid_pathname.signal("The string you supplied for filename is empty.");

and handle the exception.

Release 6.3 97

Page 100: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Using the TIX Classes

signal() also performs printf-like formatting for any additional messages you might want to display. signal() thus enables you to give more specific information about the exception and program state at the time of the exception.

The message is displayed only if the exception is not handled. If you call signal() inside a handler, you can supply an empty string ("") for the message argument.

For more information about signal(), see objectstore_exception::signal() in the C++ A P I Reference.

Displaying Additional Error Information ObjectStore provides two classes for displaying additional information about an exception: tix_context and tix_handler. For more information about these classes, see the following in the C++ A P I Reference:

• tix_context

• tix_handler

tix_context Instances of the class tix_context can be used to display information about the context in which an exception occurs. Because tix_context variables are automatic, they are active only in the block in which they are declared, and they do not cause memory leaks. Also, the string you supply as an argument to the constructor for a tix_context variable is displayed only when an exception occurs in the same scoping block.

Consider, for example, the following declaration of tc_var:

{ // block A . . . { // block B tix_context tc_var("Hello, world\n"); . . . { // block C . . . } }}

The message Hello, world appears only if an exception occurs in block B. The message appears immediately after the message displayed by the exception.

tix_context can be useful as a debugging tool, similar to the printf statement. That is, you can declare tix_context variables in different parts of your program that you suspect of causing an exception, so that a formatted message is displayed only if an exception occurs in the same scope.

98 C++ A P I User Guide

Page 101: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 6: Exception Handling

tix_handler When you handle an exception inside a TIX exception handler, ObjectStore does not display any information about the exception. However, the error-message string displayed by ObjectStore when no handler is present can contain useful information about the error that the handler might want to log in an error file. The tix_handler class provides two static function members that return this string: get_report() and get_report0(). The only difference between them is that the string returned by get_report0() does not include the error message associated with the exception.

For more information about get_report() and get_report0(), see the following in the C++ A P I Reference:

• tix_handler::get_report()

• tix_handler::get_report0()

The following handler displays its own error message when it handles the exception err_database_not_found:

TIX_HANDLE (err_database_not_found) { db1 = os_database::open(path); } TIX_EXCEPTION { // cerr << tix_handler::get_report(); cerr << "Bad file name\n"; } TIX_END_HANDLE

Following is the output:

Bad file name

After tix_handler::get_report() is uncommented, the handler displays the following:

The database was not found handcuff:/h/handcuff/2/jimmy/debug/test_tix/stuff (err_database_not_found) Bad file name

For another example of how to use tix_handler::get_report(), see the example program listed in Handling Multiple Exceptions on page 93.

Note If you are using a TIX exception handler that handles the err_deadlock or err_lock_timeout exception, you are responsible for managing the memory allocated for the exception object. To free the exception object, call tix_exception::release_pointer(), as in the following example:

tix_handler::get_exception()->release_pointer();

For more information, see tix_exception::release_pointer() in the C++ A P I Reference.

Release 6.3 99

Page 102: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Using the TIX Classes

100 C++ A P I User Guide

Page 103: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 7Database Access Control

ObjectStore provides two ways to control access to a database:

• Setting read and write permissions for various categories of users

• Requiring applications to supply a matching schema key

Both approaches are discussed in the following sections:

Restricting Access with Permissions 101

Restricting Access with Schema Keys 104

Note ObjectStore also supports server authentication services. For more information, see Authentication Required in Chapter 2 of Managing ObjectStore.

Restricting Access with Permissions For rawfs databases, you can restrict access to a directory, database, or segment for different categories of users. To specify access restrictions on a rawfs database or directory, use os_dbutil::chmod() or the utility oschmod. To specify access restrictions on a segment in a rawfs database, use os_segment::set_access_control().

For detailed information, see os_dbutil::chmod() and os_segment::set_access_control() in the C++ A P I Reference and oschmod in Managing ObjectStore.

For file databases, you can use operating commands to restrict access to a directory or database for different categories of users. With file databases, a segment always has the same protections as the database that contains it.

Categories of Users For any directory, database, or segment, there are three categories of users:

• Its owner

• Users in its primary group

• Everyone else (the default group)

Owner For file databases and the directories they occupy, the owner is determined by the native operating system and its commands. For rawfs databases and the directories they occupy, the owner is initially the creator and is subsequently determined by os_

Release 6.3 101

Page 104: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Restricting Access with Permissions

dbutil::chown() and the utility oschown. The owner of a segment is the owner of the database that contains it; see os_dbutil::chown() in the C++ A P I Reference and oschown in Managing ObjectStore.

Only the owner of a segment can modify its protections with os_segment::set_access_control(); see os_segment::set_access_control() in the C++ A P I Reference.

Group For file databases, the primary group of a directory or database is determined by the native operating system and its commands. For rawfs databases, the primary group of a directory or database is initially the owner’s group and is subsequently determined by os_dbutil::chgrp() and the utility oschgrp. The primary group of a segment initially is the group of the containing database and is subsequently determined by os_segment::set_access_control(). For more information, see os_dbutil::chgrp() in the C++ A P I Reference and oschgrp in Managing ObjectStore.

Group of a user A user’s group membership is determined by the native operating system and its commands.

Specifying Levels of Permission For rawfs databases, you can specify access permission at three levels: directory, database, and segment. For file databases, you can specify permission at two levels: directory and database.

Directory-level At any time, each user has zero or more of the following two types of access to a given directory:

• Write: allows the user to create and delete databases and directories contained in them. For file databases, write access is necessary for the user to update the databases a directory contains.

• Read: allows the user to list the directory content. For file databases, read access is necessary for the user to read the databases a directory contains. Note that rawfs directories do not follow the UNIX convention in distinguishing read and execute access.

Database-level At any time, each user has zero or more of the following two types of access to a given database:

• Write: necessary for the user to open the database for update and, for file databases, to create and modify its data and metadata

• Read: necessary for the user to open the database for read and, for file databases, to read the data and metadata it contains

Segment-level For a given segment in a rawfs database, each user has zero or more of the following two types of access to the segment:

• Write: necessary for the user to update data in the segment

• Read: necessary for the user to read data in the segment

Each new rawfs segment grants both read and write permissions to all three categories of users.

102 C++ A P I User Guide

Page 105: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 7: Database Access Control

Controlling Directory-Level Access To create or delete databases in a given directory, you must have write access to the directory. To list the contents of a directory, you must have read access to the directory.

Controlling Database-Level Access For an application to open a database for update, the user who launched the application must have write permission for the database. For file databases, the user must also have write permission for the directory containing the database.

For an application to open a database for reading, the user must have read permission for the database. For file databases, the user must also have read permission for the directory containing the database.

If a user does not have the appropriate permissions and tries to open a database for read or update, ObjectStore signals err_permission_denied.

Controlling Segment-Level Access In any transaction, the first time an application attempts to lock (for reading or writing) data in a segment, ObjectStore checks the access permissions granted to the user who launched the application, as follows:

• For the application to have write access to data in a segment, the user must have write permission for the segment. For file databases, the user must also have write permission for the directory containing the database that contains the segment, and write permission for the database.

• For the application to have read access to data in a segment, the user must have read permission for the segment. For file databases, the user must also have read permission for the directory containing the database that contains the segment, and read permission for the database.

If a user does not have the appropriate permissions and attempts read or write access to a segment, ObjectStore signals err_permission_denied.

Segment-level permissions and locking

When you change a segment’s permissions, ObjectStore locks the entire segment for write. This means that permission changes at the segment level are transaction consistent (database-level changes are not transaction consistent). This can, however, also adversely affect the performance of the application performing the change, and of concurrent applications, if there is a lot of contention for the segment’s pages.

Setting permissions for related segments

Permissions settings for related segments should be consistent. If an operation on a segment triggers access to another segment, both segments must have consistent permission settings.

Consider, for example, a collection in one segment that has an index in another segment. If you perform a query on the collection that uses the index, both segments must be accessible for read.

Release 6.3 103

Page 106: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Restricting Access with Schema Keys

Similarly, a configuration that spans segments can experience unexpected permission failures if all the configuration’s segments do not have the same protections.

Segment-level permissions A P I

For information about the programming interface for rawfs database segment-level permissions, see the following in the C++ A P I Reference:

• os_segment_access

• os_segment::get_access_control()

• os_segment::set_access_control()

• os_database::get_all_segments_and_permissions()

Restricting Access with Schema Keys You can restrict access to a database’s data and metadata by associating a schema key — a pair of 4-byte unsigned integers — with a database. To access a database that is protected by a schema key, an application must supply a matching key.

The following sections discuss how to use schema keys.

Setting the Database’s Schema Key By default, an ObjectStore database does not have a schema key. You must explicitly specify a key for the database by calling

void os_database::change_schema_key( os_unsigned_int32 old_key_low, os_unsigned_int32 old_key_high, os_unsigned_int32 new_key_low, os_unsigned_int32 new_key_high );

new_key_low and new_key_high specify the schema key for the database referenced by the implicit this argument. If the database already has a schema key and you want to change it, specify the old key in old_key_low and old_key_high, and the new key in new_key_low and new_key_high. To remove schema key protection from a database, specify the old key in old_key_low and old_key_high, and set both new_key_low and new_key_high to 0.

After a database has been given a schema key, any attempt to access its data or metadata without a matching schema key triggers the exception err_schema_key. This exception is also signaled if you call change_schema_key() and specify the wrong key in old_key_low and old_key_high.

For reference information about change_schema_key(), see os_database::change_schema_key() in the C++ A P I Reference.

104 C++ A P I User Guide

Page 107: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 7: Database Access Control

Freezing the schema key

You can prevent any change to a database’s key, even by an application with a matching key, by calling

void os_database::freeze_schema_key( os_unsigned_int32 key_low, os_unsigned_int32 key_high );

For reference information about freeze_schema_key(), see os_database::freeze_schema_key() in the C++ A P I Reference.

Setting the Application’s Schema Key To access a database that is protected by a schema key, an application must have a matching schema key. To set the schema key for an application, call

static void objectstore::set_current_schema_key( os_unsigned_int32 key_low, os_unsigned_int32 key_high );

key_low and key_high specify the schema key for the current application. The specified key must match the database’s schema key in order for the application to gain access to the database’s data and metadata. If the keys do not match, ObjectStore signals an err_schema_key exception. The effect of setting both key_low and key_high to 0 is that the application does not have a schema key.

set_current_schema_key() must be called before the application opens the protected database. The key that was set by the call continues in effect until the database is closed. If a database that is protected by a schema key is autoopened, the application’s schema key that was in effect at the time determines whether the application can access the database. For information about accessing multiple databases protected by schema keys, see Protecting Multiple Databases on page 106.

Note Certain database operations do not require an application to have a schema key, even if the database is protected by a schema key. These operations include opening, overwriting, and destroying a database — none of which requires a schema key.

For reference information about set_current_schema_key(), see objectstore::set_current_schema_key() in the C++ A P I Reference.

Using Schema Key Environment Variables You can also use the following environment variables to determine an application’s schema key:

• OS_SCHEMA_KEY_LOW

• OS_SCHEMA_KEY_HIGH

By default, these variables are not set. Each must be set to values that correspond to the key_low and key_high arguments of objectstore::set_current_schema_key(), as described in Setting the Application’s Schema Key on page 105.

Release 6.3 105

Page 108: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Restricting Access with Schema Keys

The variables determine an application's schema key when an ObjectStore application attempts to access data in a schema-protected database and either of the following is true:

• The application did not set the schema key by using objectstore::set_current_schema_key().

• The application's most recent call to objectstore::set_current_schema_key() specified 0 for both arguments.

The schema key environment variables are most useful when it is not possible to set the schema key programmatically, as when you want to use an ObjectStore utility to access a protected database’s data and metadata. For more information, see Schema Keys and ObjectStore Utilities on page 106.

For more information about the schema key environment variables, see OS_SCHEMA_KEY_HIGH and OS_SCHEMA_KEY_LOW in Managing ObjectStore.

Protecting Multiple Databases If your application opens more than one database, you can use schema keys to restrict access to any or all of them, as follows:

• If all the databases have the same key, set the application’s key with objectstore::set_current_schema_key() before opening any of them. Thereafter, as each database is opened, the application’s key is compared to each database’s key.

• If each database has a different key, call objectstore::set_current_schema_key() before opening each database. The application key that was most recently set previous to opening a database is the one that is compared to that database’s key.

When you are using the environment variables OS_SCHEMA_KEY_LOW and OS_SCHEMA_KEY_HIGH to determine an application's schema key, all schema-protected databases that the application accesses must have the same schema key.

Schema Keys and ObjectStore Utilities Schema keys prevent applications as well as the following ObjectStore utilities from accessing a database’s data or metadata unless they have a matching schema key:

• oscompact

• osexschm

• ossevol

• ossg

• ossize

• osverifydb

To allow any of these utilities to access a database that is protected by a schema key, you must do one of the following:

106 C++ A P I User Guide

Page 109: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 7: Database Access Control

• Set the environment variables OS_SCHEMA_KEY_LOW and OS_SCHEMA_KEY_HIGH, as described in Using Schema Key Environment Variables on page 105. These environment variables are provided when you cannot set a schema key programmatically, as when you are using one of the utilities.

• Build an application that calls the member of the class os_dbutil that corresponds to the utility you (or your customer) wants to use. This application can specify the schema key with objectstore::set_current_schema_key(), as described in Setting the Application’s Schema Key on page 105. For information about the class os_dbutil and its member functions, see os_dbutil in the C++ A P I Reference.

• Certain ObjectStore tools (such as the ossg utility) cannot be invoked from the ObjectStore API. To allow your customers to use such a tool on a database for which you have specified a schema key, build an application that spawns the tool as a child process and specify the key of the child process by setting the environment variables from within the application.

oscopy and schema keys

The schema key has no effect on the operation of the oscopy utility. You can use oscopy to copy a protected database without specifying the correct key. The resulting copy of the database is protected by the same schema key as the original.

All ObjectStore utilities are described in Chapter 4, Utilities, of Managing ObjectStore.

Schema Key Examples The following example programs illustrate how to use os_database::change_schema_key() and objectstore::set_current_schema_key() when you are working with multiple databases, each with its own schema key.

The first example uses a for loop (line 16) to create three databases (line 18), specifying a different schema key for each database (line 20). The value of each key is the same as that of the two integers stored in each database (lines 31–32).

Following is the listing for the first example:

01 // create_sk.cc: illustrates how to set database schema keys 02 // The program creates three databases, writes a pair of integers 03 // in each, and specifies a schema key for each database. The 04 // key values are the same as the values stored in the database.05 #include <ostore/ostore.hh>06 #include <iostream>

07 int main( ) 08 {09 char *db_name[] = { "stuff1.db", "stuff2.db", "stuff3.db" };10 char *root_name[] = { "root1", "root2", "root3" };11 os_database *db[3];12 os_database_root* root;

13 OS_ESTABLISH_FAULT_HANDLER { 14 objectstore::initialize(); 15 OS_BEGIN_TXN(t1, 0, os_transaction::update) {16 for (int i = 0; i < 3; i++) {17 // create the database, overwriting it if it exists 18 db[i] = os_database::create(db_name[i], 0644, 1);

Release 6.3 107

Page 110: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Restricting Access with Schema Keys

19 // specify the schema key for this database20 db[i]->change_schema_key(0, 0, i*2+1, i*2+2);21 cout << "Storing two integers in " << db_name[i]; 22 // create database root 23 root = db[i]->create_root(root_name[i]); 24 // allocate storage for an integer array of 2 elements 25 // and associate the array with the database root 26 int* two_ints = new (db[i], 27 os_typespec::get_int(), 2) int[2]; 28 root->set_value(two_ints); 30 // assign values to the allocated storage for both 30 // array elements 31 *two_ints = i*2+1;32 *(two_ints+1) = i*2+2;33 cout << ": " << i*2+1 << " and " << i*2+2 << endl; 34 db[i]->close(); 35 }36 } OS_END_TXN(t1)37 } OS_END_FAULT_HANDLER38 return 0;39 }

Following is the output from a sample run:

$ create_sk Storing two integers in stuff1.db: 1 and 2 Storing two integers in stuff2.db: 3 and 4 Storing two integers in stuff3.db: 5 and 6

Available online This example program is available online in

• $OS_ROOTDIR/examples/doc_demos/ug7/create_sk.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug7\create_sk.cpp (Windows)

The next program attempts to access the three databases created by the previous program. Before opening any of the databases, the program sets the schema key for the first database (line 20), which allows the application to access its data. The program does not set another schema key before opening the second database. As a result, the original key is still in effect. Since this key does not match the key for the second database, the attempt to access its data (line 24) triggers the err_schema_key exception.

The program includes a TIX exception handler (lines 23–35) for handling this exception. The handler code sets the schema key for the third database (line 33) so that the program can successfully access its data at the next iteration of the for loop.

Following is the listing for the second program:

01 // set_sk.cc: illustrates how to set the application's schema key 02 // The program attempts to access the three databases created 03 // by create_sk.cc. The first attempt is successful. The second 04 // attempt fails because the original schema key is still in effect. 05 // The exception is handled by a TIX exception handler, which sets 06 // the schema key for accessing the third database. That access 07 // is successful. 08 #include <ostore/ostore.hh> 09 #include <iostream>

108 C++ A P I User Guide

Page 111: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 7: Database Access Control

10 int main( ) 11 { 12 char *db_name[] = { "stuff1.db", "stuff2.db", "stuff3.db" }; 13 char *root_name[] = { "root1", "root2", "root3" }; 14 os_database *db[3]; 15 int* two_ints; 16 OS_ESTABLISH_FAULT_HANDLER { 17 objectstore::initialize(); 18 OS_BEGIN_TXN(t1, 0, os_transaction::read_only) { 19 // set the schema key for the first database 20 objectstore::set_current_schema_key(1, 2); 21 for (int i = 0; i < 3; i++) { 22 db[i] = os_database::open(db_name[i]); 23 TIX_HANDLE(err_schema_key) { 24 two_ints = (int*) 25 (db[i]->find_root(root_name[i]))->get_value(); 26 cout << "Contents of " << db_name[i] << ": " 27 << *two_ints <<" and "<< *(two_ints+1) << "\n\n"; 28 } TIX_EXCEPTION { 29 // we get here only if the attempt to access the 30 // database fails because of a schema key violation 31 cout << tix_handler::get_report(); 32 // set the key for accessing the third database 33 objectstore::set_current_schema_key(i*2+3,i*2+4); 34 cout << "Continuing execution . . .\n\n"; 35 } TIX_END_HANDLE 36 db[i]->close(); 37 } 38 } OS_END_TXN(t1) 39 } OS_END_FAULT_HANDLER 40 return 0; 41 }

Following is the output from a sample run:

$ set_sk Contents of stuff1.db: 1 and 2

Error using schema keys <err-0025-0151>The schema is protected and the key, if provided, did not match the one in the schema of database handcuff:/h/handcuff/2/ebailey/release_6.0/schema_keys/stuff2.db. (err_schema_key) Continuing execution . . .

Contents of stuff3.db: 5 and 6

Available online This example program is available online in

• $OS_ROOTDIR/examples/doc_demos/ug7/set_sk.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug7\set_sk.cpp (Windows)

Release 6.3 109

Page 112: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Restricting Access with Schema Keys

110 C++ A P I User Guide

Page 113: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 8Using the Notification Service

The notification service allows an ObjectStore client to notify other clients that an event has taken place. Typically, the event is a change in the database to which other clients have access. Using the notification service, clients can send and receive notifications.

This chapter describes how to use the notifications service and covers the following topics:

Notification Overview 111

Notification Retrieval Alternatives 114

General Notification Behavior 115

Notifications Example 118

Notification OverviewThe notification service allows an ObjectStore client to notify other ObjectStore clients that an event has taken place. Typically, a notification event corresponds to the modification of an object in a database, but applications can assign their own meanings to events.

A notification broadcasts to subscribers that an event (for example, a change) has occurred at a database location (for example, the location of a persistent object). ObjectStore applications can subscribe to receive notifications that are posted on a database location or on a range of database locations. If a range is specified in a subscription, notifications posted on any location in the range are received by the subscribing application. Subscribers can poll for notifications or block (remain in a wait state) until a notification is posted.

When an application posts a notification, it specifies the database location, an integer code, and a character string. The code (known as kind) and the string are made available to subscribers when they receive the notification. The notification is sent to all processes or sessions that are subscribed to the location at the time of the posting.

Release 6.3 111

Page 114: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Notification Overview

Posting a NotificationA notification specifies a database location and two additional user-defined items: a signed 32-bit integer and a null-terminated C string. These items are passed to the receiving process. The string is limited to 16,383 characters. If a notification sends a null string (0), it is received as an empty string (" ").

When a notification is posted, a message is sent to the ObjectStore server. The server then matches the notification with subscriptions and queues messages to be sent to the receiving processes. If there are no subscribers, the notification is discarded. The server returns the number of messages queued. Notifications proceed asynchronously to the cache manager of the receiving processes or sessions.

Specifying a Range of LocationsA range of locations must be contiguous. You can specify a range to be an entire database, a segment, a cluster, an object or a range of objects, or a portion of an object. You can subscribe to a range of locations (that is, every location in a cluster or an array of objects) or to a single location. A notification, however, is always associated with a single database location.

Subscribing and Unsubscribing to RangesA client can subscribe to many ranges of database locations simultaneously. Subscriptions are stored in the ObjectStore server as long as the corresponding database is opened by the client.

You can unsubscribe to ranges just as you can subscribe to them. The unsubscription is immediate. The act of closing a database unsubscribes all notifications for the database.

Notification QueuingThe cache manager maintains a queue of notifications for each client on a machine. Nothing forces a client to read notifications. A client could choose to subscribe to notifications but never receive any.

To avoid resource exhaustion in the cache manager, the size of the notification queue for each client is fixed. If a notification is received when the client’s queue is full, it is discarded. This condition is called an overflow. Overflows do not trigger an exception and do not cause the application, the cache manager, or the server to crash.

The cache manager keeps statistics on the notification queue that include

• Queue size

• Number of pending notifications

• Number of overflows

These statistics are made available to clients through the use of os_dbutil::cmgr_stat(); see Managing Cache Managers in Chapter 9 of the Advanced C++ A P I User Guide. For reference information about cmgr_stat(), see os_dbutil::cmgr_stat() in the C++ A P I Reference.

112 C++ A P I User Guide

Page 115: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 8: Using the Notification Service

Receiving NotificationsNotifications are received in response to a call to os_notification::receive(). This function can also be used to wait for notifications. Applications can poll for notifications without retrieving them, using os_notification::queue_status().

For reference information about these methods, see the following in the C++ A P I Reference:

• os_notification::queue_status()

• os_notification::receive()

Release 6.3 113

Page 116: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Notification Retrieval Alternatives

Notification Retrieval Alternatives There are three methods for retrieving notifications:

• The first method relies on a thread whose sole purpose is to receive notifications.

• The second method requires the application to poll to determine whether notifications have arrived.

• A third method available on UNIX systems is called file-descriptor-based retrieval. This method works on both threaded and nonthreaded systems.

The following sections describe each method.

Retrieving Thread-Based NotificationUsing this method, a dedicated thread is started specifically to receive notifications. This thread calls os_notification::receive() with arguments specifying to wait forever. When it receives a notification, it performs an application-specific action. For example, it might post a Windows message, modify the application’s transient data structures, or otherwise queue the notification for processing by another thread. It then waits for the next notification.

The notification thread typically does very little work. It might do queue management, for example, maintaining a priority queue of notifications for another thread or coalescing similar notifications. However, processing should be minimal so that the cache manager notification queue does not overflow.

In contrast to most other ObjectStore APIs, os_notification::receive() and os_notification::queue_status() are not locked out when other threads are in ObjectStore operations. If the thread does not access persistent data or call other ObjectStore APIs, it can run entirely asynchronously.

Retrieving Polling-Based NotificationUsing this method, the application polls periodically to see whether notifications have arrived, calling os_notification::queue_status() or os_notification::receive(). The application can poll in a main loop or use timers or similar features provided by the environment.

The application should check only whether notifications have arrived. The application should not wait indefinitely for a notification, because it might be holding a lock and the application expected to send the notification might be waiting for that lock. By waiting forever for a notification, you could create a deadly embrace. The server’s deadlock-detection mechanism cannot detect a deadly embrace.

In general, polling as a method of retrieving notifications is less flexible and less efficient than thread-based notification retrieval. However, there are situations in which polling can be quite efficient. If you are uncertain about the conditions affecting the level of efficiency, contact the ObjectStore Consulting Services group for assistance or consult a programming text such as UNIX Network Programming by W. Richard Stevens.

114 C++ A P I User Guide

Page 117: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 8: Using the Notification Service

Retrieving File-Descriptor-Based NotificationOn all UNIX platforms, ObjectStore can provide a file descriptor on which notifications arrive. This feature is currently not available on Windows platforms.

The clients poll or wait for notifications by using the operating system functions select() or poll(). This method is particularly useful in nonthreaded environments in which applications are designed to wait for multiple events by doing a multiplexed wait for activity on a set of file descriptors (fd). Many Motif implementations, for example, fall into this category.

General Notification BehaviorThe following sections describe the primary characteristics of notification.

Subscribing and UnsubscribingSubscribing is accomplished by static member functions of class os_notification, as described in os_notification in the C++ A P I Reference.

You can unsubscribe to ranges just as you can subscribe to them. The unsubscription is immediate. Closing a database for any reason unsubscribes all notifications for the database; that is, all the subscriptions are discarded. It is the application’s responsibility to reinstitute the subscriptions.

Notifications are processed asynchronously. After unsubscribing, notifications that are already queued based on previous subscriptions might result in a client’s receiving notifications even after unsubscribing. ObjectStore makes no guarantees whether such notifications will or will not be received.

Using TransactionsTransactions are entirely independent of immediate notifications, subscriptions, unsubscriptions, and notification retrieval. Sending commit-time notifications is closely integrated with transactions. Commit-time notifications can be queued only inside a transaction, and they are sent only if

• No enclosing transaction aborts.

• The top-level enclosing transaction commits.

There are no restrictions on transaction types. The enclosing transactions might be read-only or update. Databases can be opened read-only, update, or MVCC (multiversion concurrency control).

Database changes made by an application are not visible to other applications until the enclosing top-level transaction commits. Therefore, notifications that indicate changes to persistent data generally should be made at commit time.

In a two-phase commit transaction, if the transaction commits, all notifications are queued for delivery; otherwise, none are queued.

Release 6.3 115

Page 118: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

General Notification Behavior

SecurityTo send and subscribe to notifications, you must open the database in question. Consequently, if a client does not have access to open a database, it cannot send or receive notifications associated with it.

Within a database, notifications are not integrated with ObjectStore security. A client can subscribe and notify based on database locations in any segment even if the client does not have access to the segment itself.

Performance ConsiderationsAll notifications and subscriptions on a database go to the ObjectStore server. The server routes notifications to interested clients, and the cache manager queues the notifications for all its clients. If a client does not retrieve its notifications, the cache manager can run out of queue space.

Polling Because the server acknowledges each notification, sending a notification requires a roundtrip message to the server. Thus, every call to one of the following methods, as described in the C++ A P I Reference, costs the client one roundtrip message to the ObjectStore server.

• os_notification::subscribe()

• os_notification::unsubscribe()

• os_notification::notify_immediate()

Polling for notifications by using os_notification::receive() results in a call to the UNIX system routines poll() or select() or another operating-system-specific call. Polling for notifications by using os_notification::queue_status() is a simple access to shared memory and is the fastest polling mechanism.

If any commit-time notifications are queued during a transaction, there is an additional RPC call to the ObjectStore server during the top-level commit operation.

Notifications are stored and forwarded in the server, in the cache manager, and sometimes even in the receiving application. Therefore, delivery of notifications might not be particularly fast. Performance varies according to system load and the amount of notification processing. For example, delivery could range from milliseconds to several seconds. As a general rule, if you plan for your application to use notification, you should not expect high throughput. Do not expect a client application to send or receive more than approximately ten notifications per second.

Event validation ObjectStore does not check events for validity. It is possible to specify an address in a notification that is illegal in another process. For example, you could allocate a new object and post an immediate notification by using its location. Other processes see this address as invalid because the new object has not yet been committed.

116 C++ A P I User Guide

Page 119: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 8: Using the Notification Service

Notifications API The paragraphs describe the notifications API.

The function that sends a notification has the following overloadings:

static void os_notification::notify_immediate( const os_soft_pointer<void>& loc, int kind = 0, const char* message = 0, os_int32* n_queued = 0);

static void os_notification::notify_immediate( const os_notification *notifications, os_int32* n_notifications = 1, os_int32* n_queued = 0);

To ensure that subscribers do not receive notifications until the changes are visible in the database, use os_notification::notify_on_commit(), as described in the C++ A P I Reference.

Receiving notifications

An application receives notification by calling the following functions:

os_notification::receive (os_notification*&, int timeout=-1);os_reference os_notification::get_reference();int os_notification::get_kind();const char* os_notification::get_string();

Be sure to delete the returned heap-allocated os_notification object when you are finished.

Network ServiceWhen an ObjectStore application uses notifications, it automatically establishes a second network connection to the cache manager daemon on the local host. The application uses this connection to receive (and acknowledge the receipt of) incoming notifications from the cache manager. (Outgoing notifications are sent to the server, not the cache manager.) See When Your Application Uses Notification in Chapter 1 of Managing ObjectStore for specific information about defaults.

Notification Errors The notification APIs do not perform complete validation of the arguments passed to them. Invalid arguments can therefore cause segmentation violations or other undefined behavior. See Chapter 5, Predefined TIX Exceptions, of the C++ A P I Reference for information on specific errors.

ObjectStore Utilities for Managing Notification The ossvrstat utility displays statistics on the number of notifications received and sent by the ObjectStore server. The oscmstat utility displays information on notifications queued for clients, making it useful when you are debugging applications that use notifications. See ossvrstat and oscmstat in Managing ObjectStore for detailed information about these utilities.

Release 6.3 117

Page 120: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Notifications Example

Notifications ExampleThe following example program illustrates the basic operation of the notification service. The program is designed to be invoked twice concurrently.

Note This example program requires multiple processes and therefore will not work with ObjectStore / Single. For a similar example program that is designed to run under ObjectStore / Single, using multiple sessions, see $OS_ROOTDIR/examples/notify (UNIX) or %OS_ROOTDIR%\examples\notify (Windows).

The first invocation runs the program as the notifications receiver; the command line to invoke the receiver should include an argument — any string will do. After it is invoked, the receiver enters the else block (line 67), subscribes to notifications (line 69) , and enters a while loop (line 70) to wait for the notifications before exiting.

The second invocation runs the program as the sender; it should be invoked without any argument. This invocation causes it to enter the if (argc == 1) block (line 38). Once inside the block, the sender enters a while loop (line 42) and then sends notifications to all subscribers (line 46), causing the receiver to break out of the loop (line 75) and exit from the program.

Following is the listing:

01 // notify.cc: Illustrates how to send and receive ObjectStore 02 // notifications. This example is intended to be run in 2 processes. 03 // The sender process is started by running the notify executable 04 // with no command-line arguments. The receiver is started by 05 // running the notify executable with any command-line argument. 06 // Run the receiver first, then the sender. 07 #include <ostore/ostore.hh> 08 #include <iostream> 09 #include <assert.h> 10 #include "timer.hh" 11 int main(int argc, char** argv) 12 { 13 const char* db_name = "notif.db"; 14 const char* root_name = "Test Object"; 15 OS_ESTABLISH_FAULT_HANDLER { 16 // can see client name with "ossvrstat -clients <host>" 17 objectstore::set_client_name(argv[0]); 18 objectstore::initialize(); 19 cout << "Opening database " << db_name << endl; 20 os_database* db = os_database::open(db_name,0,0644); 21 os_soft_pointer<void> location1 = 0; 22 os_soft_pointer<void> location2 = 0; 23 OS_BEGIN_TXN(t1,0,os_transaction::update) { 24 os_database_root* root = db->find_root(root_name); 25 if (!root) { 26 cout << "Creating a couple of ints" << endl; 27 root = db->create_root(root_name); 28 root->set_value( 29 new (db, os_typespec::get_int(), 2) int[2]); 30 } 31 location1 = root->get_value(); // &int[0] 32 location2 = &((int*)location1.resolve())[1]; // &int[1]

118 C++ A P I User Guide

Page 121: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 8: Using the Notification Service

33 } OS_END_TXN(t1) 34 os_notification* note; 35 int iterations = 0; 36 37 // the sender process takes no args on command line 38 if (argc == 1) { 39 cout << "Sender starting notifications..." << endl; 40 os_notification::subscribe(location2); // subscribe 41 double begin = timer::now(); 42 while (iterations < 50) { 43 iterations++; 44 // send immediate notification on location1 with 45 // iterations as kind 46 os_notification::notify_immediate(location1,iterations); 47 // now get response into note 48 os_notification::receive(note); 49 // make sure note response is on location2 50 os_soft_pointer<void> location = note->get_location(); 51 assert(location == location2); 52 // make sure correct iterations come back 53 int kind = note->get_kind(); 54 assert(kind == iterations); 55 delete note; // avoid memory leak 56 } 57 double end = timer::now(); 58 // Tell receiver to exit by sending notification on location1 59 // with kind=0 60 os_notification::notify_immediate(location1,0); 61 cout << "We performed " << 2*iterations 62 << " notifications in " << (end-begin)/1000000 63 << " seconds.\n"; 64 // sender done 65 } 66 // receiver process takes any args on command line 67 else { 68 cout << "Receiver waiting for notifications\n"; 69 os_notification::subscribe(location1); //subscribe to location1 70 while(1) { 71 // receive notification for location1 in note 72 os_notification::receive(note); 73 int kind = note->get_kind(); // see what kind it is 74 if (kind == 0) // if kind is 0, exit 75 break; // receiver done 76 // make sure notification is about location1 77 os_soft_pointer<void> location = note->get_location(); 78 assert(location == location1); 79 delete note; // avoid memory leak 80 // send notification on location2 with kind 81 os_notification::notify_immediate(location2,kind); 82 } 83 } 84 objectstore::shutdown(); 85 } OS_END_FAULT_HANDLER 86 return 0; 87 }

Release 6.3 119

Page 122: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Notifications Example

Following is the output from a sample run:

$ notify arg & Opening database notif.db Receiver waiting for notifications $ notify Opening database notif.db Sender starting notifications... We performed 100 notifications in 0.300145 seconds.

Available online This example program is available online in

• $OS_ROOTDIR/examples/doc_demos/ug8/notify.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug8\notify.cpp (Windows)

120 C++ A P I User Guide

Page 123: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9Schemas

Schemas — the description of the objects stored in an ObjectStore database — play a large and important role in understanding the development of an ObjectStore application. This chapter provides basic information about schema, including schema generation and schema evolution.

This chapter discusses the following topics:

Application Schemas and Schema Source Files 122

Component Schemas 123

Database Schemas 129

Schema Validation 131

Schema Evolution 132

Example of Schema Evolution 138

For full discussions of

• Schema generation, see Chapter 3, Generating Schemas, of Building ObjectStore C++ Applications

• Schema evolution, see Chapter 7, Advanced Schema Evolution, of the Advanced C++ A P I User Guide

Release 6.3 121

Page 124: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Application Schemas and Schema Source Files

Application Schemas and Schema Source Files

Every ObjectStore application has a schema, which consists of the information about the classes the application uses when writing or reading persistent data. When building your application, you mark the classes that form the application’s schema, by specifying the name of each class in the macro OS_MARK_SCHEMA_TYPE(). See OS_MARK_SCHEMA_TYPE() in the C++ A P I Reference.

You specify these macros in one or more schema source files, which you supply as input to the schema generator, ossg, as described in ossg in Managing ObjectStore.

You must mark the following:

• Each class that your application specifies with persistent new when it is creating instances to store in an ObjectStore database

• Each class whose instances your application reads from a database, including entry-point objects

Each schema source file must also have #include directives for

• Every program header file in which a marked class is declared.

• The ObjectStore header file ostore/manschem.hh. The #include directive for this header file must follow those for your application’s header files.

The program listed in An Example Application on page 21 has the following schema source file (schema.cc):

Example of a schema source file

#include "bookclub.hh"#include <ostore/manschem.hh>

OS_MARK_SCHEMA_TYPE(Ele); OS_MARK_SCHEMA_TYPE(Reader); OS_MARK_SCHEMA_TYPE(Book);

The program header file (bookclub.hh) specified on the first line contains an #include directive for the ObjectStore header file ostore/ostore.hh.

ossg command line

Following is the ossg command line used to produce the application schema database (bc_schema.adb) from the schema source file schema.cc:

$ ossg -I$OS_ROOTDIR/include -assf bc_schema.cc \ -asdb bc_schema.adb schema.cc

For more information

For more information about

• The ossg utility and its options, see ossg in Managing ObjectStore

• Using ossg to generate schema, see Chapter 3, Generating Schemas, of Building ObjectStore C++ Applications

• The schema source file macros, see the following in the C++ A P I Reference:

- OS_MARK_SCHEMA_TYPE()

- OS_MARK_SCHEMA_TYPESPEC()

122 C++ A P I User Guide

Page 125: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

- OS_REPORT_DLL_LOAD_AND_UNLOAD()

- OS_SCHEMA_DLL_ID()

- OS_SCHEMA_INFO_NAME()

Component Schemas A dynamic-link library (DLL) — also called a shared library — has its own schema, known as a component schema (sometimes referred to as DLL schema). A component schema plays the same role for the DLL as an application schema does for an application.

Note When you are building a DLL, it is important to compile and link it according to your compiler vendor’s specifications. Incorrectly compiling or linking a DLL can result in serious run-time problems (for example, C++ constructors and destructors that fail to work) that the compiler or linker might not detect.

See the following makefiles for examples of command lines to use when building a DLL:

• Windows: %OS_ROOTDIR%\examples\dllschma\makefile

• UNIX: $OS_ROOTDIR/examples/dllschma/makefile

For information about the example programs in these directories, see Component Schema Examples on page 129.

Component Schemas and Application Schemas A component schema is similar to an application schema in the following respects:

• You use ossg to generate component schemas just as you would for application schemas, as described in Generating an Application or Component Schema in Chapter 3 of Building ObjectStore C++ Applications.

• The file name extension .adb is used for both application schemas and component schemas.

• All ObjectStore utilities that can be used on application schemas can also be used on component schemas.

The differences between a component schema and an application schema are as follows:

• A component schema can be loaded and unloaded dynamically at run time.

• Multiple component schemas can be in effect at the same time in a single program.

• A component schema can be installed only in incremental mode. For information about incremental mode, see Incremental Schema Installation on page 130.

Uses for Component Schemas The following paragraphs describe typical uses of component schemas.

Release 6.3 123

Page 126: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Component Schemas

Stand-alone unit

A class library or object manager for a particular kind of persistent objects is often packaged as a DLL. Packaging the associated schema (the classes implemented by the library and all classes reachable from them) as a component schema allows the DLL/component schema pair to serve as a stand-alone unit. Although an application statically linked to the DLL will have the externally visible classes of the DLL in its application schema, there might be additional internal classes that are only in the component schema. Also, applications like the ObjectStore Inspector, that provide generic database operations without having specific persistent classes built into them, can automatically load the DLL and its component schema when operating on a database that contains persistent instances of the classes implemented by the DLL.

Dynamic switching between implemen-tations

A program can switch dynamically between two different implementations of a persistent class by unloading one DLL that implements the class and loading a different DLL that implements a class with the same name. The corresponding changes to the program schema occur as a result of loading and unloading component schemas. For this to work, the class being changed must not be in the application schema, and the two implementations of the class must have identical run-time layouts.

Component-based systems

A component-based system, such as a web server that loads plug-ins or extensions, can load ObjectStore-based components packaged as DLLs. In this case, the application is completely unaware of ObjectStore and there is no application schema. The component schema of the component assumes the role of an application schema. This scenario is more likely than the first two to involve unloading DLLs and component schemas when components are no longer active.

DLL Loading and Unloading Typically, DLLs are loaded and unloaded by means of operating system calls. ObjectStore provides methods for loading and unloading DLLs. For information about these methods, see the following in the C++ A P I Reference:

• A platform-independent interface:

- objectstore::load_DLL()

- objectstore::lookup_DLL_symbol()

- objectstore::unload_DLL()

• An interface that enables ObjectStore to load DLLs automatically when a database is put into use:

- objectstore::get_autoload_DLLs_function()

- objectstore::set_autoload_DLLs_function()

DLL Load and Unload Reporting When a DLL that has a component schema is loaded or unloaded, it must report that fact to ObjectStore so that ObjectStore can load or unload the component schema. For information about the functions to call for report DLL loading and unloading, see the following in the C++ A P I Reference:

124 C++ A P I User Guide

Page 127: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

• os_DLL_schema_info::DLL_loaded()

• os_DLL_schema_info::DLL_unloaded()

By default, the ossg schema generator automatically generates calls to these functions and inserts them into the DLL as initialization and termination functions. The DLL developer does not have to do anything to implement this reporting.

However, if you want to control reporting explicitly, you must specify the OS_REPORT_DLL_LOAD_AND_UNLOAD(0) macro in the schema source file. For reference information about this macro, see OS_REPORT_DLL_LOAD_AND_UNLOAD() in the C++ A P I Reference.

Specifying 0 (false) as the argument to the OS_REPORT_DLL_LOAD_AND_UNLOAD() macro disables the generation of calls to report DLL loading and unloading. You must then supply the code to make the calls. For example, you could set some ObjectStore parameters or the pathname of the component schema database before calling os_DLL_schema_info::DLL_loaded(). This code could be in DLL initialization and termination functions, or could be in entry points to the DLL that are called according to the developer’s specific protocol.

The flight4 example described in Component Schema Examples on page 129 illustrates how to report DLL loading and unloading manually.

DLL Identifiers DLLs that use component schemas must be assigned one or more DLL identifiers. A DLL identifier is a generic way of identifying a DLL and is recognized by a platform when you want automatic loading. You can assign multiple identifiers to a DLL, but each platform needs to recognize only one of them.

A DLL identifier is a string of the form prefix:suffix, where prefix is a string that identifies the catalog of DLLs to be used and the suffix is something meaningful to that catalog. A colon can also appear in suffix, but the first colon in an identifier is always interpreted as the separator that ends prefix.

When a type is copied from a component schema into a database schema, the DLL identifiers of the component schema are recorded in the database as “required DLL identifiers.” Later, when the database is put in use, ObjectStore checks the database’s required DLL identifiers against the set of component schema that are loaded. If a required DLL identifier is not loaded, ObjectStore tries to load it but doesn’t signal an exception if it is not found.

Release 6.3 125

Page 128: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Component Schemas

How loading occurs depends on the following built-in DLL identifier prefixes:

• DLL: followed by a platform-independent file name. This prefix is converted into a platform-specific file name when it is assigned a suffix such as .dll (Windows) or .so (UNIX). These names are case sensitive. The DLL prefix causes ObjectStore to use the operating system call to search the DLL search path.

• file: followed by a platform-specific file name. It can be an absolute or relative pathname, or just a file name subject to the usual search rules. The case sensitivity of the names is determined by the platform’s file system.

For information about the methods that manipulate DLL identifiers, see the following in the C++ A P I Reference:

• os_database::get_required_DLL_identifiers()

• os_database::insert_required_DLL_identifier()

• os_database::insert_required_DLL_identifiers()

• os_database::remove_required_DLL_identifier()

Creating a DLL Identifier PrefixIn some circumstances, you might need to create a DLL identifier by using a prefix other than the ObjectStore-supplied prefixes DLL: and file:. Some examples of DLL catalogs for which there could be prefixes are

• DLL file (secure search of system paths only...)

• Windows ProgID (symbolic name, mapped in registry)

• Symbolic JavaBean ID, and so on

To inform ObjectStore how to understand a DLL prefix, create an instance of a subclass of os_DLL_finder and call its register_ function with the prefix string; see os_DLL_finder and os_DLL_finder::register_() in the C++ A P I Reference for more information.

Be sure to unregister the prefix before deleting the instance. It is customary for each subclass of os_DLL_finder to know the prefix that it implements and have a constructor and destructor that call register_() and unregister(), respectively. For information about the unregister() method, see os_DLL_finder::unregister() in the C++ A P I Reference.

The argument to the os_DLL_finder::get() function is a DLL identifier, not a prefix. The function finds the prefix by searching for a colon. See os_DLL_finder::get() in the C++ A P I Reference.

Note You cannot register a DLL finder from a static constructor that could be called before objectstore::initialize() has been called. You must call objectstore::initialize() before doing anything with DLL finders, even registering them. For information about initialize(), see objectstore::initialize() in the C++ A P I Reference.

126 C++ A P I User Guide

Page 129: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

Each subclass of os_DLL_finder must provide an implementation of load_DLL that interprets the suffix part of the DLL identifier and calls the appropriate operating system API (or calls another os_DLL_finder) to load the DLL.

The objectstore::load_DLL() functions call os_DLL_finder::load_DLL() for the finder that implements the prefix of the specified DLL_identifier.

Each subclass of os_DLL_finder must provide an implementation of equal_DLL_identifiers_same_prefix that compares two DLL identifiers that are both implemented by this finder and returns true if they are equal.

To compare two DLL identifier strings, call the static function os_DLL_finder::equal_DLL_identifiers(). It takes care of getting the prefixes, looking up the finder, and calling equal_DLL_identifiers_same_prefix. Looking up objects by DLL identifier calls os_DLL_finder::equal_DLL_identifiers() to compare identifiers that have the same prefix.

See os_DLL_finder in the C++ A P I Reference for further information.

Component Schema Macros ObjectStore provides the macros listed in the following table for use in schema source files when you are generating component schemas. For detailed information about these and other system-supplied macros, see Chapter 2, Class Library, of the C++ A P I Reference.

Compiler Dope Damage Compiler dope is additional information added to the run-time layout of an object by the compiler, along with the nonstatic data members of the object. Compilers most frequently use compiler dope to point to a table of virtual function implementations for the object’s class. When ObjectStore brings a persistent object into memory from the database, it ensures that the object contains the correct compiler dope for the current program, for the compiler with which the program was compiled. The correct compiler dope for an object can change as a result of loading or unloading a component schema — for example, because the compiler dope can point to a virtual function implementation contained in a DLL that is being loaded or unloaded.

Transient dope is the portion of the compiler dope that contains pointers to transient (nonpersistent) memory (that is, pointers to virtual function tables), and thus must

Macro Name Function

OS_REPORT_DLL_LOAD_AND_UNLOAD(boolean) Enables (true, the default) or disables (false) ossg-generated calls to report DLL loading and unloading. This macro is optional.

OS_SCHEMA_DLL_ID(string) Specifies the DLL identifier of the component schema. This macro must be used in schema source files when you are generating component schema.

OS_SCHEMA_INFO_NAME(name) Generates the variable extern os_DLL_schema_info name; that is, the os_DLL_schema_info of this component schema. This macro is optional.

Release 6.3 127

Page 130: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Component Schemas

be regenerated each time a persistent object is brought into the memory of a new program instance.

Dope damage occurs when the transient compiler dope of a cached persistent object becomes outdated as a result of loading or unloading component schemas. ObjectStore automatically detects dope damage when it occurs. You can select whether the response to dope damage that occurs during component schema loading is to signal the err_transient_dope_damaged exception or to repair the damage by regenerating transient compiler dope in all cached user data pages of affected databases.

Automatic dope repair

To select the action taken by ObjectStore, call

static void objectstore::enable_damaged_dope_repair( os_boolean);

Specify nonzero (true) to enable automatic repair and 0 (false) to disable it. The default is 0.

Call the following function to determine whether automatic repair is enabled:

static os_boolean objectstore::is_damaged_dope_repair_enabled();

This function returns nonzero (true) if automatic repair is enabled and 0 (false) if disabled.

Both functions are static and therefore do not require the implicit this argument.

Repairing dope damage can take time, so enable repair only if you expect programs that load and unload DLLs dynamically to create dope damage. Disable automatic repair for programs that do not expect dope damage to occur so that unanticipated dope damage does not cause an explained slowdown.

Transient compiler dope can be damaged during the unloading of a component schema if there were any objects with virtual functions implemented in the unloaded DLL. ObjectStore always repairs this form of damage.

Transient compiler dope can be damaged during the loading of a component schema in the following cases:

• When a persistent object is brought into virtual memory before the implementation of its class is loaded

• When a class implementation is redefined by loading a DLL at a point in time that the class is in actual use

Note Dope damage always occurs during the process of unloading a component schema with virtual functions, and it is always repaired, regardless of the setting of enable_damaged_dope_repair().

For reference information, see the following in the C++ A P I Reference:

• objectstore::enable_damaged_dope_repair()

• objectstore::is_damaged_dope_repair_enabled()

128 C++ A P I User Guide

Page 131: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

Schema Evolution and Component Schemas For information about evolving a database to match a component schema, see the following:

• ossevol in Managing ObjectStore

• os_schema_evolution::augment_dll_schema_db_names() in the C++ A P I Reference

Component Schema Examples The ObjectStore examples directory contains the files for building executable programs that illustrate how to package an ObjectStore database application into a component that can be included in a larger application. The examples (including a readme file and makefiles) are located in the following directories:

• On Windows platforms: %OS_ROOTDIR%\examples\dllschma

• On UNIX platforms: $OS_ROOTDIR/examples/dllschma

The examples illustrate possible configurations to use when you are developing an application that uses component schemas, as described in the following:

• flights: does not use a component schema. This example is the base case, which uses a standard application schema and links the application with object files.

• flights2: uses a component schema generated for the DLL flight_cs. The DLL is statically linked with the application at load time, and the component schema is loaded and unloaded automatically.

• flights3: is dynamically linked with the DLL. This example uses automatic load and unload reporting so that ObjectStore can load or unload the component schema.

• flights4: like flights3, but without automatic load reporting. Instead, the application reports DLL loading and unloading manually.

Database SchemasA database’s schema is determined by the schemas of the applications that access the database. An application augments the schema of a database either through batch schema installation or through incremental schema installation. The following sections describe both methods.

Batch Schema InstallationWith batch schema installation (the default), whenever an application first accesses a database, each class in the application’s schema that can be persistently allocated is added to the database’s schema. Subsequent runs of the application need not install schema in that database unless the application’s schema changes, as evidenced by a change in the schema information in the .adb file.

Release 6.3 129

Page 132: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Database Schemas

Batch schema installation occurs when a database is created, assuming that neither the database nor the application is in incremental installation mode. For databases not created by the application, batch schema installation occurs the first time the application accesses the database, within an update transaction, while it is opened for read or write, assuming that neither the database nor the application is in incremental installation mode.

Incremental Schema InstallationWith incremental schema installation, a class is added to a database’s schema only when an instance of that class is first allocated in the database. Incremental installation results in a smaller database schema but can take more time over the lifetime of the database; see Batch Installation Compared to Incremental Installation on page 130.

If you want to ensure incremental installation, use one of the following:

• The OS_INC_SCHEMA_INSTALLATION environment variable, as described in OS_INC_SCHEMA_INSTALLATION in Managing ObjectStore.

• The os_database::set_incremental_schema_installation() function. Calling this function on a database with a nonzero (true) argument causes applications that subsequently open the database to install schemas incrementally. Calling this function with a 0 (false) argument sets the specified database’s installation mode to batch.

• The objectstore::set_incremental_schema_installation() function. Calling this function with a nonzero (true) argument specifies that, for the duration of the current run, an application should perform incremental schema installation on all databases it accesses, regardless of the database’s mode.

See os_database::set_incremental_schema_installation() and objectstore::set_incremental_schema_installation() in the C++ A P I Reference for information about these functions.

Batch Installation Compared to Incremental Installation Advantages of incremental installation

The advantages of incremental installation are as follows:

• Creating an empty database is faster because no user schema is installed at the time of creation (a minimal internally used schema is installed at create time).

• Database schemas, in general, are smaller because only the classes actually required to describe objects in the database are installed.

• Because database schemas do not contain unnecessary classes, spurious incompatibilities between application and database schemas are reduced.

Disadvan-tages of incremental installation

The disadvantages of incremental installation are as follows:

• The first persistent allocation of an object of a given class is slower because it involves installation of that class into the database schema. (The speed of subsequent allocations of objects of that class is unchanged with regard to batch schema installation.)

130 C++ A P I User Guide

Page 133: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

• If an application actually uses all the classes in its schema for access to a given database, the total time to install the schema increases because of the overhead associated with incremental schema merging.

• The schema of the database will have a page or pages locked during schema installation, preventing other clients from using these pages for accessing the database.

Schema ValidationWhen an application accesses a database, the application’s schema and the database’s schema must be compatible. The application’s schema consists of the application schema, if any, along with each component schema that has been loaded. ObjectStore checks for this compatibility during schema validation. For each transaction in which an application accesses a database, ObjectStore checks to make sure that the definitions of all classes in both schemas agree. If the application schema and the database schema are incompatible, ObjectStore signals the exception err_schema_validation_error.

Schema validation occurs the first time in each transaction that the application accesses the database. Normally, the first access to the database in a transaction requires full schema validation. Subsequent transactions check a timestamp to make sure that the schema in the database has not changed since the last transaction.

If you want to handle an err_schema_validation_error exception, you should write TIX exception handlers to enclose the transactions in which a database access could occur. For information about handling exceptions, see Establishing a TIX Exception Handler on page 92.

If you get an err_schema_validation_error exception, you should exit. Expect to do schema evolution in a separate process if you intend to continue using the application against the database. See Schema Evolution on page 132.

The next sections describe the criteria used to determine compatibility during schema validation for the following:

• Classes

• Scalars

• Enumerations

• Arrays

• Pointers

Classes In general, two class definitions are compatible if the following conditions are true:

• They have the same base classes in the same order and at the same offsets. The addition or removal of a virtual function table can cause the offsets to change, as

Release 6.3 131

Page 134: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Schema Evolution

can recompiling an application with compile-line options that affect data alignment.

• They have the same data members declared in the same order and with the same value types.

Scalars ObjectStore uses the following rules to validate integral scalars as compatible:

• char, unsigned char, and signed char are compatible.

• short, unsigned short, and signed short are compatible.

• int, unsigned int, and signed int are compatible.

• long, unsigned long, and signed long are compatible.

Floating-point types are compatible only if they are of the same type (that is, float, double, or long double).

Enumeration Types Enumeration types are compatible if they agree in name and if their enumerators agree in name, number, and value. If any of the enumerators changes, they are no longer compatible.

Arrays Arrays are compatible if the following conditions are true:

• The base types are the same; that is, the types of the array elements must be compatible as classes, scalars, enumeration types, or pointers.

• They have the same number of elements and dimensions.

Pointers Pointers are compatible if their target types are compatible as classes, scalars, or enumeration types.

Schema Evolution Schema evolution refers to the changes that a database’s schema undergoes during a database’s lifetime, especially to changes that require changing the representations of objects already stored in the database. The following are examples of changes requiring schema evolution:

• Adding a data member to a class

• Adding a new base class to a persistently stored class

• Changing a class from concrete to abstract

• Renaming a class

• Removing a class

132 C++ A P I User Guide

Page 135: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

ObjectStore provides the ossevol utility for modifying a schema when the changes are uncomplicated, such as the addition of a zero-initialized data member. For information about this utility, see ossevol in Managing ObjectStore.

If the changes to the schema are more extensive or complicated, ObjectStore provides an API for writing a schema evolution application. With either approach, you modify the schema and change the representations of any existing instances in the database to conform to the new class definitions.

The following sections describe the basic elements of schema evolution. For more advanced topics, see Chapter 7, Advanced Schema Evolution, of the Advanced C++ A P I User Guide.

Planning Schema EvolutionWhen you are planning your schema evolution, it is essential to anticipate all your requirements for the evolution and to plan your application carefully around the desired outcome. A good rule to follow is to try your schema evolution application on small databases to investigate the process, determine what works, and locate anything that might introduce complications.

The following steps will help you to plan your schema evolution:

1 Make a copy of the database you want to evolve and do all your testing on the copy.

2 Use the osverifydb utility to check for database errors.

3 Determine whether you can use the ossevol utility to update a database’s schema or whether you must design a schema evolution application. For information about the types of schema changes that ossevol can process, see Using ossevol on page 133.

4 Plan the schema evolution model in cases in which you must write a schema evolution application.

5 Implement your design.

6 Test your implementation and troubleshoot.

7 If the facility is to be used to upgrade databases currently in use, obtain some active databases for predeployment validation.

8 Limit your initial deployment to beta customers, followed by general deployment to all customers.

When you evolve the schema of a database, you also need to modify and recompile any application that uses that database. The old application or applications will only work with the unevolved database and the new application or applications will only work with new, evolved database. You should plan to put the new applications and the new database into service simultaneously.

Using ossevol The ossevol utility modifies a database and its schema so that it matches a revised application schema. It handles many common cases of schema evolution. Running

Release 6.3 133

Page 136: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Schema Evolution

the ossevol utility changes the physical structure of your database, so you should back up your database before running this utility.

Guidelines for using ossevol

In general, you can use ossevol to evolve a database when schema changes are limited to the following:

• Deleting a class and all instances of that class.

• Adding an uninitialized data member. ossevol initializes all newly added data members to 0.

• Deleting a data member.

• Reordering existing data members.

• Adding or changing an enumerator to an existing enumeration type.

• Changing the type of a data member to an assignment-compatible type.

Getting a task list

Before actually evolving the database, you can run the ossevol utility with the -task_list option. This option causes ossevol to produce (in C++ pseudocode) a list of tasks it performs as part of the evolution of the target database. When you specify this option, ObjectStore does not actually perform schema evolution.

For more information, see ossevol in Managing ObjectStore, which provides detailed guidelines for using ossevol.

Postevolution Processing When you use the ossevol utility to perform schema evolution, it initializes new data members to 0. If you want a value other than 0 and you can calculate the new value independently of any data that are only in the unevolved database, you can write a postevolution application to write this value to the evolved database.

A postevolution application is no different from any other application you would design to access the evolved database, except that it is used only once, immediately after you have used the ossevol utility to evolve the database. Its only purpose is to put the database into the state in which it can be used.

Postevolution processing compared to schema evolution application

If, however, the value of the new data member must be calculated on data stored in the unevolved database and no longer available in the evolved database, you cannot use either ossevol or a postevolution application. Postevolution processing cannot depend on the data members or instances that were removed from the unevolved database during schema evolution. For example, suppose that your application previously stored a product_id member as an int and that you decided to change it to an array of chars. After processing by ossevol, the old int value is lost, making it impossible to convert the old value to the new format in a postevolution application. For situations like this one, you must use an intermediate schema that includes both the old and the new data members in the database. Use ossevol to evolve the database to the intermediate schema. Then run a postevolution application that copies the data from the old data members to the new data members. Finally, run ossevol a second time to evolve the database to the final schema, removing the old data members. For more details and an example, see [reference to a section in the advanced user guide].

134 C++ A P I User Guide

Page 137: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

For an example of a postevolution application, see Using ossevol and a Postevolution Application on page 139.

Writing a Schema Evolution Application Like ossevol, a schema evolution application also performs schema evolution, but it is much more powerful. Unlike ossevol, a schema evolution application allows you to supply code to be executed in the schema evolution process to transform instances of specific classes. Your transformer functions can access the pre-evolved or post-evolved state of an instance, but not both states in the same transformer. An application used to evolve a schema should be dedicated to that task; in other words, do not use the schema evolution API in an application used for normal database access.

A schema evolution application can process schema changes that ossevol can process, plus the following:

• Initializing instances of a new data member from data members of existing instances

• Changing a data member to or from being indexable

• Reinitializing user-defined structures that contain addresses of evolved instances

• Customized handling of untranslatable pointers

Contents of a schema evolution application

A schema evolution application typically consists of a main() function that initiates evolution and one or more transformer functions for each of the classes that you want to transform or modify. Each of these functions uses the schema evolution API; see os_schema_evolution in the C++ A P I Reference. The following paragraphs summarize the basic features of a schema evolution application. For more detailed information, see Chapter 7, Advanced Schema Evolution, of the Advanced C++ A P I User Guide.

Header files The application must include the following ObjectStore header files (in the specified order):

• ostore/ostore.hh

• ostore/coll.hh

• ostore/schmevol.hh

as well as the application’s header files that define the new classes. Some applications can require ostore/mop.hh.

main() function The main() function must include

• A call to os_schema_evolution::initialize() to initialize the schema evolution facility; see Initializing ObjectStore on page 28 and os_schema_evolution::initialize() in the C++ A P I Reference.

• A call to os_schema_evolution::evolve() to initiate evolution of the specified database. For more information, see os_schema_evolution::evolve() in the C++ A P I Reference.

Release 6.3 135

Page 138: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Schema Evolution

• A call to os_schema_evolution::shutdown() to undo the initialization of the schema evolution facility. For more information, see os_schema_evolution::shutdown() in the C++ A P I Reference.

In addition, for each transformer function your application includes, main() must also include a call to os_schema_evolution::augment_pre_evol_transformers() or os_schema_evolution::augment_post_evol_transformers(). These functions take as their argument an instance of the class os_transformer_binding, which associates a transformer function with a class. Each of these functions add its binding to the list to be used during subsequent evolutions. A pre-evol transformer function modifies the object before any object is moved or evolved, while a post-evol transformer function modifies the object after every object has been moved and evolved. See os_schema_evolution::augment_pre_evol_transformers() and os_schema_evolution::augment_post_evol_transformers() as well as os_transformer_binding in the C++ A P I Reference.

Note Do not perform schema evolution in a transaction. ObjectStore generates transactions on its own when performing schema evolution, and signals an exception if the application uses a transaction.

Transformer functions

Transformer functions specify additional modifications you want to make to objects.

Transformer functions all have the signature

void transform-func (void* the_new_obj );

Note that the_new_obj is a void* pointer, which means that it requires a cast to a pointer of the new class.

Linking After you write the schema evolution application and compile it, you must link it with the libraries listed in the following table. Note that for Windows there is only one library, ostore.lib.

If your schema evolution application specifies an application schema in an argument to the os_schema_evolution::set_evolved_schema_db_name(), you need to also link the schema evolution application with the specified application schema object file.

For an example of a schema evolution application that includes a transformer function, see Example of a Schema Evolution Application on page 140.

Library UNIX Link Option Windows Library

Metaobject Protocol -losmop ostore.lib

Schema Evolution -losse ostore.lib

Collections -loscol ostore.lib

Queries -losqry ostore.lib

Basic ObjectStore Libraries -los ostore.lib

Threads -losthr ostore.lib

136 C++ A P I User Guide

Page 139: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

For information about linking ObjectStore applications, see Chapter 4, Compiling, Linking, and Debugging Programs, in Building ObjectStore C++ Applications.

Using osscheq to Verify Schema Changes The osscheq utility is useful for detecting changes to a schema that can cause it to be incompatible with the other schema databases used by an application. Check for any incompatibilities immediately following schema evolution. When schemas are not compatible, execution of the application fails because of a schema validation error.

Invoke the osscheq utility as follows:

osscheq test1 test2

test1 and test2 are the pathnames of the databases you want to compare. One of the databases can be a .adb file, that is, the application schema database produced by the ossg utility. For example, the following command line compares the application’s schema database, my_schema.adb, to the database’s schema in the database file my_db:

$ osscheq my_schema.adb my_db

The minimal checking performed by osscheq ensures that the application and the database use the same layout for all shared classes.

You can also invoke the osscheq utility with the -layout option as follows:

osscheq -layout test1 test2

When using the -layout option, osscheq compares the databases on the basis of the layouts of their persistent objects. The output of osscheq is a list of classes that need to be evolved because they have different layouts.

For more information, see osscheq in Managing ObjectStore.

Using osverifydb to Verify Pointers and References The osverifydb utility verifies all pointers and references in a database. Use this utility before attempting schema evolution and again after schema evolution to establish that the database pointers and references are sound. For more information, see osverifydb in Managing ObjectStore.

Validation Checklist The following checklist can help you confirm that schema evolution actually accomplished your objectives and did not make unexpected alterations to the database:

• The first part of the validation stage should rerun osverifydb and again return a 0 result code, indicating no errors.

• Inspect the database in light of the semantics of the data stored there (as much data as possible should be validated).

• If the database is very large, do some statistical probing of the data to ensure that it has the expected values.

Release 6.3 137

Page 140: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Example of Schema Evolution

• Write a test harness to exercise the database completely.

Contacting Technical Support

If you encounter difficulties when performing or testing the results of any schema evolution operation, you must debug carefully with the assistance of Technical Support. The support people will ask you to supply the following:

• Preevolution database

• Your schema evolution application

• Your new application schema

• Stack trace of the time of failure

Deploying Schema EvolutionAfter you are satisfied that the schema evolution plans achieve your objectives, you can begin deployment. Start by getting some of your customers’ databases and initiating schema evolution on them. Be sure to deploy in stages by validating that your schema evolution application works on several small customer databases before you make it generally available.

Example of Schema Evolution This section illustrates the following approaches to schema evolution:

• Using the ossevol utility and a postevolution application

• Using a schema evolution application

Both approaches will be applied to the example program listed in Example of a Lexical Transaction on page 79 after its schema has been modified. The original program stores a persistent instance of the class note. For this example, the definition of note has been modified to include a date stamp along with the text and priority number for each object.

Following is the header file that defines the class note before it has been modified:

Header file for the unevolved program

// note.hh: note program

#include <iostream>#include <string.h>#include <stdlib.h>#include <ostore/ostore.hh>

class note{public:

// public member functions note(const char*, note*, int); ~note(); void display(ostream& = cout); static os_typespec* get_os_typespec();

// public data members char* user_text; note* next;

138 C++ A P I User Guide

Page 141: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

int priority;};

The only change to the class definition is the addition of the following public data member:

Adding a data member

char date[DATE_SIZE];

where DATE_SIZE is defined as 9.

The application is also changed to include the following function:

void get_today(char* date);

This function sets its argument to the current date, formatted as mm/dd/yy. It is used to assign a value to the newly added date member.

Available online The source files for the modified example are in the following directories:

• $OS_ROOTDIR/examples/doc_demos/ug9 (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug9 (Windows)

Using ossevol and a Postevolution Application Because the change to the schema is limited to adding a data member (putting aside for the moment the question of its initial value), the ossevol utility can evolve the database to include the new data member and the new schema.

Running ossevol to evolve the database

Following is the command line to run ossevol:

$ ossevol workdb new_schema.adb notes.db

workdb is the name of a scratch file that ossevol uses as a workspace; you can delete this file after ossevol has finished successfully. new_schema.adb is the application schema database. notes.db is the copy of the unevolved database, which ossevol modifies to become the evolved database.

Note, however, that after ossevol finishes evolving the database, it is not fully usable because ossevol sets all new data members to 0. In other words, the date member is an empty string. To give date a more meaningful value, a postevolution application is required to fix each instance of note in the evolved database by writing an appropriately formatted string to the date member.

Postevolution application

Designing an application to do this work requires no more than substituting the following transaction for the one listed in Example of a Lexical Transaction on page 79:

OS_BEGIN_TXN(t1,0,os_transaction::update) { os_database_root *root_head = db->find_root("head"); if (!root_head) // if there’s no root, create one root_head = db->create_root("head"); head = (note *)root_head->get_value(note::get_os_typespec());

// write the current date to n->date for (note* n = head; n; n = n->next) get_today(n->date);

} OS_END_TXN(t1)

Release 6.3 139

Page 142: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Example of Schema Evolution

Available online This example of a postevolution application is available online in

• $OS_ROOTDIR/examples/doc_demos/ug9/post_process.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug9\post_process.cpp (Windows)

As mentioned in Example of Schema Evolution on page 138, get_today() is defined to write a formatted string to its argument.

Keep in mind that this application, like any other application designed to access the evolved database, must be linked with the new application schema object file, which you obtain after running ossg and compiling the application schema source file that ossg outputs.

Example of a Schema Evolution Application The example schema evolution application that follows combines the work of both ossevol and the postevolution application, and achieves the same results. The main() function evolves the database, and the transformer function note_transform() writes the desired string to the date member of each new instance.

Following is the listing:

// evol_schema.cpp: schema-evolution application for evolving// schema for note program. Schema evolution in this example// will include setting the new data member (date) to the// desired date, using the get_today() function defined by the// application.#include "note.hh"#include <ostore/coll.hh>#include <ostore/schmevol.hh>

static void note_transform(os_void_p new_note);

int main(int argc, char** argv){ OS_ESTABLISH_FAULT_HANDLER { if (argc != 3) { // check for name of database cout << "Usage: " << argv[0] << " <scratch-file> <db-to-evolve>" << endl; return 1; }

os_schema_evolution::initialize(0, 0, 0);

// associate the transformer function note_transform() // with the class note os_schema_evolution::augment_post_evol_transformers( os_transformer_binding("note", note_transform));

// initiate evolution os_schema_evolution::evolve(argv[1], argv[2]);

os_schema_evolution::shutdown();

} OS_END_FAULT_HANDLER return 0;}

140 C++ A P I User Guide

Page 143: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Chapter 9: Schemas

// called for each new note object in evolved database,// to write current date in the new date memberstatic void note_transform(os_void_p new_note){ // get a pointer to new note object note* p = (note*)new_note; // call get_today() (defined in note.cpp) to copy // string containing current date into date member get_today(p->date);}

If the executable version of this program were invoked with the command line

$ evol_schema scratch_file notes.db

after execution, notes.db would contain the fully evolved database. scratch_file (an intermediate workspace file) could be deleted.

Available online This example of a schema evolution application is available online in

• $OS_ROOTDIR/examples/doc_demos/ug9/evol_schema.cc (UNIX)

• %OS_ROOTDIR%\examples\doc_demos\ug9\evol_schema.cpp (Windows)

For detailed information about writing schema evolution applications, see Chapter 7, Advanced Schema Evolution, of the Advanced C++ A P I User Guide.

Release 6.3 141

Page 144: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Example of Schema Evolution

142 C++ A P I User Guide

Page 145: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Release 6.3

Index

Aabort()

os_transaction, defined by 84abort_top_level()

os_transaction, defined by 85aborting transactions

explicit aborts 84forms of 76rollback 84

abort-only transactions 80absolute pathname resolution 66access control

err_permission_denied exception 103file database 101group 101and locking 103primary group 102rawfs database 101schema key 104schema key mismatch 105segment-level API 104

accessing datalocks and transactions 83

acquire_lock()objectstore, defined by 84

.adb file name extensionapplication schema 137component schema 123

add_DLL_identifier()

os_DLL_schema_info, defined by 124affiliate()

os_database, defined by 65example 27

affiliationchanging 68enabling cross-database pointers 65example 27osaffiliate utility 68

ossize utility 68aliases 75all_exceptions parent exception 90allocating persistent storage 35allocation strategy 46alternative names 75API overview 20application schemas

defined 18application, schema evolution 135arguments

common_parent 67create_mode 60deep_affiliation 66force 63if_exists_overwrite 57pathname

os_database::create() 56os_database::open() 60

read_only 60relative 66schema_database

os_database::create() 57os_database::open() 61

typespec 72arrays

allocating persistently 35compatible 132

augment_post_evol_transformers()

os_schema_evolution, defined by 136augmenting schemas 129automatic variables

of the class os_close_database 63returned by os_cluster::with() 52of the class os_release_..._pointer 51

Bbasic_undo, the class 97

143

Page 146: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

C

batch database schema installation 129begin()

os_transaction, defined by 81schema evolution 136

bracespage-fault macros 34TIX exception handler macros 92transaction macros 77

Ccache manager 16cache, client

See client cachechange_affiliation()

os_database, defined by 68change_schema_key()

os_database, defined by 104checking for mismatch errors 30checking for schema compatibility 131chgrp()

os_dbutil, defined by 102child exceptions 89chmod()

os_dbutil, defined by 101chown()

os_dbutil, defined by 101class templates

creating typespecs for 41classes

compatible 131marking for schema 122

client cachedefined 16

close()

os_database, defined by 63example 31

closing a database 63effect on notifications subscribers 115example 31and multiple opens 63using os_close_database 63

clusteringos_cluster::with() 42

example 30os_cluster_with() 26roots 69using 42

clusters

allocation 36creating 14default 14definition 14huge 14maximum size 14normal 14

coll.hh header file 135comments, segment 48commit()

os_transaction, defined by 81schema evolution 136

committing transactions 76two-phase commit 88

common parent directory 67common_parent argument 67compatibility

rules for validating schema 131compiler dope 127component schemas 123

examples 129schema evolution 129

concurrencyand transactions 77

concurrently opened databases 59constructing os_typespec 40control settings 46create()

os_database, defined by 55create_cluster()

os_database, defined by 14create_mode argument 60create_root()

os_database, defined by 69create_segment()

os_database, defined by 14creating clusters 14creating databases 55creating roots 69creating segments 14cross-database pointers

affiliating databases 65common parent 67declaration example 27enabling 65osaffiliate utility 68and relative pathname 66resolving 66

144 C++ A P I User Guide

Page 147: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Index

using, example 31when valid 44

cross-transaction pointers 44

Ddatabase schemas

batch installation 129defined 18incremental installation 130

databasesSee also access control, file databaseSee also rawfs databaseaccess rights 103affiliation 65basic operations 55changing pointer to 68closing 63concurrently opened 59creating 55cross-database pointers 65description 14destroying 59determining open status 62finding 64overwriting 57pathname, retrieving 65permissions 103reopening 64schema 57schema key 104types of access permission 102

deadlocksconditions causing 76in lexical transactions 78

DECLARE_EXCEPTION(), the macro 91example 26

declaring user-defined exceptions 91deep_affiliation argument 66default

cluster 14segment 14

default group 101DEFINE_EXCEPTION(), the macro 91defining user-defined exceptions 91delete

ObjectStore objects 50persistent storage 37releasing storage 37

transient storage 37deleting

database 59os_notification objects 117persistent storage, example 27return values 20

destroy()

os_database, defined by 59destroying a database 59detecting illegal pointers 45directories

and databases 74permissions 103rawfs 19types of access permission 102

DLL_loaded()os_DLL_schema_info, defined by 124

DLL_unloaded()

os_DLL_schema_info, defined by 124DLLs

component schemas 123DLL identifiers 125loading and unloading 124

dynamic transactionsbeginning 81committing 81deadlocks 82defined 76demarcating 81example 82explicit aborts 82compared to lexical transactions 82multithreaded applications 82

Eenable_damaged_dope_repair()

objectstore, defined by 128entry point

process of retrieving 69enumeration types, compatible 132environment variables

OS_SCHEMA_KEY_HIGH 105OS_SCHEMA_KEY_LOW 105

err_cluster_full exceptionsignaled by ::operator new() 36

err_database_exists exception 56err_database_not_found exception

destroying a database 59

Release 6.3 145

Page 148: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

F

displaying error information 99example 28opening a database 60signaled by os_database::lookup() 64signaled by os_database::open() 60

err_database_not_open exception 69err_db_cannot_change_open exception 61err_deadlock exception 99err_explicit_abort exception 86err_invalid_pathname exception

handling example 94signaling 97

err_invalid_root_value exception 70err_lock_timeout exception 99err_no_entry_point exception, user-defined 26err_no_query_trans exception

signaled in dynamic transaction 81signaled in lexical transaction 78

err_objectstore parent exception 90handling example 94

err_opened_read_only exceptionsignaled by os_database::open() 60

err_permission_denied exception 103err_rpc parent exception 90err_schema_key exception 104err_schema_validation_error exception 131err_transient_dope_damaged exception 128err_type_mismatch exception 72err_typespec_mismatch exception 72error messages, displaying 98establishing entry point

example 31evolve()

os_schema_evolution, defined by 135example programs

abort-only transaction 80application schema key 108bookclub.hh 21database schema key 107dynamic transactions 82lexical transaction 79main.cc 21nested exception handler 94nested transaction 80notifications 118postevolution application 139schema evolution application 140schema source file 122

exception handlerSee TIX exception handler

exceptionsall_exceptions 90calling close() 63during lexical transactions 76err_cluster_full 36err_database_exists 56err_database_not_found

destroying a database 59displaying error information 99example handler 28finding a database 64opening a database 60

err_database_not_open 69err_db_cannot_change_open 61err_deadlock 99err_explicit_abort 86err_invalid_pathname 97

handling 94err_invalid_root_value 70err_lock_timeout 99err_no_entry_point 26err_no_query_trans

signaled in dynamic transactions 81signaled in lexical transactions 78

err_objectstorehandling 94

err_opened_read_only 60err_permission_denied 103err_rpc 90err_schema_key 104err_schema_validation_error 131err_transient_dope_damaged 128err_type_mismatch 72err_typespec_mismatch 72signaling 30stored by transaction macro 78

explicit abortdefined 76in dynamic transaction 86objectstore::abort() 84

Ffetch policy 46file databases

definition 14setting access 101

146 C++ A P I User Guide

Page 149: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Index

find()

os_database_root, defined by 72find_root()

os_database, defined by 72example 30

findingdatabase 64root 72

force argumentos_database::close() 63

forcing a database to close 63freeze_schema_key()

os_database, defined by 105fundamental types

typespecs for 38

Gget_access_control()

os_segment, defined by 104get_affiliated_databases()

os_database, defined by 68get_all_clusters()

os_segment, defined by 52get_all_databases()

os_database, defined by 52get_all_roots()

os_database, defined by 70returns ObjectStore object 52

get_all_segments()

os_database, defined by 52get_all_segments_and_permissions()

os_database, defined by 104get_allocation_strategy()

objectstore, defined by 47os_cluster, defined by 47os_database, defined by 47os_segment, defined by 47

get_autoload_DLLs_function()

objectstore, defined by 124get_char()

os_typespec, defined by 38get_comment()

os_segment, defined by 48get_current()

os_transaction, defined by 85get_double()

os_typespec, defined by 38get_fetch_policy()

objectstore, defined by 47os_cluster, defined by 47os_database, defined by 47os_segment, defined by 47

get_float()

os_typespec, defined by 38get_int()

os_typespec, defined by 38get_kind()

os_notification, defined by 117get_lock_option()

objectstore, defined by 47os_cluster, defined by 47os_database, defined by 47os_segment, defined by 47

get_lock_status()objectstore, defined by 84

get_lock_timeout()

objectstore, defined by 83get_long()

os_typespec, defined by 38get_long_double()

os_typespec, defined by 38get_n_roots()

os_database, defined by 70get_null_illegal_pointers()

objectstore, defined by 45os_cluster, defined by 45os_database, defined by 45os_segment, defined by 45

get_os_int16()os_typespec, defined by 38

get_os_int32()

os_typespec, defined by 38get_os_int64()

os_typespec, defined by 38get_os_signed_int8()

os_typespec, defined by 38get_os_typespec()

declarationexample 27

using 39example 29

get_os_unsigned_int16()

os_typespec, defined by 38get_os_unsigned_int32()

os_typespec, defined by 38get_os_unsigned_int64()

os_typespec, defined by 38

Release 6.3 147

Page 150: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

H

get_os_unsigned_int8()

os_typespec, defined by 38get_parent()

os_transaction, defined by 85get_path_to()

os_database, defined by 67get_pathname()

os_database, defined bydatabase names 68finding database 65rawfs database 75

get_pointer()os_typespec, defined by 39

get_reference()

os_notification, defined by 117get_report()

tix_handler, defined by 99get_required_DLL_identifiers()

os_database, defined by 126get_scope()

os_transaction, defined by 87get_short()

os_typespec, defined by 38get_signed_char()

os_typespec, defined by 38get_signed_int()

os_typespec, defined by 38get_signed_long()

os_typespec, defined by 39get_signed_short()

os_typespec, defined by 39get_string()

os_notification, defined by 117get_thread_locking()

objectstore, defined by 20get_transaction_max_retries()

objectstore, defined by 78get_transient_cluster()

os_cluster, defined by 36get_transient_database()

os_database, defined by 36get_transient_segment()

os_segment, defined by 36get_unsigned_char()

os_typespec, defined by 39get_unsigned_int()

os_typespec, defined by 39get_unsigned_long()

os_typespec, defined by 39

get_unsigned_short()

os_typespec, defined by 39get_value()

os_database_root, defined by 72example 30

global transactions 87granularity, levels of 46groups 101

Hhandling exceptions

See TIX exception handlerheader files 135

coll.hh 135manschem.hh 122in ObjectStore application 25ostore.hh 25in schema evolution application 135in schema source files 122schmevol.hh 135See also including header files

huge cluster 14

Iidentifier, DLL 125if_exists_overwrite argument 57illegal pointers

behavior when detected 45changing default behavior 45cross-transaction pointers 44dangling 46detecting 45incorrectly typed 46

including header filesexample 25in ObjectStore applications 25in schema evolution application 135in schema source files 122

incremental database schema installation 130initialize()

objectstore, defined by 28os_schema_evolution, defined by 135

initializing ObjectStore 28schema evolution application 135

insert_required_DLL_identifier()

os_database, defined by 126insert_required_DLL_identifiers()

148 C++ A P I User Guide

Page 151: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Index

os_database, defined by 126installing database schema 129is_damaged_dope_repair_enabled()

objectstore, defined by 128is_open()

os_database, defined by 62is_open_multi_db_mvcc()

os_database, defined by 62is_open_mvcc()

os_database, defined by 62is_open_read_only()

os_database, defined by 62is_open_single_db_mvcc()

os_database, defined by 62is_open_update()

os_database, defined by 62is_persistent()

objectstore, defined by 37is_transient_cluster()

os_cluster, defined by 36is_transient_database()

os_database, defined by 36is_transient_segment()

os_segment, defined by 36

Kkey mismatch 105

Llevels of granularity 46lexical transactions

automatic retries 78deadlocks 78defined 76compared to dynamic transactions 82example 29explicit aborts 86macros 77

librariesObjectStore 136

lifetimes of objects, managing 48linking

schema evolution applications 136load reporting 124load_DLL()

objectstore, defined by 124loading DLLs 124

local transactionsmultithreaded applications

global transactions 87locking

explicit 84retrieving status 84setting behavior 46setting segment-level permissions 103setting timeouts 83two-phase 83

lookup()

os_database, defined by 64lookup_DLL_symbol()

objectstore, defined by 124

Mmacros, system-supplied

DECLARE_EXCEPTION() 91DEFINE_EXCEPTION() 91OS_BEGIN_TXN() 77OS_END_FAULT_HANDLER 34OS_END_TXN() 77OS_ESTABLISH_FAULT_HANDLER 34

schema evolution 136OS_INC_SCHEMA_INSTALLATION 130OS_MARK_SCHEMA_TYPE() 122OS_REPORT_DLL_LOAD_AND_UNLOAD 127OS_SCHEMA_DLL_ID() 127OS_SCHEMA_INFO_NAME() 127TIX_END_HANDLE 92TIX_EXCEPTION 92TIX_HANDLE() 92TIX_HANDLE_IF() 92

main() function of schema evolution application 135

main.cc, example program 21managing transient objects 48manschem.hh header file 122marking classes for schema 122memory leaks

dynamic transactions 82exception handler 99methods leading to 52ObjectStore objects 48preventing 20

memory mapping architecture 17mismatch errors, checking for 30mkdir()

Release 6.3 149

Page 152: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

N

os_dbutil, defined by 74multiple opens 61multithreaded applications 19

dynamic transactions 82using page-fault macros 34

Nnames

alternative (aliases) 75database roots 26

nested exception handlers 94nested transactions 80

example 80locks 83

network connections and notifications 117new

allocating persistent storage 34examples 30, 35overloadings 35syntax 34typespec argument 35use 34

normal cluster 14notification service

API described 117example 118and the network 117oscmstat utility 117ossvrstat utility 117overview 111performance considerations 116polling 114retrieving notifications 114RPC calls 116sending 116subscribing 115transactions 115unsubscribing 115validation 117

notify_immediate()

os_notification, defined by 116notify_on_commit()

os_notification, defined by 117nulling illegal pointers 46

OObjectStore

API 20cache manager 16directory

See directoriesinitializing 28memory mapping architecture 17ObjectStore file system (rawfs) 19ObjectStore / Single 16overview 17processes 16servers 16

ObjectStore libraries 136ObjectStore objects 48ObjectStore / Single

description 16process diagram 17

objectstore, the classacquire_lock() 84enable_damaged_dope_repair() 128get_allocation_strategy() 47get_autoload_DLLs_function() 124get_fetch_policy() 47get_lock_option() 47get_lock_status() 84get_lock_timeout() 83get_null_illegal_pointers() 45get_thread_locking() 20get_transaction_max_retries() 78initialize() 28

schema evolution application 135is_damaged_dope_repair_enabled() 128is_persistent() 37load_DLL() 124lookup_DLL_symbol() 124propagate_log() 28set_allocation_strategy() 47set_always_null_illegal_pointers() 46set_application_schema_pathname() 28set_autoload_DLLs_function() 124set_cache_file() 28set_cache_size() 28set_client_name() 28set_current_schema_key() 105set_fetch_policy() 47set_incremental_schema_

installation() 130set_lock_option() 47set_lock_timeout() 83

150 C++ A P I User Guide

Page 153: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Index

set_null_illegal_pointers() 45set_thread_locking() 19set_transaction_max_retries() 78set_transient_delete_function() 37unload_DLL() 124

objectstore_exception, the classpredefined TIX exceptions 89signal()

example 30predefined exceptions 97user-defined exception 91

TIX exceptions 89user-defined exceptions 91

octal mode argumentos_database::create() 56os_database::open() 60

of()

os_cluster, defined byoverloading new 35returns ObjectStore object 52

os_database, defined byoverloading new 35returns ObjectStore object 52

os_segment, defined byoverloading new 35returns ObjectStore object 52

open countos_database::close() 63os_database::open() 61

open database status, determining 62open()

os_database, defined by 59creating a database 57example 29

open_multi_db_mvcc()

os_database, defined by 62open_mvcc()

os_database, defined by 62::operator delete()

See delete::operator new()

See newOS_BEGIN_TXN(), the macro

example 29lexical transactions 77

os_close_database, the class 63os_cluster, the class

get_allocation_strategy() 47get_fetch_policy() 47

get_lock_option() 47get_null_illegal_pointers() 45get_transient_cluster() 36is_transient_cluster() 36managing objects of 48of()

overloading new 35returns ObjectStore object 52

release_pointer() 50retain_pointer() 50set_allocation_strategy() 47set_fetch_policy() 47set_lock_option() 47set_null_illegal_pointers() 45with()

clustering 42example 30overloading new 35prevents memory leak 52

os_cluster_with, the classexample 26

os_database, the classaffiliate() 65

example 27change_affiliation() 68change_schema_key() 104close() 63

example 31create() 55create_cluster() 14create_root() 69create_segment() 14destroy() 59find_root() 72

example 30freeze_schema_key() 105get_affiliated_databases() 68get_all_databases() 52get_all_roots() 70

returns ObjectStore object 52get_all_segments() 52get_all_segments_and_permissions() 104get_allocation_strategy() 47get_fetch_policy() 47get_lock_option() 47get_n_roots() 70get_null_illegal_pointers() 45get_path_to() 67get_pathname()

Release 6.3 151

Page 154: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

O

database names 68finding database 65rawfs databases 75

get_required_DLL_identifiers() 126get_transient_database() 36insert_required_DLL_identifier() 126insert_required_DLL_identifiers() 126is_open() 62is_open_multi_db_mvcc() 62is_open_mvcc() 62is_open_read_only() 62is_open_single_db_mvcc() 62is_open_update() 62is_transient_database() 36lookup() 64managing objects of 48of()

overloading new 35returns ObjectStore object 52

open() 59creating a database 57example 29

open_multi_db_mvcc() 62open_mvcc() 62release_pointer() 50remove_required_DLL_identifier() 126retain_pointer() 50set_allocation_strategy() 47set_fetch_policy() 47set_incremental_schema_

installation() 130set_lock_option() 47set_null_illegal_pointers() 45

os_database_root, the classfind() 72get_value() 72

example 30managing objects of 48release_pointer() 50retain_pointer() 50root 69set_value() 70

example 31os_dbutil, the class

chgrp() 102chmod() 101chown() 101mkdir() 74

os_DLL_finder, the class 126

os_DLL_schema_info, the classadd_DLL_identifier() 124DLL_loaded() 124DLL_unloaded() 124

OS_END_FAULT_HANDLER, the macroexample 28using 34

OS_END_TXN(), the macro 77OS_ESTABLISH_FAULT_HANDLER, the macro

example 28schema evolution 136using 34

OS_INC_SCHEMA_INSTALLATION, the macro 130os_lock_timeout_exception, the class 97OS_MARK_SCHEMA_TYPE(), the macro 122os_notification, the class

get_kind() 117get_reference() 117get_string() 117notify_immediate() 116notify_on_commit() 117queue_status() 114receive() 114subscribe() 116unsubscribe() 116

os_release_cluster_pointer, the class 51os_release_database_pointer, the class 51os_release_root_pointer, the class 51os_release_segment_pointer, the class 51OS_REPORT_DLL_LOAD_AND_UNLOAD, the macro 127OS_SCHEMA_DLL_ID(), the macro 127os_schema_evolution, the class

augment_post_evol_transformers() 136evolve() 135

OS_SCHEMA_INFO_NAME(), the macro 127OS_SCHEMA_KEY_HIGH environment variable 105OS_SCHEMA_KEY_LOW environment variable 105os_segment, the class

get_access_control() 104get_all_clusters() 52get_allocation_strategy() 47get_comment() 48get_fetch_policy() 47get_lock_option() 47get_null_illegal_pointers() 45get_transient_segment() 36is_transient_segment() 36managing objects of 48

152 C++ A P I User Guide

Page 155: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Index

of()

overloading new 35returns ObjectStore object 52

release_pointer() 50retain_pointer() 50set_access_control() 101

access control 104set_allocation_strategy() 47set_comment() 48set_fetch_policy() 47set_lock_option() 47set_null_illegal_pointers() 45

os_transaction, the classabort() 84abort_top_level() 85begin() 81

schema evolution 136commit() 81

schema evolution 136get_current() 85get_parent() 85get_scope() 87read_only

dynamic transaction 81lexical transaction 78

updatedynamic transaction 81lexical transaction 78

os_transformer_binding, the class 136os_typespec

constructing 40os_typespec, the class

get_char() 38get_double() 38get_float() 38get_int() 38get_long() 38get_long_double() 38get_os_int16() 38get_os_int32() 38get_os_int64() 38get_os_signed_int8() 38get_os_unsigned_int16() 38get_os_unsigned_int32() 38get_os_unsigned_int64() 38get_os_unsigned_int8() 38get_pointer() 39get_short() 38get_signed_char() 38

get_signed_int() 38get_signed_long() 39get_signed_short() 39get_unsigned_char() 39get_unsigned_int() 39get_unsigned_long() 39get_unsigned_short() 39

os_with_mapped, the class 43osaffiliate utility 68oschgrp utility 102oschmod utility 101oschown utility 102oscmstat utility 117oscompact utility 106oscopy utility 107osexchm utility 106osls utility 75osmkdir utility 74osscheq utility 137ossevol utility

and schema evolution application 135generating task list 134modifying schemas 133schema keys 106when to use 133

ossg utility 19application schema 122component schema 123load and unload reporting 125schema keys 106

ossize utilityaffiliated databases 68schema keys 106segment comments 48

ossvrstat utility 117ostore.hh header file

in ObjectStore applications 25ostore.lib 136osverifydb utility 137

schema keys 106overloadings of new 35overriding operators new and delete 34overview of ObjectStore 17overwriting existing database 57owner 101

Ppage faults 18

Release 6.3 153

Page 156: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Q

macros for handling 33Para Font> 135, 136parameterized typespecs 41parent exceptions

all_exceptions 90err_objectstore 90err_rpc 90in user-defined exceptions 91

pathname argumentrawfs database 74

pathname argumentos_database::create() 56os_database::open() 60

pathname, retrieving 65permissions

directory-level 103incompatibility failures 104segment-level 103segment-level API 104specifying 102

persistent datadefinition 14compared to transient data 33

persistent newSee also new

persistent storageallocating 35delete 36physical organization 14pointers 43

placement argument to persistent new 34example 30

pointerscompatible 132cross-database 44cross-transaction 44illegal 43library calls 43non-ObjectStore processes 43persistent storage 43releasing 50retaining 50system calls 43use with os_with_mapped 43verifying after schema evolution 137

polling 114portable types

typespecs for 38

postevolution processing 134prefix, DLL identifier 126prefix, host server 56prefix, rawfs host 74primary groups 102programming interfaces to ObjectStore, overview 20programs, examples

See example programspropagate_log()

objectstore, defined by 28protection mode values 56

Qqueue_status()

os_notification, defined by 114

Rrawfs database 74rawfs databases

defined 19rawfs host prefix 74setting access 101

read locks 83read permission 102read_only

os_transaction, defined bydynamic transaction 81lexical transaction 78

read_only argument 60receive()

os_notification, defined by 114references

verifying after schema evolution 137registering delete operator 37relative argument 66relative pathname resolution 66release_pointer()

os_cluster, defined by 50os_database, defined by 50os_database_root, defined by 50os_segment, defined by 50tix_exception, defined by 99

releasing transient or persistent storage 37remote procedure calls

See RPCremote schemas 57remove_required_DLL_identifier()

154 C++ A P I User Guide

Page 157: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Index

os_database, defined by 126reopening a database 64reporting, load and unload 124resolving cross-database pointers 66retain_pointer()

os_cluster, defined by 50os_database, defined by 50os_database_root, defined by 50os_segment, defined by 50

retriesautomatic 78

retrieving a database pointer 64retrieving notifications 114

example 118retrieving typespecs, example 29returning transaction 85rolling back transactions 76

objectstore::abort() 84root, database

creating 69finding 72looking up names 70naming 69

RPC calls and notifications 116RPC exceptions 90rules for determining compatibility 131

Sscalars, compatible 132schema evolution

API 135application 135checking for incompatibilities 137component schemas 129defined 132example 138fault-handler macros 136and header files 135ossevol utility 133planning 133and pointers 137postprocessing 134and references 137and transactions 136transformer functions 136validation checklist 137when required 132

schema information 18

stored remotely 57where stored 19

schema keys 104environment variables 105err_schema_key exception 104interaction with ObjectStore utilities 106mismatch 105multiple databases 106and oscompact utility 106and oscopy utility 107and osexchm utility 106and ossevol utility 106and ossg utility 106and ossize utility 106setting 104

schema_database argumentos_database::create() 57os_database::open() 61

schemasSee also schema evolutionaugmenting 129compatibility 131component 123database schema 129installing schema database 129marking classes 122ossg utility 19overview 18remote 57schema generator 19schema segment 14terminology 121validation 131

schmevol.hh header file 135segments

allocation 36comments 48compatible permissions 103creating 14default 14definition 14grouping clusters in 47locking and permissions 103permissions 103permissions API 104schema 14types of access permission 102

sending notifications 116

Release 6.3 155

Page 158: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

T

serializability and locking 83server host prefix in pathname 56servers

defined 16set_access_control()

os_segment, defined by 101access control 104

set_allocation_strategy()

objectstore, defined by 47os_cluster, defined by 47os_database, defined by 47os_segment, defined by 47

set_always_null_illegal_pointers()objectstore, defined by 46

set_application_schema_pathname()

objectstore, defined by 28set_autoload_DLLs_function()

objectstore, defined by 124set_cache_file()

objectstore, defined by 28set_cache_size()

objectstore, defined by 28set_client_name()

objectstore, defined by 28set_comment()

os_segment, defined by 48set_current_schema_key()

objectstore, defined by 105set_fetch_policy()

objectstore, defined by 47os_cluster, defined by 47os_database, defined by 47os_segment, defined by 47

set_incremental_schema_installation()

objectstore, defined by 130os_database, defined by 130

set_lock_option()

objectstore, defined by 47os_cluster, defined by 47os_database, defined by 47os_segment, defined by 47

set_lock_timeout()

objectstore, defined by 83set_null_illegal_pointers()

objectstore, defined by 45os_cluster, defined by 45os_database, defined by 45os_segment, defined by 45

set_thread_locking()

objectstore, defined by 19set_transaction_max_retries()

objectstore, defined by 78set_transient_delete_function()

objectstore, defined by 37set_value()

os_database_root, defined bydescribed 70example 31

setting controls 46shutdown()

os_schema_evolution, defined by 136signal()

objectstore_exception, defined byexample 30predefined exceptions 97user-defined exception 91

signaling exceptionsobjectstore_exception::signal() 97user-defined 91

slash ( ⁄ ), used in pathnames 56subscribe()

os_notification, defined by 116subscribing to notifications 115

example 118

Ttask list, generated with ossevol 134templates

creating typespecs for 41threads support 19

global transactions 87timeouts, lock 83TIX exception handler

establishing 92example handler 94without a handler 95handling schema validation errors 131macros 92multiple exceptions 93nested 93transaction aborts 76

TIX exceptionsSee also TIX exception handlerSee also user-defined exceptionsall_exceptions parent exception 90basic_undo, the class 97

156 C++ A P I User Guide

Page 159: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

Index

child 89classes 97err_objectstore parent exception 90err_rpc parent exception 90hierarchy 89macros 91os_lock_timeout_exception, the class 97parent 89predefined 89tix_context, the class 97tix_exception, the class 97tix_handler, the class 97

tix_context, the classdisplaying error information 98handling TIX exceptions 97

TIX_END_HANDLE, the macroexample 29exception handler 92

tix_exception, the class 97predefined TIX exceptions 89release_pointer() 99use with transaction macro 78

TIX_EXCEPTION, the macroexample 29exception handler 92

TIX_HANDLE(), the macroexample 28exception handler 92

TIX_HANDLE_IF(), the macroexception handler 92

tix_handler, the class 97get_report() 99

transaction tags 77transactions

See also dynamic transactionsSee also lexical transactionsaborting 76

forcing roll back 84abort-only 80accessing persistent data 75committing 76concurrent database access 77and deadlock 76defined 75explicit abort 76global 87local 87locking 83

marking off 77nesting 80notifications 115read locking 83returning 85rollback

defined 76explicit abort 84

and schema evolution 136two-phase commit 88write locking 83

transformer functionsdefined 136syntax 136

transient data compared to persistent data 33transient instances

os_database, returned by create() 55and persistent entities 15

transient storageallocating with ::operator new() 36delete 36

triggering exceptions 97two-phase commit 88two-phase locking 83typespec argument

new 35os_database_root::get_value() 72os_database_root::set_value() 71

typespecsallocated transiently 41for classes 39constructing 40for entry-point object

71for portable types 38for fundamental types 38

example 39get_char() example 39get_os_typespec() 39

example 27parameterized 41preventing mismatch errors 71retrieving 37use with ::operator new() 37

Uunload reporting 124unload_DLL()

Release 6.3 157

Page 160: C++ A P I User Guide - Progress.commedia.progress.com/.../customcd/OStore/6.3/doc/pdf/user1/basicug.pdfC++ API User Guide ObjectStore Release ... AppsAlive, AppServer, ASPen, ASP-in-a-Box,

V

objectstore, defined by 124unloading DLLs 124unsubscribe()

os_notification, defined by 116unsubscribing to notifications 115update

os_transaction, defined bydynamic transaction 81lexical transaction 78

use count 49user-defined exceptions

declaring 91example 26

defining 91example 91macros 91signaling 91

example 30utilities

interaction with schema keys 106osaffiliate 68oschgrp 102oschmod 101oschown 102oscmstat 117oscompact 106oscopy 107osexchm 106osls 75osmkdir 74osscheq 137ossevol 106

generating a task list 134modifying schemas 133and schema evolution applications 135when to use 133

ossg

application schema 122component schema 123load and unload reporting 125and schema keys 106

ossize 106affiliated databases 68segment comments 48

ossvrstat 117osverifydb

and schema keys 106verifying schemas 137

Vvalidating

notifications 117schema 131schema evolution 137

values for protection mode 56verifying schemas

ObjectStore validation 131osscheq utility 137osverifydb utility 137

WWindows library 136with()

os_cluster, defined byclustering 42example 30overloading new 35prevents memory leak 52

write locks 83write permission 102

158 C++ A P I User Guide