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