FMDatabase.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. #import "FMDatabase.h"
  2. #import "unistd.h"
  3. @implementation FMDatabase
  4. + (id)databaseWithPath:(NSString*)aPath {
  5. return [[[self alloc] initWithPath:aPath] autorelease];
  6. }
  7. - (id)initWithPath:(NSString*)aPath {
  8. self = [super init];
  9. if (self) {
  10. databasePath = [aPath copy];
  11. db = 0x00;
  12. logsErrors = 0x00;
  13. crashOnErrors = 0x00;
  14. busyRetryTimeout = 0x00;
  15. }
  16. return self;
  17. }
  18. - (void)dealloc {
  19. [self close];
  20. [cachedStatements release];
  21. [databasePath release];
  22. [super dealloc];
  23. }
  24. + (NSString*)sqliteLibVersion {
  25. return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
  26. }
  27. - (NSString *)databasePath {
  28. return databasePath;
  29. }
  30. - (sqlite3*)sqliteHandle {
  31. return db;
  32. }
  33. - (BOOL)open {
  34. if (NULL != db) {
  35. return YES;
  36. }
  37. int err = sqlite3_open((databasePath ? [databasePath fileSystemRepresentation] : ":memory:"), &db );
  38. if(err != SQLITE_OK) {
  39. NSLog(@"error opening!: %d", err);
  40. return NO;
  41. }
  42. return YES;
  43. }
  44. #if SQLITE_VERSION_NUMBER >= 3005000
  45. - (BOOL)openWithFlags:(int)flags {
  46. int err = sqlite3_open_v2((databasePath ? [databasePath fileSystemRepresentation] : ":memory:"), &db, flags, NULL /* Name of VFS module to use */);
  47. if(err != SQLITE_OK) {
  48. NSLog(@"error opening!: %d", err);
  49. return NO;
  50. }
  51. return YES;
  52. }
  53. #endif
  54. - (BOOL)close {
  55. [self clearCachedStatements];
  56. if (!db) {
  57. return YES;
  58. }
  59. int rc;
  60. BOOL retry;
  61. int numberOfRetries = 0;
  62. do {
  63. retry = NO;
  64. rc = sqlite3_close(db);
  65. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  66. retry = YES;
  67. usleep(20);
  68. if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
  69. NSLog(@"%s:%d", __FUNCTION__, __LINE__);
  70. NSLog(@"Database busy, unable to close");
  71. return NO;
  72. }
  73. }
  74. else if (SQLITE_OK != rc) {
  75. NSLog(@"error closing!: %d", rc);
  76. }
  77. }
  78. while (retry);
  79. db = nil;
  80. return YES;
  81. }
  82. - (void)clearCachedStatements {
  83. NSEnumerator *e = [cachedStatements objectEnumerator];
  84. FMStatement *cachedStmt;
  85. while ((cachedStmt = [e nextObject])) {
  86. [cachedStmt close];
  87. }
  88. [cachedStatements removeAllObjects];
  89. }
  90. - (FMStatement*)cachedStatementForQuery:(NSString*)query {
  91. return [cachedStatements objectForKey:query];
  92. }
  93. - (void)setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query {
  94. //NSLog(@"setting query: %@", query);
  95. query = [query copy]; // in case we got handed in a mutable string...
  96. [statement setQuery:query];
  97. [cachedStatements setObject:statement forKey:query];
  98. [query release];
  99. }
  100. - (BOOL)rekey:(NSString*)key {
  101. #ifdef SQLITE_HAS_CODEC
  102. if (!key) {
  103. return NO;
  104. }
  105. int rc = sqlite3_rekey(db, [key UTF8String], strlen([key UTF8String]));
  106. if (rc != SQLITE_OK) {
  107. NSLog(@"error on rekey: %d", rc);
  108. NSLog(@"%@", [self lastErrorMessage]);
  109. }
  110. return (rc == SQLITE_OK);
  111. #else
  112. return NO;
  113. #endif
  114. }
  115. - (BOOL)setKey:(NSString*)key {
  116. #ifdef SQLITE_HAS_CODEC
  117. if (!key) {
  118. return NO;
  119. }
  120. int rc = sqlite3_key(db, [key UTF8String], strlen([key UTF8String]));
  121. return (rc == SQLITE_OK);
  122. #else
  123. return NO;
  124. #endif
  125. }
  126. - (BOOL)goodConnection {
  127. if (!db) {
  128. return NO;
  129. }
  130. FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"];
  131. if (rs) {
  132. [rs close];
  133. return YES;
  134. }
  135. return NO;
  136. }
  137. - (void)compainAboutInUse {
  138. NSLog(@"The FMDatabase %@ is currently in use.", self);
  139. #ifndef NS_BLOCK_ASSERTIONS
  140. if (crashOnErrors) {
  141. NSAssert1(false, @"The FMDatabase %@ is currently in use.", self);
  142. }
  143. #endif
  144. }
  145. - (NSString*)lastErrorMessage {
  146. return [NSString stringWithUTF8String:sqlite3_errmsg(db)];
  147. }
  148. - (BOOL)hadError {
  149. int lastErrCode = [self lastErrorCode];
  150. return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW);
  151. }
  152. - (int)lastErrorCode {
  153. return sqlite3_errcode(db);
  154. }
  155. - (sqlite_int64)lastInsertRowId {
  156. if (inUse) {
  157. [self compainAboutInUse];
  158. return NO;
  159. }
  160. [self setInUse:YES];
  161. sqlite_int64 ret = sqlite3_last_insert_rowid(db);
  162. [self setInUse:NO];
  163. return ret;
  164. }
  165. - (int)numChanges {
  166. if (inUse) {
  167. [self compainAboutInUse];
  168. return 0;
  169. }
  170. [self setInUse:YES];
  171. int ret = sqlite3_changes(db);
  172. [self setInUse:NO];
  173. return ret;
  174. }
  175. - (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt; {
  176. if ((!obj) || ((NSNull *)obj == [NSNull null])) {
  177. sqlite3_bind_null(pStmt, idx);
  178. }
  179. // FIXME - someday check the return codes on these binds.
  180. else if ([obj isKindOfClass:[NSData class]]) {
  181. sqlite3_bind_blob(pStmt, idx, [obj bytes], (int)[obj length], SQLITE_STATIC);
  182. }
  183. else if ([obj isKindOfClass:[NSDate class]]) {
  184. sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
  185. }
  186. else if ([obj isKindOfClass:[NSNumber class]]) {
  187. if (strcmp([obj objCType], @encode(BOOL)) == 0) {
  188. sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0));
  189. }
  190. else if (strcmp([obj objCType], @encode(int)) == 0) {
  191. sqlite3_bind_int64(pStmt, idx, [obj longValue]);
  192. }
  193. else if (strcmp([obj objCType], @encode(long)) == 0) {
  194. sqlite3_bind_int64(pStmt, idx, [obj longValue]);
  195. }
  196. else if (strcmp([obj objCType], @encode(long long)) == 0) {
  197. sqlite3_bind_int64(pStmt, idx, [obj longLongValue]);
  198. }
  199. else if (strcmp([obj objCType], @encode(float)) == 0) {
  200. sqlite3_bind_double(pStmt, idx, [obj floatValue]);
  201. }
  202. else if (strcmp([obj objCType], @encode(double)) == 0) {
  203. sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
  204. }
  205. else {
  206. sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
  207. }
  208. }
  209. else {
  210. sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
  211. }
  212. }
  213. - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args {
  214. if (inUse) {
  215. [self compainAboutInUse];
  216. return nil;
  217. }
  218. [self setInUse:YES];
  219. FMResultSet *rs = nil;
  220. int rc = 0x00;;
  221. sqlite3_stmt *pStmt = 0x00;;
  222. FMStatement *statement = 0x00;
  223. if (traceExecution && sql) {
  224. NSLog(@"%@ executeQuery: %@", self, sql);
  225. }
  226. if (shouldCacheStatements) {
  227. statement = [self cachedStatementForQuery:sql];
  228. pStmt = statement ? [statement statement] : 0x00;
  229. }
  230. int numberOfRetries = 0;
  231. BOOL retry = NO;
  232. if (!pStmt) {
  233. do {
  234. retry = NO;
  235. rc = sqlite3_prepare_v2(db, [sql UTF8String], -1, &pStmt, 0);
  236. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  237. retry = YES;
  238. usleep(20);
  239. if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
  240. NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
  241. NSLog(@"Database busy");
  242. sqlite3_finalize(pStmt);
  243. [self setInUse:NO];
  244. return nil;
  245. }
  246. }
  247. else if (SQLITE_OK != rc) {
  248. if (logsErrors) {
  249. NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  250. NSLog(@"DB Query: %@", sql);
  251. #ifndef NS_BLOCK_ASSERTIONS
  252. if (crashOnErrors) {
  253. NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  254. }
  255. #endif
  256. }
  257. sqlite3_finalize(pStmt);
  258. [self setInUse:NO];
  259. return nil;
  260. }
  261. }
  262. while (retry);
  263. }
  264. id obj;
  265. int idx = 0;
  266. int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
  267. while (idx < queryCount) {
  268. if (arrayArgs) {
  269. obj = [arrayArgs objectAtIndex:idx];
  270. }
  271. else {
  272. obj = va_arg(args, id);
  273. }
  274. if (traceExecution) {
  275. NSLog(@"obj: %@", obj);
  276. }
  277. idx++;
  278. [self bindObject:obj toColumn:idx inStatement:pStmt];
  279. }
  280. if (idx != queryCount) {
  281. NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
  282. sqlite3_finalize(pStmt);
  283. [self setInUse:NO];
  284. return nil;
  285. }
  286. [statement retain]; // to balance the release below
  287. if (!statement) {
  288. statement = [[FMStatement alloc] init];
  289. [statement setStatement:pStmt];
  290. if (shouldCacheStatements) {
  291. [self setCachedStatement:statement forQuery:sql];
  292. }
  293. }
  294. // the statement gets closed in rs's dealloc or [rs close];
  295. rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
  296. [rs setQuery:sql];
  297. statement.useCount = statement.useCount + 1;
  298. [statement release];
  299. [self setInUse:NO];
  300. return rs;
  301. }
  302. - (FMResultSet *)executeQuery:(NSString*)sql, ... {
  303. va_list args;
  304. va_start(args, sql);
  305. id result = [self executeQuery:sql withArgumentsInArray:nil orVAList:args];
  306. va_end(args);
  307. return result;
  308. }
  309. - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments {
  310. return [self executeQuery:sql withArgumentsInArray:arguments orVAList:nil];
  311. }
  312. - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args {
  313. if (inUse) {
  314. [self compainAboutInUse];
  315. return NO;
  316. }
  317. [self setInUse:YES];
  318. int rc = 0x00;
  319. sqlite3_stmt *pStmt = 0x00;
  320. FMStatement *cachedStmt = 0x00;
  321. if (traceExecution && sql) {
  322. NSLog(@"%@ executeUpdate: %@", self, sql);
  323. }
  324. if (shouldCacheStatements) {
  325. cachedStmt = [self cachedStatementForQuery:sql];
  326. pStmt = cachedStmt ? [cachedStmt statement] : 0x00;
  327. }
  328. int numberOfRetries = 0;
  329. BOOL retry = NO;
  330. if (!pStmt) {
  331. do {
  332. retry = NO;
  333. rc = sqlite3_prepare_v2(db, [sql UTF8String], -1, &pStmt, 0);
  334. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  335. retry = YES;
  336. usleep(20);
  337. if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
  338. NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
  339. NSLog(@"Database busy");
  340. sqlite3_finalize(pStmt);
  341. [self setInUse:NO];
  342. return NO;
  343. }
  344. }
  345. else if (SQLITE_OK != rc) {
  346. if (logsErrors) {
  347. NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  348. NSLog(@"DB Query: %@", sql);
  349. #ifndef NS_BLOCK_ASSERTIONS
  350. if (crashOnErrors) {
  351. NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  352. }
  353. #endif
  354. }
  355. sqlite3_finalize(pStmt);
  356. [self setInUse:NO];
  357. if (outErr) {
  358. *outErr = [NSError errorWithDomain:[NSString stringWithUTF8String:sqlite3_errmsg(db)] code:rc userInfo:nil];
  359. }
  360. return NO;
  361. }
  362. }
  363. while (retry);
  364. }
  365. id obj;
  366. int idx = 0;
  367. int queryCount = sqlite3_bind_parameter_count(pStmt);
  368. while (idx < queryCount) {
  369. if (arrayArgs) {
  370. obj = [arrayArgs objectAtIndex:idx];
  371. }
  372. else {
  373. obj = va_arg(args, id);
  374. }
  375. if (traceExecution) {
  376. NSLog(@"obj: %@", obj);
  377. }
  378. idx++;
  379. [self bindObject:obj toColumn:idx inStatement:pStmt];
  380. }
  381. if (idx != queryCount) {
  382. NSLog(@"Error: the bind count is not correct for the # of variables (%@) (executeUpdate)", sql);
  383. sqlite3_finalize(pStmt);
  384. [self setInUse:NO];
  385. return NO;
  386. }
  387. /* Call sqlite3_step() to run the virtual machine. Since the SQL being
  388. ** executed is not a SELECT statement, we assume no data will be returned.
  389. */
  390. numberOfRetries = 0;
  391. do {
  392. rc = sqlite3_step(pStmt);
  393. retry = NO;
  394. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  395. // this will happen if the db is locked, like if we are doing an update or insert.
  396. // in that case, retry the step... and maybe wait just 10 milliseconds.
  397. retry = YES;
  398. if (SQLITE_LOCKED == rc) {
  399. rc = sqlite3_reset(pStmt);
  400. if (rc != SQLITE_LOCKED) {
  401. NSLog(@"Unexpected result from sqlite3_reset (%d) eu", rc);
  402. }
  403. }
  404. usleep(20);
  405. if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
  406. NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
  407. NSLog(@"Database busy");
  408. retry = NO;
  409. }
  410. }
  411. else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
  412. // all is well, let's return.
  413. }
  414. else if (SQLITE_ERROR == rc) {
  415. NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(db));
  416. NSLog(@"DB Query: %@", sql);
  417. }
  418. else if (SQLITE_MISUSE == rc) {
  419. // uh oh.
  420. NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(db));
  421. NSLog(@"DB Query: %@", sql);
  422. }
  423. else {
  424. // wtf?
  425. NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db));
  426. NSLog(@"DB Query: %@", sql);
  427. }
  428. } while (retry);
  429. assert( rc!=SQLITE_ROW );
  430. if (shouldCacheStatements && !cachedStmt) {
  431. cachedStmt = [[FMStatement alloc] init];
  432. [cachedStmt setStatement:pStmt];
  433. [self setCachedStatement:cachedStmt forQuery:sql];
  434. [cachedStmt release];
  435. }
  436. if (cachedStmt) {
  437. cachedStmt.useCount = cachedStmt.useCount + 1;
  438. rc = sqlite3_reset(pStmt);
  439. }
  440. else {
  441. /* Finalize the virtual machine. This releases all memory and other
  442. ** resources allocated by the sqlite3_prepare() call above.
  443. */
  444. rc = sqlite3_finalize(pStmt);
  445. }
  446. [self setInUse:NO];
  447. return (rc == SQLITE_OK);
  448. }
  449. - (BOOL)executeUpdate:(NSString*)sql, ... {
  450. va_list args;
  451. va_start(args, sql);
  452. BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orVAList:args];
  453. va_end(args);
  454. return result;
  455. }
  456. - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments {
  457. return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orVAList:nil];
  458. }
  459. - (BOOL)update:(NSString*)sql error:(NSError**)outErr bind:(id)bindArgs, ... {
  460. va_list args;
  461. va_start(args, bindArgs);
  462. BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orVAList:args];
  463. va_end(args);
  464. return result;
  465. }
  466. - (BOOL)rollback {
  467. BOOL b = [self executeUpdate:@"ROLLBACK TRANSACTION;"];
  468. if (b) {
  469. inTransaction = NO;
  470. }
  471. return b;
  472. }
  473. - (BOOL)commit {
  474. BOOL b = [self executeUpdate:@"COMMIT TRANSACTION;"];
  475. if (b) {
  476. inTransaction = NO;
  477. }
  478. return b;
  479. }
  480. - (BOOL)beginDeferredTransaction {
  481. BOOL b = [self executeUpdate:@"BEGIN DEFERRED TRANSACTION;"];
  482. if (b) {
  483. inTransaction = YES;
  484. }
  485. return b;
  486. }
  487. - (BOOL)beginTransaction {
  488. BOOL b = [self executeUpdate:@"BEGIN EXCLUSIVE TRANSACTION;"];
  489. if (b) {
  490. inTransaction = YES;
  491. }
  492. return b;
  493. }
  494. - (BOOL)logsErrors {
  495. return logsErrors;
  496. }
  497. - (void)setLogsErrors:(BOOL)flag {
  498. logsErrors = flag;
  499. }
  500. - (BOOL)crashOnErrors {
  501. return crashOnErrors;
  502. }
  503. - (void)setCrashOnErrors:(BOOL)flag {
  504. crashOnErrors = flag;
  505. }
  506. - (BOOL)inUse {
  507. return inUse || inTransaction;
  508. }
  509. - (void)setInUse:(BOOL)b {
  510. inUse = b;
  511. }
  512. - (BOOL)inTransaction {
  513. return inTransaction;
  514. }
  515. - (void)setInTransaction:(BOOL)flag {
  516. inTransaction = flag;
  517. }
  518. - (BOOL)traceExecution {
  519. return traceExecution;
  520. }
  521. - (void)setTraceExecution:(BOOL)flag {
  522. traceExecution = flag;
  523. }
  524. - (BOOL)checkedOut {
  525. return checkedOut;
  526. }
  527. - (void)setCheckedOut:(BOOL)flag {
  528. checkedOut = flag;
  529. }
  530. - (int)busyRetryTimeout {
  531. return busyRetryTimeout;
  532. }
  533. - (void)setBusyRetryTimeout:(int)newBusyRetryTimeout {
  534. busyRetryTimeout = newBusyRetryTimeout;
  535. }
  536. - (BOOL)shouldCacheStatements {
  537. return shouldCacheStatements;
  538. }
  539. - (void)setShouldCacheStatements:(BOOL)value {
  540. shouldCacheStatements = value;
  541. if (shouldCacheStatements && !cachedStatements) {
  542. [self setCachedStatements:[NSMutableDictionary dictionary]];
  543. }
  544. if (!shouldCacheStatements) {
  545. [self setCachedStatements:nil];
  546. }
  547. }
  548. - (NSMutableDictionary *) cachedStatements {
  549. return cachedStatements;
  550. }
  551. - (void)setCachedStatements:(NSMutableDictionary *)value {
  552. if (cachedStatements != value) {
  553. [cachedStatements release];
  554. cachedStatements = [value retain];
  555. }
  556. }
  557. - (int)changes {
  558. return(sqlite3_changes(db));
  559. }
  560. @end
  561. @implementation FMStatement
  562. - (void)dealloc {
  563. [self close];
  564. [query release];
  565. [super dealloc];
  566. }
  567. - (void)close {
  568. if (statement) {
  569. sqlite3_finalize(statement);
  570. statement = 0x00;
  571. }
  572. }
  573. - (void)reset {
  574. if (statement) {
  575. sqlite3_reset(statement);
  576. }
  577. }
  578. - (sqlite3_stmt *)statement {
  579. return statement;
  580. }
  581. - (void)setStatement:(sqlite3_stmt *)value {
  582. statement = value;
  583. }
  584. - (NSString *)query {
  585. return query;
  586. }
  587. - (void)setQuery:(NSString *)value {
  588. if (query != value) {
  589. [query release];
  590. query = [value retain];
  591. }
  592. }
  593. - (long)useCount {
  594. return useCount;
  595. }
  596. - (void)setUseCount:(long)value {
  597. if (useCount != value) {
  598. useCount = value;
  599. }
  600. }
  601. - (NSString*)description {
  602. return [NSString stringWithFormat:@"%@ %d hit(s) for query %@", [super description], useCount, query];
  603. }
  604. @end