home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Headers / dbkit / DBBinder.h < prev    next >
Text File  |  1992-05-05  |  16KB  |  408 lines

  1. /*
  2. **      DBBinder.h
  3. **      Database Kit, Release 3.0
  4. **      Copyright (c) 1992, NeXT Computer, Inc.  All rights reserved. 
  5. */
  6.  
  7. #import <objc/Object.h>
  8. #import <dbkit/protocols.h>
  9. #import <dbkit/enums.h>
  10.  
  11. #import <objc/List.h>
  12. #import <streams/streams.h>
  13. #import <mach/cthreads.h>
  14.  
  15. @class DBQualifier;
  16. @class DBDatabase;
  17.  
  18. /*
  19. ** The DBBinder is a class which "connects" objects and variables within
  20. **  a NextStep program to external data.  The connect can be bidirectional,
  21. **  that is, values from NextStep can be either propagated to or slaved to
  22. **  the external database.  There are three verbs for modifying external
  23. **  data: "insert", "update", and "delete".  Data can be pulled from the
  24. **  database with "select" and "fetch".  Lastly, "evaluate" can be used to
  25. **  perform either function.
  26. **
  27. ** DBBinder is composed of two internal pieces --
  28. ** (1) mappings, which link DBDataPaths (which can be thought of as
  29. **     pointers to external data) to any number of DBDataWraps (which
  30. **     correspond to internal NextStep "pointers")  When selecting data from
  31. **     the external database into the app, the dataPaths help locate
  32. **     the data, and then the dataWraps are used to load the data into the
  33. **     app's objects.  When loading data from the app to the database, the
  34. **     dataWraps provide data, which is put into the locations referred to
  35. **     by the dataPaths.$    7(2) qualifiers, which are a list of query expressions which are combined
  36. **     by the adaptor into a single expression.  The qualifiers can be built
  37. **     from any combination of objects that recognize the stringValue message,
  38. **     although its common to use DBString, DBExpressionList, DBDataWrap,
  39. **     and DBDataGuide objects as the basic building blocks.  The qualifiers
  40. **     reside in the DBDataSet for the binder.
  41. */
  42.  
  43. @interface DBBinder : Object <DBCursorPositioning>
  44. {
  45. @public
  46.   id database;               // remote source of info
  47.   id recordPrototype;        // the template object (an instance)
  48.   id container;              // the repository for recordPrototype copies
  49.   id delegate;               // receive channel notification, if used
  50.  
  51. @private
  52.   id _qualifier;             // the DBQualifier (optional)
  53.   id _properties;         // a list of DBDataPaths or DBAttributes
  54.   id _mappingsByProperty;    // HashTable of BinderMappings by dataPath
  55.   id _private;
  56.  
  57.   mutex_t _protoLock;        // lock for recordPrototype access
  58.   condition_t _dataAvailable;// condition for async fetch
  59.   cthread_t _fetchThread;    // thread that implements async fetch
  60.   void *_fetchMsg;           // mach message used in async fetch
  61.  
  62.   unsigned _currentPosition; // for cursoring
  63.   unsigned _fetchLimit;         // to control number of rows fetched
  64.   struct {
  65.     BOOL abortFlag:1;         // used by cancel
  66.     BOOL flushEnabled:1;     // whether container is emptied on success
  67.     BOOL freeOnFlush:1;      // whether to free objects in container on flush
  68.     BOOL limitHit:1;         // fetchLimit has been exceeded
  69.     BOOL fetchDone:1;        // when caching, only one fetch done per eval
  70.     BOOL ignoreDuplicates:1; // select distinct, versus select all
  71.     BOOL sharesContext:1;    // shared transaction context for selects
  72.     BOOL ownsRecordPrototype:1; // recordPrototype was created by binder
  73.     int _RESERVED:8;
  74.   } _flags;
  75.   NXZone *_tempZone;         // recursive structures built here for easy free
  76.   NXZone *_protoZone;         // zone for dynamically created objects
  77. }
  78.  
  79. - init;
  80. - initForDatabase:aDb
  81.     withProperties:(List*)aList andQualifier:(DBQualifier*)aQualifier;
  82. - free;
  83.  
  84. /*
  85. ** Setting the database allows you to use the database for transaction
  86. **  control and as a source of data.  It also has a default data dictionary,
  87. **  used by $    8mically created protoClasses.
  88. */
  89. - setDatabase:(DBDatabase*)aDatabase;
  90. - (DBDatabase*)database;
  91.  
  92. /*
  93. ** getProperties fills a List based on the binder's mappings;
  94. **  the order is the same order as the "target list" in the query.
  95. **  Both the list and the properties that are contained in it
  96. **  should not be freed, since they are not copied.
  97. **
  98. ** IMPORTANT!  setProperties: causes a reset of the entire binder!
  99. **
  100. ** setProperties: returns the new recordPrototype object
  101. **
  102. ** addProperty is used to describe a class to be built on the fly.
  103. **  The class is then used to create a recordPrototype instance that is used as
  104. **  the binder's recordPrototype.  This is an alternative to using
  105. **  setRecordPrototype. createRecordPrototype will automatically be called
  106. **  by data-producing methods.
  107. **
  108. ** Calling addProperty or removeProperty should eventually be followed
  109. **  by a call to createRecordPrototype.  Because of this, you normally want to
  110. **  call reset before the first addProperty.
  111. **
  112. ** addProperty: either creates a new mapping or returns the pre-existing
  113. **  mapping for the argument. createRecordPrototype returns new
  114. **  recordPrototype or nil.
  115. */
  116. - (List*)getProperties:(List*)aList;
  117. - (List*)setProperties:(List*)aList;
  118.  
  119. - addProperty:anObject;
  120. - removePropertyAt:(unsigned)index;
  121. - createRecordPrototype;
  122.  
  123. /*
  124. ** These are the main feature...and are pretty self explanatory.  Evaluate
  125. **  is in fact the routine that does all the work usually -- the other verbs
  126. **  take the binder and format it into the expected query language.
  127. **
  128. ** DBBinder is basically a cursor into NextStep -- fetch will load the objects
  129. **  that are contained in the DBDataWraps in the mappings with the next "row"
  130. **  of data.
  131. **
  132. ** Note that a select will normally do a fetch, but that an evaluate leaves
  133. **  it to the programmer to do a fetch if necessary.  Also note that it is
  134. **  good practice to call cancelFetch: when finished, since many databases
  135. **  dedicate expensive runtime structures to a binder, which can be reclaimed
  136. **  upon cancelFetch.
  137. */
  138. - insert;
  139. - select;
  140. - update;
  141. - delete;
  142.  
  143. - (BOOL)evaluateString:(const unsigned char*)aString;
  144.  
  145. - fetch;
  146. - cancelFetch;
  147.  
  148. /*
  149. ** Having used the DBCursorPositioning methods for positioning, here is the
  150. **  method to set/retrieve values from the recordPrototype.  The DBValue
  151. **  pointer that is r$    9ned is owned by the binder, and will change
  152. **  frequently. It is not safe to keep references to one of these DBValues
  153. **  lying around; you should retrieve it immediately before use.
  154. **
  155. ** Note that not all properties in the binder will have a value,
  156. **  since some properties can be embedded in the qualifier tree,
  157. **  rather than having a binding associated with them.  In this case, the call
  158. **  will return nil.
  159. **
  160. ** Also note that there is a maxiumum of one value for a given
  161. **  property.  This means that complex qualifiers MUST use the qualifier
  162. **  mechanism -- the mechanism by which a binding can be used as a qualifier
  163. **  is really meant for qualified updates based on a previous select.
  164. */
  165. - (DBValue*)valueForProperty:(id<DBProperties>)aProperty;
  166.  
  167. /*
  168. ** Adaptors that support sorted results can utilize these methods.  Multiple
  169. **  property sorts are handled in the order in which they are added.
  170. */
  171. - addRetrieveOrder:(DBRetrieveOrder)anOrder for:(id<DBProperties>)aProperty;
  172. - removeRetrieveOrderFor:(id<DBProperties>)aProperty;
  173. - (DBRetrieveOrder)retrieveOrderFor:(id<DBProperties>)aProperty;
  174. - (unsigned)positionInOrderingsFor:(id<DBProperties>)aProperty;
  175.  
  176. /*
  177. ** The binder delegate can act as a control, denying or approving
  178. **  operations.  Any of the methods that return BOOL will either confirm or
  179. **  deny the operation in the head binder.  For example, if binderWillInsert
  180. **  returns NO from the delegate, the insert will not be executed.
  181. */
  182. - delegate;
  183. - setDelegate:anObject;
  184.  
  185. - read:(NXTypedStream*)s;
  186. - write:(NXTypedStream*)s;
  187.  
  188. @end
  189.  
  190. @interface Object (BinderDelegate)
  191.  
  192. - (BOOL)binderWillInsert:aBinder;
  193. - binderDidInsert:aBinder;
  194. - (BOOL)binderWillSelect:aBinder;
  195. - binderDidSelect:aBinder;
  196. - (BOOL)binderWillUpdate:aBinder;
  197. - binderDidUpdate:aBinder;
  198. - (BOOL)binderWillDelete:aBinder;
  199. - binderDidDelete:aBinder;
  200.  
  201. - (BOOL)binder:aBinder willEvaluateString:(const unsigned char*)aString;
  202. - binder:aBinder didEvaluateString:(const unsigned char*)aString;
  203. - (BOOL)binderWillFetch:aBinder;
  204. - binderDidFetch:aBinder;
  205.  
  206. @end
  207.  
  208. @interface DBBinder (Advanced)
  209. /*
  210. ** Data is moved from an adaptor into a binder through the use of objective-C
  211. **  objects.  The binder's recordPrototype object acts as a shuttle between the
  212. **  external database and the binder.
  213. **
  214. ** If there is no recordPrototype object, but the proto class $    @been set, an
  215. **  object of that class will be created; a subclass will be created if there
  216. **  are more results than the recordPrototype can handle.
  217. ** 
  218. ** If no recordPrototype or protoclass has been specified, a class which will
  219. **  suffice is created dynamically.  This class defaults to being a subclass
  220. **  of Object, although the superclass, can be specified using
  221. **  setDynamicRecordSuperclassName.
  222. **
  223. ** To turn off the effect of these methods, set their arguments to NULL.
  224. **  These should not be used without a good understanding of the dynamic
  225. **  class creation protocol.
  226. **
  227. ** Note that these apply globally to all binders using dynamic class creation!
  228. */
  229. + setDynamicRecordSuperclassName:(const char*)aName;
  230. + setDynamicRecordClassName:(const char*)aName;
  231.  
  232. /*
  233. ** Reset clears the binder, and frees any internal structures that it had
  234. **  created.  If you passed your own ids, its up to you to free them, including
  235. **  the recordPrototype, the properties, and the set.  (Internal
  236. **  DBDataPaths and DBDataWraps are freed, but this does not free the
  237. **  objects that are wrapped inside!)
  238. **
  239. ** scratchZone returns an NXZone pointer that can be used to allocate objects
  240. **  that will then be freed en masse whenever a reset is done to the binder.
  241. **  This can be a very efficient way of allocating numerous support objects
  242. **  that exist for a single query.  Note that the zone returned will vary
  243. **  from reset to reset!
  244. **
  245. ** Flush is called by objects that return data (typically adaptors).  Use
  246. **  setFlushEnabled and setFreeObjectsOnFlush to affect its behavior.
  247. */
  248. - reset;
  249. - (BOOL)flush;
  250. - (NXZone*)scratchZone;
  251.  
  252. /*
  253. ** setQualifier returns the old qualifier -- it does not cause a reset.
  254. */
  255. - setQualifier:(DBQualifier*)aQualifier;
  256. - (DBQualifier*)qualifier;
  257.  
  258. /*
  259. ** setRecordPrototype is useful for filling existing classes with data from a
  260. **  database.  Pass a prototypical object in, do the query, and then use
  261. **  the container full o' newly minted objects.  setRecordPrototype: is usually
  262. **  followed by one or more calls to associateXXX:
  263. **
  264. ** The recordPrototype is used as a conduit for data from the adaptor to
  265. **  the binder. When the newly stuffed recordPrototype arrives, the
  266. **  appropriate parts are distributed to any "external mappings" that exist.
  267. **  Because of this, external mappings must always have a val$    Aeference 
  268. **  to the recordPrototype. (This is managed automatically by the binder.)
  269. */
  270. - setRecordPrototype:anObject;
  271. - recordPrototype;
  272. - (BOOL)ownsRecordPrototype;
  273.  
  274. /*
  275. ** These establish which selectors or instance variables in a custom
  276. **  recordPrototype will be mapped onto the database.
  277. */
  278. - associateRecordIvar:(const char*)ivar 
  279.     withProperty:(id<DBProperties>)aProperty;
  280. - associateRecordSelectors:(SEL)set :(SEL)get
  281.     withProperty:(id<DBProperties>)aProperty;
  282.  
  283. /*
  284. ** The container holds the objects to be submitted to the database, or the
  285. **  objects that result from a query.  setContainer: returns the old container
  286. **  so that it can be freed.
  287. **
  288. ** In order to use asynchronous fetching of data safely, the container should
  289. **  either be threadsafe, or all access to the container should be through
  290. **  the binder.  (setTo, etc.)
  291. **
  292. ** Providing a container turns on "caching" of data -- setting it to nil
  293. **  turns it off.
  294. **
  295. ** Containers provide random access positioning -- these routines return
  296. **  an id, which corresponds to the "current object".  
  297. **
  298. ** All of these methods, except setNext:, will raise an exception if there
  299. **  is no container.
  300. **
  301. ** If there is an asyncFetch going on, these calls will BLOCK until the
  302. **  requested "row" is available.  These are actually the preferred interface
  303. **  for getting at the results of an asynchronous fetch while the fetch
  304. **  is in progress.
  305. **
  306. ** All of the positioning methods except for setNext: (which works in all
  307. **  cases) will raise an exception if there is no container.
  308. */
  309. - setContainer:(id<DBContainers>)anObject;
  310. - (id<DBContainers>)container;
  311.  
  312. /*
  313. ** This regulates whether the container is emptied on every data generating
  314. **  message, and if it is emptied, then whether the objects in the container
  315. **  are freed.
  316. */
  317. - setFlushEnabled:(BOOL)yn;
  318. - (BOOL)isFlushEnabled;
  319. - setFreeObjectsOnFlush:(BOOL)yn;
  320. - (BOOL)areObjectsFreedOnFlush;
  321.  
  322. /*
  323. ** fetchAsync forks a thread and returns.
  324. **
  325. ** It then continues to stuff results into the container until fetchData:
  326. **  returns nil.  Because of this, its possible to have an adaptor that could
  327. **  be streaming results back, while accepting changes through evaluate: or
  328. **  other calls...  This will be particularly handy with news feeds, etc.
  329. **
  330. ** It is, however, fatal to change the structure of the binder wil$    Be
  331. **  fetch thread is running -- this means that only the non-structural calls
  332. **  are safe, such as cancelFetch:
  333. **
  334. **  fetchInThread will fetch data into the container in a separate thread.
  335. **   There must be a container for this routine to work.  If this is used
  336. **   from a non-NeXTstep program, then checkThreadedFetchCompletion: can be
  337. **   used to sync with the thread.
  338. */
  339. - selectWithoutFetching;
  340. - fetchInThread;
  341.  
  342. - checkThreadedFetchCompletion:(double)timeout;
  343.  
  344. /*
  345. ** This can be called by an adaptor -- the default behavior is to defer to
  346. **  the database, who in turn defers to its delegate.  This method is
  347. **  called immediately before an expression is evaluated.  If NO is returned,
  348. **  the expression is cancelled.  Normally, this method is called for every
  349. **  query expression evaluated.
  350. */
  351. - (BOOL)adaptorWillEvaluateString:(const unsigned char*)aString;
  352.  
  353. /*
  354. ** If you'd only like only unique rows for the qualifier (and the adaptor
  355. **  supports this) then setIgnoreDuplicateResults:YES
  356. */
  357. - setIgnoresDuplicateResults:(BOOL)yn;
  358. - (BOOL)ignoresDuplicateResults;
  359.  
  360. /*
  361. ** Databases that support "protected" access through the notion of
  362. **  transaction processing have a "cursor" that can be shared among a number
  363. **  of binders.  The default is for this "cursor" to be used by any data
  364. **  modifying operations (like insert, delete, or update).  Select, evaluate,
  365. **  and fetch, however, get their own "cursors" by default.  To change this
  366. **  behavior for a binder, set this flag to YES.  The binder will then use
  367. **  the shared "cursor" even for these operations.
  368. **
  369. ** NOTE HOWEVER: when using a single shared resource, all operations must fully
  370. **  complete before the next is invoked.  (selects and updates could not be
  371. **  interleaved, for instance...)  The default behavior permits a looser
  372. **  ordering than this, but is also potentially more expensive in terms of
  373. **  resources.
  374. **
  375. ** ALSO NOTE: [setSharesContext:YES] will turn OFF flushing for a binder!
  376. **
  377. ** By setting the context to be shared, "select for update" can be implemented,
  378. **  in which the selected items are locked until updated.
  379. **
  380. **    [someBinder setSharesContext:YES];
  381. **     [someDatabase beginTransaction];
  382. **    [someBinder select:self];
  383. **      ...processing here...
  384. **    [someBinder update:self];
  385. **     [someDatabase endTransaction];
  386. */
  387. - setSharesContext:(BOOL)$    C- (BOOL)sharesContext;
  388.  
  389. /*
  390. ** A sanity check for retrieving data -- note that multiple fetches can
  391. **  be performed to "continue" an operation that was stopped because of the
  392. **  limit.  This limit only applies when a container is in place and a
  393. **  synchronous fetch is used.
  394. */
  395. - (unsigned)maximumRecordsPerFetch;
  396. - setMaximumRecordsPerFetch:(unsigned)aRecordCount;
  397. - (BOOL)recordLimitReached;
  398.  
  399. @end
  400.  
  401. @interface List (DBContainers)
  402.  
  403. - addObject:anObject forBinder:(DBBinder*)aBinder;
  404. - (unsigned int)prepareForBinder:(DBBinder*)aBinder;
  405. - objectAt:(unsigned int)index forBinder:(DBBinder*)aBinder;
  406.  
  407. @end
  408.