2
0

sqlite.bmx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  1. ' Copyright (c) 2007-2023 Bruce A Henderson
  2. ' All rights reserved.
  3. '
  4. ' Redistribution and use in source and binary forms, with or without
  5. ' modification, are permitted provided that the following conditions are met:
  6. ' * Redistributions of source code must retain the above copyright
  7. ' notice, this list of conditions and the following disclaimer.
  8. ' * Redistributions in binary form must reproduce the above copyright
  9. ' notice, this list of conditions and the following disclaimer in the
  10. ' documentation and/or other materials provided with the distribution.
  11. ' * Neither the auther nor the names of its contributors may be used to
  12. ' endorse or promote products derived from this software without specific
  13. ' prior written permission.
  14. '
  15. ' THIS SOFTWARE IS PROVIDED BY Bruce A Henderson ``AS IS'' AND ANY
  16. ' EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  17. ' WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. ' DISCLAIMED. IN NO EVENT SHALL Bruce A Henderson BE LIABLE FOR ANY
  19. ' DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  20. ' (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21. ' LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  22. ' ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. ' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  24. ' SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. '
  26. SuperStrict
  27. Rem
  28. bbdoc: Database Driver - SQLite
  29. about: An SQLite database driver for #Database.Core
  30. End Rem
  31. Module Database.SQLite
  32. ModuleInfo "Version: 1.21"
  33. ModuleInfo "Author: Bruce A Henderson"
  34. ModuleInfo "License: BSD"
  35. ModuleInfo "Copyright: Wrapper - 2007-2023 Bruce A Henderson"
  36. ModuleInfo "Copyright: SQLite - The original author of SQLite has dedicated the code to the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute the original SQLite code, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means."
  37. ModuleInfo "Modserver: BRL"
  38. ModuleInfo "History: 1.21"
  39. ModuleInfo "History: Update to SQLite 3.44.2."
  40. ModuleInfo "History: 1.20"
  41. ModuleInfo "History: Update to SQLite 3.38.5."
  42. ModuleInfo "History: 1.19"
  43. ModuleInfo "History: Update to SQLite 3.37.2."
  44. ModuleInfo "History: 1.18"
  45. ModuleInfo "History: Update to SQLite 3.29.0."
  46. ModuleInfo "History: Disable double-quoted string literals by default."
  47. ModuleInfo "History: 1.17"
  48. ModuleInfo "History: Update to SQLite 3.28.0."
  49. ModuleInfo "History: 1.16"
  50. ModuleInfo "History: Update to SQLite 3.27.2."
  51. ModuleInfo "History: Fixed query free issue."
  52. ModuleInfo "History: 1.15"
  53. ModuleInfo "History: Update to SQLite 3.22.0."
  54. ModuleInfo "History: Fixed for 64-bit targets."
  55. ModuleInfo "History: 1.14"
  56. ModuleInfo "History: Update to SQLite 3.8.11.1."
  57. ModuleInfo "History: Added user authentication support."
  58. ModuleInfo "History: 1.13"
  59. ModuleInfo "History: Update to SQLite 3.8.2."
  60. ModuleInfo "History: 1.12"
  61. ModuleInfo "History: Update to SQLite 3.7.15."
  62. ModuleInfo "History: Updated documentation."
  63. ModuleInfo "History: Added loadOrSaveDB() function."
  64. ModuleInfo "History: 1.11"
  65. ModuleInfo "History: Update to SQLite 3.6.15."
  66. ModuleInfo "History: Fixed prepared statement reuse issue."
  67. ModuleInfo "History: Fixed problem where open/live queries could cause problem when committing."
  68. ModuleInfo "History: Added getTableInfo() support."
  69. ModuleInfo "History: Added blob support."
  70. ModuleInfo "History: 1.10"
  71. ModuleInfo "History: Update to SQLite 3.5.6."
  72. ModuleInfo "History: Fixed lack of error reporting during query execution."
  73. ModuleInfo "History: Transaction queries are finalized more quickly."
  74. ModuleInfo "History: Statement should generally be reset before acquiring error message."
  75. ModuleInfo "History: 1.09"
  76. ModuleInfo "History: Update to SQLite 3.5.2. Now using the Amalgamated version."
  77. ModuleInfo "History: Implementation of Date, DateTime and Time types."
  78. ModuleInfo "History: 1.08"
  79. ModuleInfo "History: Fixed null column types not being handled."
  80. ModuleInfo "History: 1.07"
  81. ModuleInfo "History: Fixed problem with lastInsertedId() not returning.. the last inserted id."
  82. ModuleInfo "History: 1.06"
  83. ModuleInfo "History: Update to SQLite 3.4.2."
  84. ModuleInfo "History: 1.05"
  85. ModuleInfo "History: Fixed database Close to cleanup non-finalized queries."
  86. ModuleInfo "History: 1.04"
  87. ModuleInfo "History: Improved error message details."
  88. ModuleInfo "History: 1.03"
  89. ModuleInfo "History: Fixed NextRow returning True on empty queries."
  90. ModuleInfo "History: 1.02"
  91. ModuleInfo "History: Fixed issue with mis-count of bound parameters."
  92. ModuleInfo "History: 1.01"
  93. ModuleInfo "History: Added hasPrepareSupport() and hasTransactionSupport() methods."
  94. ModuleInfo "History: 1.00 Initial Release"
  95. ModuleInfo "History: Includes SQLite 3.3.13 source."
  96. ModuleInfo "CC_OPTS: -DSQLITE_USER_AUTHENTICATION"
  97. ModuleInfo "CC_OPTS: -DSQLITE_DQS=0" ' deactivates the double-quoted string literal "misfeature". see https://www.sqlite.org/quirks.html#dblquote
  98. Import Database.Core
  99. Import "common.bmx"
  100. ' Notes
  101. '
  102. ' Appended userauth.c to end of sqlite3.c
  103. '
  104. ' The implementation
  105. Type TDBSQLite Extends TDBConnection
  106. Field queries:TSQLiteResultSet[2]
  107. Function Create:TDBConnection(dbname:String = Null, host:String = Null, ..
  108. port:Int = Null, user:String = Null, password:String = Null, ..
  109. server:String = Null, options:String = Null)
  110. Local this:TDBSQLite = New TDBSQLite
  111. this.init(dbname, host, port, user, password, server, options)
  112. If this._dbname Then
  113. this.open(user, password)
  114. End If
  115. Return this
  116. End Function
  117. Method close()
  118. clearQueries()
  119. If _isOpen Then
  120. If sqlite3_close(handle) <> SQLITE_OK Then
  121. setError("Error closing database", Null, TDatabaseError.ERROR_CONNECTION)
  122. End If
  123. handle = Null
  124. _isOpen = False
  125. End If
  126. End Method
  127. Method commit:Int()
  128. If Not _isOpen Then
  129. Return False
  130. End If
  131. ' we need to ensure our queries are in a state that can be committed.
  132. resetQueries()
  133. Local query:TDatabaseQuery = executeQuery("COMMIT TRANSACTION")
  134. If hasError() Then
  135. setError("Error committing transaction", error().error, TDatabaseError.ERROR_TRANSACTION)
  136. Return False
  137. End If
  138. query.Free()
  139. Return True
  140. End Method
  141. Method getTables:String[]()
  142. Local list:String[]
  143. If Not _isOpen Then
  144. Return list
  145. End If
  146. Local tables:TList = New TList
  147. Local query:TDatabaseQuery = TDatabaseQuery.Create(Self)
  148. Local sql:String = "SELECT name FROM sqlite_master WHERE type = 'table' " + ..
  149. "UNION ALL SELECT name FROM sqlite_temp_master WHERE type = 'table'"
  150. If query.execute(sql) Then
  151. While query.nextRow()
  152. tables.addLast(query.value(0).getString())
  153. Wend
  154. End If
  155. If tables.count() > 0 Then
  156. list = New String[tables.count()]
  157. Local i:Int = 0
  158. For Local s:String = EachIn tables
  159. list[i] = s
  160. i:+ 1
  161. Next
  162. End If
  163. Return list
  164. End Method
  165. Method getTableInfo:TDBTable(tableName:String, withDDL:Int = False)
  166. If Not _isOpen Then
  167. Return Null
  168. End If
  169. Local query:TDatabaseQuery = TDatabaseQuery.Create(Self)
  170. Local table:TDBTable
  171. Local sql:String = "PRAGMA table_info(" + tableName + ")"
  172. If query.execute(sql) Then
  173. table = New TDBTable
  174. table.name = tableName
  175. Local cols:TList = New TList
  176. For Local rec:TQueryRecord = EachIn query
  177. Local name:String = rec.GetString(1)
  178. Local dbType:Int = TSQLiteResultSet.dbTypeFromNative(rec.GetString(2))
  179. Local nullable:Int = rec.GetInt(3)
  180. Local defaultValue:TDBType = rec.value(4)
  181. cols.AddLast(TDBColumn.Create(name, dbType, nullable, defaultValue))
  182. Next
  183. table.SetCountColumns(cols.count())
  184. Local i:Int
  185. For Local col:TDBColumn = EachIn cols
  186. table.SetColumn(i, col)
  187. i:+ 1
  188. Next
  189. cols.Clear()
  190. If withDDL Then
  191. sql = "SELECT sql FROM sqlite_master WHERE Type = 'table' and name = '" + tableName + "'"
  192. If query.execute(sql) Then
  193. For Local rec:TQueryRecord = EachIn query
  194. table.ddl:+ rec.GetString(0) + ";~n~n"
  195. Next
  196. End If
  197. End If
  198. Else
  199. ' no table?
  200. End If
  201. Return table
  202. End Method
  203. Method open:Int(user:String = Null, pass:String = Null)
  204. ' close if the connection is already open
  205. If _isOpen Then
  206. close()
  207. End If
  208. Local s:Byte Ptr = _dbname.ToUTF8String()
  209. Local flags:Int = _options.ToInt()
  210. If Not flags Then
  211. flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
  212. End If
  213. Local ret:Int = sqlite3_open_v2(s, Varptr handle, flags, Null)
  214. MemFree(s)
  215. If ret = SQLITE_OK Then
  216. _isOpen = True
  217. ' authenticate
  218. If _user Then
  219. Local u:Byte Ptr = _user.ToUTF8String()
  220. Local p:Byte Ptr = _password.ToUTF8String()
  221. ret = sqlite3_user_authenticate(handle, u, p, _strlen(p))
  222. MemFree(p)
  223. MemFree(u)
  224. If ret <> SQLITE_OK Then
  225. setError("Error authenticating user", Null, TDatabaseError.ERROR_CONNECTION, ret)
  226. End If
  227. End If
  228. Return True
  229. Else
  230. setError("Error opening database", Null, TDatabaseError.ERROR_CONNECTION, ret)
  231. End If
  232. Return False
  233. End Method
  234. Method rollback:Int()
  235. If Not _isOpen Then
  236. Return False
  237. End If
  238. ' we need to ensure our queries are in a state that can be rolledback.
  239. resetQueries()
  240. Local query:TDatabaseQuery = executeQuery("ROLLBACK TRANSACTION")
  241. If hasError() Then
  242. setError("Error rolling back transaction", error().error, TDatabaseError.ERROR_TRANSACTION)
  243. Return False
  244. End If
  245. query.Free()
  246. Return True
  247. End Method
  248. Method startTransaction:Int()
  249. If Not _isOpen Then
  250. Return False
  251. End If
  252. Local query:TDatabaseQuery = executeQuery("BEGIN TRANSACTION")
  253. If hasError() Then
  254. setError("Error starting transaction", error().error, TDatabaseError.ERROR_TRANSACTION)
  255. Return False
  256. End If
  257. query.Free()
  258. Return True
  259. End Method
  260. Method databaseHandle:Byte Ptr()
  261. Return handle
  262. End Method
  263. Method createResultSet:TQueryResultSet()
  264. Return TSQLiteResultSet.Create(Self)
  265. End Method
  266. Method nativeErrorMessage:String(err:Int)
  267. Select err
  268. Case 0 Return "Successful result"
  269. Case 1 Return "SQL error Or missing database"
  270. Case 2 Return "An internal logic error in SQLite"
  271. Case 3 Return "Access permission denied"
  272. Case 4 Return "Callback routine requested an abort"
  273. Case 5 Return "The database file is locked"
  274. Case 6 Return "A table in the database is locked"
  275. Case 7 Return "A malloc() failed"
  276. Case 8 Return "Attempt To write a readonly database"
  277. Case 9 Return "Operation terminated by sqlite_interrupt()"
  278. Case 10 Return "Some kind of disk I/O error occurred"
  279. Case 11 Return "The database disk image is malformed"
  280. Case 12 Return "(Internal Only) Table Or record Not found"
  281. Case 13 Return "Insertion failed because database is full"
  282. Case 14 Return "Unable To open the database file"
  283. Case 15 Return "Database lock protocol error"
  284. Case 16 Return "(Internal Only) Database table is empty"
  285. Case 17 Return "The database schema changed"
  286. Case 18 Return "Too much data For one row of a table"
  287. Case 19 Return "Abort due To constraint violation"
  288. Case 20 Return "Data Type mismatch"
  289. Case 21 Return "Library used incorrectly"
  290. Case 22 Return "Uses OS features Not supported on host"
  291. Case 23 Return "Authorization denied"
  292. Case 25 Return "2nd parameter to sqlite3_bind out of range"
  293. Case 26 Return "File opened that is not a database file"
  294. Case 27 Return "Notifications from sqlite3_log()"
  295. Case 28 Return "Warnings from sqlite3_log()"
  296. Case 100 Return "sqlite_step() has another row ready"
  297. Case 101 Return "sqlite_step() has finished executing"
  298. Default Return "Error " + err
  299. End Select
  300. End Method
  301. Method hasPrepareSupport:Int()
  302. Return True
  303. End Method
  304. Method hasTransactionSupport:Int()
  305. Return True
  306. End Method
  307. Method clearQueries()
  308. For Local i:Int = 0 Until queries.length
  309. Local q:TSQLiteResultSet = queries[i]
  310. If q Then
  311. q.free()
  312. End If
  313. Next
  314. End Method
  315. Method resetQueries()
  316. For Local q:TSQLiteResultSet = EachIn queries
  317. If q Then
  318. q.reset()
  319. End If
  320. Next
  321. End Method
  322. Method addQuery(query:TSQLiteResultSet)
  323. Local firstFree:Int = -1
  324. For Local i:Int = 0 Until queries.length
  325. Local q:TSQLiteResultSet = queries[i]
  326. If Not q And firstFree < 0 Then
  327. firstFree = i
  328. Else If queries[i] = query Then
  329. Return
  330. End If
  331. Next
  332. If firstFree >= 0 Then
  333. queries[firstFree] = query
  334. Else
  335. queries :+ [query]
  336. End If
  337. End Method
  338. Method removeQuery(query:TSQLiteResultSet)
  339. For Local i:Int = 0 Until queries.length
  340. If queries[i] = query Then
  341. queries[i] = Null
  342. Exit
  343. End If
  344. Next
  345. End Method
  346. Method addUser(username:String, password:String, isAdmin:Int = False)
  347. If username Then
  348. Local n:Byte Ptr = username.ToUTF8String()
  349. Local p:Byte Ptr
  350. Local plen:Int
  351. If password Then
  352. p = password.ToUTF8String()
  353. plen = _strlen(p)
  354. End If
  355. Local res:Int = sqlite3_user_add(handle, n, p, plen, isAdmin)
  356. If p Then
  357. MemFree(p)
  358. End If
  359. MemFree(n)
  360. If res <> SQLITE_OK Then
  361. setError("Error adding user", Null, TDatabaseError.ERROR_CONNECTION, res)
  362. End If
  363. End If
  364. End Method
  365. Method modifyUser(username:String, password:String, isAdmin:Int = False)
  366. If username Then
  367. Local n:Byte Ptr = username.ToUTF8String()
  368. Local p:Byte Ptr
  369. Local plen:Int
  370. If password Then
  371. p = password.ToUTF8String()
  372. plen = _strlen(p)
  373. End If
  374. Local res:Int = sqlite3_user_change(handle, n, p, plen, isAdmin)
  375. If p Then
  376. MemFree(p)
  377. End If
  378. MemFree(n)
  379. If res <> SQLITE_OK Then
  380. setError("Error changing user", Null, TDatabaseError.ERROR_CONNECTION, res)
  381. End If
  382. End If
  383. End Method
  384. Method deleteUser(username:String)
  385. If username Then
  386. Local n:Byte Ptr = username.ToUTF8String()
  387. Local res:Int = sqlite3_user_delete(handle, n)
  388. MemFree(n)
  389. If res <> SQLITE_OK Then
  390. setError("Error deleting user", Null, TDatabaseError.ERROR_CONNECTION, res)
  391. End If
  392. EndIf
  393. End Method
  394. End Type
  395. Type TSQLiteResultSet Extends TQueryResultSet
  396. Field initialFetch:Int = True
  397. Field fakeFirstRowFetch:Int
  398. Method free()
  399. TDBSQLite(conn).removeQuery(Self)
  400. If stmtHandle Then
  401. sqlite3_finalize(stmtHandle)
  402. stmtHandle = Null
  403. End If
  404. End Method
  405. Method Delete()
  406. free()
  407. End Method
  408. Method reset()
  409. initialFetch = True
  410. index = SQL_BeforeFirstRow
  411. If stmtHandle Then
  412. sqlite3_reset(stmtHandle)
  413. End If
  414. End Method
  415. Function Create:TQueryResultSet(db:TDBConnection, sql:String = Null)
  416. Local this:TSQLiteResultSet = New TSQLiteResultSet
  417. this.init(db, sql)
  418. this.rec = TQueryRecord.Create()
  419. TDBSQLite(this.conn).addQuery(this)
  420. Return this
  421. End Function
  422. Method executeQuery:Int(statement:String)
  423. If Not prepare(statement) Then
  424. Return False
  425. End If
  426. Return execute()
  427. End Method
  428. Method cleanup()
  429. index = SQL_beforeFirstRow
  430. initialFetch = True
  431. clear()
  432. free()
  433. End Method
  434. Method prepare:Int(stmt:String)
  435. If Not conn Or Not conn.isOpen() Then
  436. Return False
  437. End If
  438. cleanup()
  439. TDBSQLite(conn).addQuery(Self)
  440. ' set the query if not set already
  441. If Not query Then
  442. query = stmt
  443. End If
  444. Local q:Byte Ptr = query.ToUTF8String()
  445. Local result:Int = sqlite3_prepare_v2(TDBSQLite(conn).handle, q, _strlen(q) , Varptr stmtHandle, 0)
  446. MemFree(q)
  447. If result <> SQLITE_OK Then
  448. conn.setError("Error preparing statement", String.FromUTF8String(sqlite3_errmsg(TDBSQLite(conn).handle)), TDatabaseError.ERROR_STATEMENT, result)
  449. free()
  450. Return False
  451. End If
  452. Return True
  453. End Method
  454. Method execute:Int()
  455. fakeFirstRowFetch = False
  456. Local result:Int = sqlite3_reset(stmtHandle)
  457. If result <> SQLITE_OK Then
  458. conn.setError("Error resetting statement", String.FromUTF8String(sqlite3_errmsg(TDBSQLite(conn).handle)), TDatabaseError.ERROR_STATEMENT, result)
  459. free()
  460. Return False
  461. End If
  462. ' BIND stuff
  463. Local values:TDBType[] = boundValues
  464. Local paramCount:Int = sqlite3_bind_parameter_count(stmtHandle)
  465. If paramCount = bindCount Then
  466. For Local i:Int = 0 Until paramCount
  467. ' reset error-state flag
  468. result = SQLITE_OK
  469. If Not values[i] Or values[i].isNull() Then
  470. result = sqlite3_bind_null(stmtHandle, i + 1)
  471. Else
  472. Select values[i].kind()
  473. Case DBTYPE_INT
  474. result = sqlite3_bind_int(stmtHandle, i + 1, values[i].getInt())
  475. Case DBTYPE_FLOAT
  476. result = sqlite3_bind_double(stmtHandle, i + 1, values[i].getFloat())
  477. Case DBTYPE_DOUBLE
  478. result = sqlite3_bind_double(stmtHandle, i + 1, values[i].getDouble())
  479. Case DBTYPE_LONG
  480. result = sqlite3_bind_int64(stmtHandle, i + 1, values[i].getLong())
  481. Case DBTYPE_STRING
  482. Local s:Byte Ptr = values[i].getString().ToUTF8String()
  483. result = bmx_sqlite3_bind_text64(stmtHandle, i + 1, s, _strlen(s), -1)
  484. MemFree(s)
  485. Case DBTYPE_BLOB
  486. result = bmx_sqlite3_bind_blob64(stmtHandle, i + 1, values[i].getBlob(), values[i].size(), 0)
  487. Case DBTYPE_DATE, DBTYPE_DATETIME, DBTYPE_TIME
  488. Local s:Byte Ptr = values[i].getString().ToUTF8String()
  489. result = bmx_sqlite3_bind_text64(stmtHandle, i + 1, s, _strlen(s), -1)
  490. MemFree(s)
  491. End Select
  492. End If
  493. If result <> SQLITE_OK Then
  494. sqlite3_reset(stmtHandle)
  495. conn.setError("Failed to bind parameter (" + i + ")", String.FromUTF8String(sqlite3_errmsg(TDBSQLite(conn).handle)), TDatabaseError.ERROR_STATEMENT, result)
  496. free()
  497. Return False
  498. End If
  499. Next
  500. Else
  501. conn.setError("Parameter count mismatch", Null, TDatabaseError.ERROR_STATEMENT, 0)
  502. free()
  503. Return False
  504. End If
  505. ' we need to pre-fetch the first row to get the field information
  506. nextRow()
  507. If conn.error().isSet() Then
  508. _isActive = False
  509. Return False
  510. End If
  511. _isActive = True
  512. Return True
  513. End Method
  514. Method firstRow:Int()
  515. If index = SQL_BeforeFirstRow Then
  516. Return nextRow()
  517. End If
  518. Return False
  519. End Method
  520. Method nextRow:Int()
  521. If fakeFirstRowFetch Then
  522. fakeFirstRowFetch = False
  523. Return True
  524. End If
  525. If initialFetch Then
  526. fakeFirstRowFetch = True
  527. initialFetch = False
  528. End If
  529. Local result:Int = sqlite3_step(stmtHandle)
  530. Select result
  531. Case SQLITE_ROW
  532. If rec.isEmpty() Then
  533. initRecord()
  534. resetValues(rec.count())
  535. End If
  536. For Local i:Int = 0 Until rec.count()
  537. If values[i] Then
  538. values[i].clear()
  539. End If
  540. Select sqlite3_column_type(stmtHandle, i)
  541. Case SQLITE_INTEGER
  542. values[i] = New TDBLong
  543. Local lvalue:Long
  544. bmx_sqlite3_column_int64(stmtHandle, i, Varptr lvalue)
  545. values[i].setLong(lvalue)
  546. Case SQLITE_FLOAT
  547. values[i] = New TDBDouble
  548. values[i].setDouble(sqlite3_column_double(stmtHandle, i))
  549. Case SQLITE_NULL
  550. Case SQLITE_BLOB
  551. values[i] = New TDBBlob
  552. values[i].setBlob(sqlite3_column_blob(stmtHandle, i), sqlite3_column_bytes(stmtHandle, i))
  553. Default
  554. values[i] = New TDBString
  555. values[i].setString(sizedUTF8toISO8859(sqlite3_column_text(stmtHandle, i), sqlite3_column_bytes(stmtHandle, i)))
  556. End Select
  557. Next
  558. index:+ 1
  559. Return True
  560. Case SQLITE_DONE
  561. If rec.isEmpty() Then
  562. initRecord()
  563. resetValues(rec.count())
  564. rec.SetIsEmptySet()
  565. End If
  566. ' prevent NextRow() returning True on the first fetch - since there are no rows.
  567. fakeFirstRowFetch = False
  568. sqlite3_reset(stmtHandle)
  569. Return False
  570. Default
  571. sqlite3_reset(stmtHandle)
  572. ' raise an error!
  573. conn.setError(String.FromUTF8String(sqlite3_errmsg(TDBSQLite(conn).handle)), Null, TDatabaseError.ERROR_STATEMENT, result)
  574. Return False
  575. End Select
  576. Return False
  577. End Method
  578. Method initRecord()
  579. rec.clear()
  580. Local colCount:Int = sqlite3_column_count(stmtHandle)
  581. If colCount <= 0 Then
  582. Return
  583. End If
  584. rec.init(colCount)
  585. For Local i:Int = 0 Until colCount
  586. Local columnName:String = String.FromUTF8String(sqlite3_column_name(stmtHandle, i))
  587. Local tn:Byte Ptr = sqlite3_column_decltype(stmtHandle, i)
  588. Local typeName:String
  589. If tn Then
  590. typeName = String.FromUTF8String(tn)
  591. End If
  592. Local dotPosition:Int = columnName.findLast(".") + 1
  593. rec.setField(i, TQueryField.Create(columnName[dotPosition..], dbTypeFromNative(typeName)))
  594. Next
  595. End Method
  596. Function dbTypeFromNative:Int(name:String, _type:Int = 0, _flags:Int = 0)
  597. name = name.ToLower()
  598. If name.startsWith("numeric") Then
  599. Return DBTYPE_DOUBLE
  600. End If
  601. Select name
  602. Case "integer"
  603. Return DBTYPE_LONG
  604. Case "int"
  605. Return DBTYPE_LONG
  606. Case "double"
  607. Return DBTYPE_DOUBLE
  608. Case "float"
  609. Return DBTYPE_DOUBLE
  610. Case "blob"
  611. Return DBTYPE_BLOB
  612. Default
  613. Return DBTYPE_STRING
  614. End Select
  615. End Function
  616. Method lastInsertedId:Long()
  617. If isActive() Then
  618. Local id:Long
  619. bmx_sqlite3_last_insert_rowid(conn.handle, Varptr id)
  620. Return id
  621. End If
  622. End Method
  623. Method rowsAffected:Int()
  624. If isActive() Then
  625. Return sqlite3_changes(conn.handle)
  626. End If
  627. Return -1
  628. End Method
  629. End Type
  630. Rem
  631. bbdoc: Loads an SQLite database from file into an already open in-memory database, or saves an in-memory database to a file.
  632. End Rem
  633. Function loadOrSaveDB:Int(inMemory:TDBSQLite, filename:String, isSave:Int, database:String = "main")
  634. Local db:TDBSQLite = TDBSQLite(TDBSQLite.Create(filename))
  635. Local rc:Int
  636. If db.isOpen() Then
  637. Local toHandle:Byte Ptr
  638. Local fromHandle:Byte Ptr
  639. If isSave Then
  640. fromHandle = inMemory.handle
  641. toHandle = db.handle
  642. Else
  643. fromHandle = db.handle
  644. toHandle = inMemory.handle
  645. End If
  646. Local d:Byte Ptr = database.ToUTF8String()
  647. Local backup:Byte Ptr = sqlite3_backup_init(toHandle, d, fromHandle, d)
  648. If backup Then
  649. sqlite3_backup_step(backup, -1)
  650. sqlite3_backup_finish(backup)
  651. End If
  652. rc = sqlite3_errcode(toHandle)
  653. db.close()
  654. Else
  655. If db.hasError() Then
  656. rc = db.error().errorValue
  657. End If
  658. End If
  659. Return rc
  660. End Function
  661. Type TSQLiteDatabaseLoader Extends TDatabaseLoader
  662. Method New()
  663. _type = "SQLITE"
  664. End Method
  665. Method LoadDatabase:TDBConnection( dbname:String = Null, host:String = Null, ..
  666. port:Int = Null, user:String = Null, password:String = Null, ..
  667. server:String = Null, options:String = Null )
  668. Return TDBSQLite.Create(dbName, host, port, user, password, server, options)
  669. End Method
  670. End Type
  671. AddDatabaseLoader New TSQLiteDatabaseLoader