mariadb.bmx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. ' Copyright (c) 2007-2022 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 - MariaDB
  29. about: A MariaDB database driver for #Database.Core
  30. End Rem
  31. Module Database.MariaDB
  32. ModuleInfo "Version: 1.01"
  33. ModuleInfo "Author: Bruce A Henderson"
  34. ModuleInfo "License: BSD"
  35. ModuleInfo "Copyright: Wrapper - 2007-2022 Bruce A Henderson"
  36. ModuleInfo "History: 1.01"
  37. ModuleInfo "History: Fixed setting Null binds"
  38. ModuleInfo "History: 1.00 Initial Release"
  39. ?win32x86
  40. ModuleInfo "LD_OPTS: -L%PWD%/lib/win32x86"
  41. ?win32x64
  42. ModuleInfo "LD_OPTS: -L%PWD%/lib/win32x64"
  43. ?win32arm
  44. ModuleInfo "LD_OPTS: -L%PWD%/lib/win32arm"
  45. ?win32arm64
  46. ModuleInfo "LD_OPTS: -L%PWD%/lib/win32arm64"
  47. ?macos
  48. ModuleInfo "CC_OPTS: `pkg-config --cflags libmariadb`"
  49. ModuleInfo "LD_OPTS: `pkg-config --libs-only-L libmariadb`"
  50. ?linux
  51. ModuleInfo "CC_OPTS: `pkg-config --cflags libmariadb`"
  52. ModuleInfo "LD_OPTS: `pkg-config --libs-only-L libmariadb`"
  53. ?
  54. Import "common.bmx"
  55. Type TDBMariaDB Extends TDBConnection
  56. Field clientVersion:Int
  57. Field serverVersion:Int
  58. Function Create:TDBConnection(dbname:String = Null, host:String = Null, ..
  59. port:Int = Null, user:String = Null, password:String = Null, server:String = Null, options:String = Null)
  60. Local this:TDBMariaDB = New TDBMariaDB
  61. this.init(dbname, host, port, user, password, server, options)
  62. this.clientVersion = mysql_get_client_version()
  63. If this._dbname Then
  64. this.open(user, password)
  65. End If
  66. Return this
  67. End Function
  68. Method close()
  69. If _isOpen Then
  70. free() ' tidy up queries and stuff
  71. mysql_close(handle)
  72. handle = Null
  73. _isOpen = False
  74. End If
  75. End Method
  76. Method isOpen:Int()
  77. If _isOpen Then
  78. ' really check that the database is open
  79. If mysql_ping(handle) Then
  80. setError("Connection has closed", Null, TDatabaseError.ERROR_CONNECTION, mysql_errno(handle))
  81. _isOpen = False
  82. End If
  83. End If
  84. Return _isOpen
  85. End Method
  86. Method commit:Int()
  87. If Not _isOpen Then
  88. Return False
  89. End If
  90. If mysql_query(handle, "COMMIT") Then
  91. setError("Error committing transaction", Null, TDatabaseError.ERROR_TRANSACTION, mysql_errno(handle))
  92. Return False
  93. End If
  94. Return True
  95. End Method
  96. Method getTables:String[]()
  97. Local list:String[]
  98. If Not _isOpen Then
  99. Return list
  100. End If
  101. Local tableList:TList = New TList
  102. Local tablesHandle:Byte Ptr = mysql_list_tables(handle, Null)
  103. If tablesHandle Then
  104. Local row:Byte Ptr = mysql_fetch_row(tablesHandle)
  105. While row
  106. Local s:String = String.FromUTF8String(bmx_mysql_rowField_chars(row, 0))
  107. tableList.addLast(s)
  108. row = mysql_fetch_row(tablesHandle)
  109. Wend
  110. mysql_free_result(tablesHandle)
  111. End If
  112. If tableList.count() > 0 Then
  113. list = New String[tableList.count()]
  114. Local i:Int = 0
  115. For Local s:String = EachIn tableList
  116. list[i] = s
  117. i:+ 1
  118. Next
  119. End If
  120. Return list
  121. End Method
  122. Method getTableInfo:TDBTable(tableName:String, withDDL:Int = False)
  123. If Not _isOpen Then
  124. Return Null
  125. End If
  126. Local query:TDatabaseQuery = TDatabaseQuery.Create(Self)
  127. Local table:TDBTable
  128. Local sql:String = "SHOW COLUMNS FROM " + tableName
  129. If query.execute(sql) Then
  130. table = New TDBTable
  131. table.name = tableName
  132. Local cols:TList = New TList
  133. For Local rec:TQueryRecord = EachIn query
  134. Local name:String = rec.GetString(0)
  135. Local _type:String = rec.GetString(1).Split("(")[0]
  136. Local dbType:Int
  137. Select _type
  138. Case "boolean", "bool", "tinyint", "smallint", "mediumint", "int", "integer"
  139. dbType = DBTYPE_INT
  140. Case "bigint"
  141. dbType = DBTYPE_LONG
  142. Case "real", "double", "decimal"
  143. dbType = DBTYPE_DOUBLE
  144. Case "float"
  145. dbType = DBTYPE_FLOAT
  146. Case "date"
  147. dbType = DBTYPE_DATE
  148. Case "timestamp", "datetime"
  149. dbType = DBTYPE_DATETIME
  150. Case "time"
  151. dbType = DBTYPE_TIME
  152. Case "tinyblob", "blob", "mediumblob", "longblob"
  153. dbType = DBTYPE_BLOB
  154. Default
  155. dbType = DBTYPE_STRING
  156. End Select
  157. Local nullable:Int
  158. If rec.GetString(2) = "YES" Then
  159. nullable = True
  160. End If
  161. Local defaultValue:TDBType = rec.value(4)
  162. cols.AddLast(TDBColumn.Create(name, dbType, nullable, defaultValue))
  163. Next
  164. table.SetCountColumns(cols.count())
  165. Local i:Int
  166. For Local col:TDBColumn = EachIn cols
  167. table.SetColumn(i, col)
  168. i:+ 1
  169. Next
  170. cols.Clear()
  171. If withDDL Then
  172. sql = "SHOW CREATE TABLE " + tableName
  173. If query.execute(sql) Then
  174. For Local rec:TQueryRecord = EachIn query
  175. table.ddl:+ rec.GetString(1) + ";~n~n"
  176. Next
  177. End If
  178. End If
  179. Else
  180. ' no table?
  181. End If
  182. Return table
  183. End Method
  184. Method open:Int(user:String = Null, pass:String = Null)
  185. If _isOpen Then
  186. close()
  187. End If
  188. If user Then
  189. _user = user
  190. End If
  191. If pass Then
  192. _password = pass
  193. End If
  194. ' initialize the handle
  195. handle = mysql_init(0)
  196. If handle Then
  197. Local ret:Int = 0
  198. Local d:Byte Ptr = _dbname.ToUTF8String()
  199. If _host Then
  200. Local h:Byte Ptr = _host.ToUTF8String()
  201. If _user Then
  202. Local u:Byte Ptr = _user.ToUTF8String()
  203. If _password
  204. Local p:Byte Ptr = _password.ToUTF8String()
  205. ret = mysql_real_connect(handle, h, u, p, d, _port, Null, _options.ToInt())
  206. MemFree(p)
  207. Else
  208. ret = mysql_real_connect(handle, h, u, Null, d, _port, Null, _options.ToInt())
  209. End If
  210. MemFree(u)
  211. Else
  212. ret = mysql_real_connect(handle, h, Null, Null, d, _port, Null, _options.ToInt())
  213. End If
  214. MemFree(h)
  215. Else
  216. If _user Then
  217. Local u:Byte Ptr = _user.ToUTF8String()
  218. If _password
  219. Local p:Byte Ptr = _password.ToUTF8String()
  220. ret = mysql_real_connect(handle, Null, u, p, d, _port, Null, _options.ToInt())
  221. MemFree(p)
  222. Else
  223. ret = mysql_real_connect(handle, Null, u, Null, d, _port, Null, _options.ToInt())
  224. End If
  225. MemFree(u)
  226. Else
  227. ret = mysql_real_connect(handle, Null, Null, Null, d, _port, Null, _options.ToInt())
  228. End If
  229. End If
  230. If Not ret Then
  231. setError("Error connecting to database", String.FromUTF8String(mysql_error(handle)), TDatabaseError.ERROR_CONNECTION, mysql_errno(handle))
  232. Return False
  233. End If
  234. If mysql_select_db(handle, _dbname) Then
  235. setError("Error opening database '" + _dbname + "'", String.FromUTF8String(mysql_error(handle)), TDatabaseError.ERROR_CONNECTION, mysql_errno(handle))
  236. Return False
  237. End If
  238. Else
  239. setError("Error initializing database", Null, TDatabaseError.ERROR_CONNECTION, 0)
  240. Return False
  241. End If
  242. If clientVersion >= 50007 Then
  243. mysql_set_character_set(handle, "utf8")
  244. End If
  245. serverVersion = mysql_get_server_version(handle)
  246. _isOpen = True
  247. Return True
  248. End Method
  249. Method rollback:Int()
  250. If Not _isOpen Then
  251. Return False
  252. End If
  253. If mysql_query(handle, "ROLLBACK") Then
  254. setError("Error rolling back transaction", Null, TDatabaseError.ERROR_TRANSACTION, mysql_errno(handle))
  255. Return False
  256. End If
  257. Return True
  258. End Method
  259. Method startTransaction:Int()
  260. If Not _isOpen Then
  261. Return False
  262. End If
  263. If mysql_query(handle, "BEGIN WORK") Then
  264. setError("Error starting transaction", Null, TDatabaseError.ERROR_TRANSACTION, mysql_errno(handle))
  265. Return False
  266. End If
  267. Return True
  268. End Method
  269. Method databaseHandle:Byte Ptr()
  270. Return handle
  271. End Method
  272. Method createResultSet:TQueryResultSet()
  273. Return TMySQLResultSet.Create(Self)
  274. End Method
  275. Method nativeErrorMessage:String(err:Int)
  276. End Method
  277. Method hasPrepareSupport:Int()
  278. Return True
  279. End Method
  280. Method hasTransactionSupport:Int()
  281. Return True
  282. End Method
  283. End Type
  284. Type TMySQLField
  285. Field mySQLField:Byte Ptr
  286. Field dataValue:Byte Ptr
  287. Field dataLength:ULongInt
  288. Field isNull:Byte
  289. Field flag:Int
  290. Method clear()
  291. mySQLField = Null
  292. If dataValue Then
  293. MemFree(dataValue)
  294. dataValue = Null
  295. End If
  296. dataLength = 0
  297. isNull = 0
  298. End Method
  299. Method Delete()
  300. clear()
  301. End Method
  302. End Type
  303. Type TMySQLResultSet Extends TQueryResultSet
  304. ' a pointer to a mysql result
  305. Field resultHandle:Byte Ptr
  306. Field row:Byte Ptr
  307. ' a pointer to a mysql prepared statement
  308. Field stmtHandle:Byte Ptr
  309. Field metaHandle:Byte Ptr
  310. Field preparedQuery:Int
  311. Field _rowsAffected:Int
  312. Field parameterBindings:Byte Ptr
  313. Field selectBindings:Byte Ptr
  314. Field mySQLFields:TMySQLField[]
  315. Function Create:TQueryResultSet(db:TDBConnection, sql:String = Null)
  316. Local this:TMySQLResultSet = New TMySQLResultSet
  317. this.init(db, sql)
  318. this.rec = TQueryRecord.Create()
  319. Return this
  320. End Function
  321. Method executeQuery:Int(statement:String)
  322. If Not conn.isOpen() Then
  323. Return False
  324. End If
  325. preparedQuery = False
  326. Local q:Byte Ptr = statement.ToUTF8String()
  327. If mysql_real_query(conn.handle, q, _strlen(q)) Then
  328. conn.setError("Error executing query", String.FromUTF8String(mysql_error(conn.handle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(conn.handle))
  329. Return False
  330. End If
  331. resultHandle = mysql_store_result(conn.handle)
  332. If Not resultHandle And mysql_field_count(conn.handle) > 0 Then
  333. conn.setError("Error storing result set", String.FromUTF8String(mysql_error(conn.handle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(conn.handle))
  334. Return False
  335. End If
  336. Local fieldCount:Int = mysql_field_count(conn.handle)
  337. initRecord(fieldCount)
  338. Local af:Long
  339. bmx_mysql_affected_rows(conn.handle, Varptr af)
  340. _rowsAffected = af
  341. If fieldCount <> 0 Then
  342. For Local i:Int = 0 Until fieldCount
  343. Local _field:Byte Ptr = mysql_fetch_field_direct(resultHandle, i)
  344. Local qf:TQueryField = TQueryField.Create(bmx_mysql_field_name(_field), dbTypeFromNative(Null, bmx_mysql_field_type(_field), bmx_mysql_field_flags(_field)))
  345. qf.length = bmx_mysql_field_length(_field)
  346. qf.precision = bmx_mysql_field_decimals(_field)
  347. rec.setField(i, qf)
  348. Next
  349. End If
  350. _isActive = True
  351. Return True
  352. End Method
  353. Method prepare:Int(statement:String)
  354. cleanup()
  355. If Not statement Or statement.length = 0 Then
  356. Return False
  357. End If
  358. ' initialize the statement if required
  359. If Not stmtHandle Then
  360. stmtHandle = mysql_stmt_init(conn.handle)
  361. End If
  362. If Not stmtHandle Then
  363. conn.setError("Error preparing statement", String.FromUTF8String(mysql_error(conn.handle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(conn.handle))
  364. Return False
  365. End If
  366. ' prepare the statement
  367. Local q:Byte Ptr = statement.ToUTF8String()
  368. Local result:Int = mysql_stmt_prepare(stmtHandle, q, _strlen(q))
  369. MemFree(q)
  370. If result Then
  371. conn.setError("Error preparing statement", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  372. cleanup()
  373. Return False
  374. End If
  375. ' if the param count > 0 there are "?" in the SQL that need to be bound
  376. If mysql_stmt_param_count(stmtHandle) > 0 Then
  377. parameterBindings = bmx_mysql_makeBindings(mysql_stmt_param_count(stmtHandle))
  378. End If
  379. ' **********************************
  380. ' setup bindings for inbound data...
  381. If Not metaHandle Then
  382. metaHandle = mysql_stmt_result_metadata(stmtHandle)
  383. End If
  384. If metaHandle Then
  385. Local fieldCount:Int = mysql_num_fields(metaHandle)
  386. initRecord(fieldCount)
  387. mySQLFields = New TMySQLField[fieldCount]
  388. selectBindings = bmx_mysql_makeBindings(fieldCount)
  389. For Local i:Int = 0 Until fieldCount
  390. Local _field:Byte Ptr = mysql_fetch_field(metaHandle)
  391. mySQLFields[i] = New TMySQLField
  392. mySQLFields[i].mySQLField = _field
  393. mySQLFields[i].dataLength = bmx_mysql_field_length(_field) + 1
  394. ' make some space for the data...
  395. Local size:Size_T = bmx_mysql_length_for_field(_field)
  396. mySQLFields[i].dataValue = MemAlloc(size)
  397. Local ty:Int = bmx_mysql_field_type(_field)
  398. ' build result set field information
  399. Local qf:TQueryField = TQueryField.Create(bmx_mysql_field_name(_field), dbTypeFromNative(Null, ty, bmx_mysql_field_flags(_field)))
  400. qf.length = bmx_mysql_field_length(_field)
  401. qf.precision = bmx_mysql_field_decimals(_field)
  402. rec.setField(i, qf)
  403. bmx_mysql_inbind(selectBindings, i, _field, mySQLFields[i].dataValue, Varptr mySQLFields[i].dataLength, Varptr mySQLFields[i].isNull, ty)
  404. Next
  405. End If
  406. Return True
  407. End Method
  408. Method execute:Int()
  409. If Not preparedQuery Then
  410. Return False
  411. End If
  412. If Not stmtHandle Then
  413. Return False
  414. End If
  415. index = SQL_BeforeFirstRow
  416. Local result:Int = 0
  417. result = bmx_mysql_stmt_reset(stmtHandle)
  418. If result Then
  419. conn.setError("Error resetting statement", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  420. Return False
  421. End If
  422. ' BIND stuff
  423. Local values:TDBType[] = boundValues
  424. Local paramCount:Int = mysql_stmt_param_count(stmtHandle)
  425. Local strings:Byte Ptr[]
  426. Local times:Byte Ptr[]
  427. Local nulls:Byte[]
  428. If paramCount = bindCount Then
  429. strings = New Byte Ptr[paramCount]
  430. times = New Byte Ptr[paramCount]
  431. nulls = New Byte[paramCount]
  432. For Local i:Int = 0 Until paramCount
  433. Local isNull:Int = False
  434. Local nullsPtr:Byte Ptr
  435. If Not values[i] Or values[i].isNull() Then
  436. isNull = True
  437. nulls[i] = 1
  438. nullsPtr = Byte Ptr(nulls) + i
  439. End If
  440. Select values[i].kind()
  441. Case DBTYPE_INT
  442. bmx_mysql_bind_int(parameterBindings, i, Varptr TDBInt(values[i]).value, nullsPtr)
  443. Case DBTYPE_FLOAT
  444. bmx_mysql_bind_float(parameterBindings, i, Varptr TDBFloat(values[i]).value, nullsPtr)
  445. Case DBTYPE_DOUBLE
  446. bmx_mysql_bind_double(parameterBindings, i, Varptr TDBDouble(values[i]).value, nullsPtr)
  447. Case DBTYPE_LONG
  448. bmx_mysql_bind_long(parameterBindings, i, Varptr TDBLong(values[i]).value, nullsPtr)
  449. Case DBTYPE_STRING
  450. local s:Byte Ptr = values[i].getString().ToUTF8String()
  451. strings[i] = s
  452. bmx_mysql_bind_string(parameterBindings, i, s, _strlen(s), nullsPtr)
  453. Case DBTYPE_BLOB
  454. bmx_mysql_bind_blob(parameterBindings, i, TDBBlob(values[i]).value, TDBBlob(values[i])._size, nullsPtr)
  455. Case DBTYPE_DATE
  456. Local date:TDBDate = TDBDate(values[i])
  457. times[i] = bmx_mysql_makeTime()
  458. bmx_mysql_bind_date(parameterBindings, i, times[i], date.getYear(), date.getMonth(), date.getDay(), nullsPtr)
  459. Case DBTYPE_DATETIME
  460. Local date:TDBDateTime = TDBDateTime(values[i])
  461. times[i] = bmx_mysql_makeTime()
  462. bmx_mysql_bind_datetime(parameterBindings, i, times[i], date.getYear(), date.getMonth(), date.getDay(), date.getHour(), date.getMinute(), date.getSecond(), nullsPtr)
  463. Case DBTYPE_TIME
  464. Local date:TDBTime = TDBTime(values[i])
  465. times[i] = bmx_mysql_makeTime()
  466. bmx_mysql_bind_time(parameterBindings, i, times[i], date.getHour(), date.getMinute(), date.getSecond(), nullsPtr)
  467. End Select
  468. Next
  469. ' actually bind the parameters
  470. result = bmx_mysql_stmt_bind_param(stmtHandle, parameterBindings)
  471. If result Then
  472. conn.setError("Error binding values", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  473. ' free up the strings
  474. For Local i:Int = 0 Until paramCount
  475. If strings[i] Then
  476. MemFree(strings[i])
  477. End If
  478. If times[i] Then
  479. bmx_mysql_deleteTime(times[i])
  480. End If
  481. Next
  482. Return False
  483. End If
  484. End If
  485. ' execute the statement
  486. result = mysql_stmt_execute(stmtHandle)
  487. ' free up the strings
  488. If strings Or times Then
  489. For Local i:Int = 0 Until paramCount
  490. If strings[i] Then
  491. MemFree(strings[i])
  492. End If
  493. If times[i] Then
  494. bmx_mysql_deleteTime(times[i])
  495. End If
  496. Next
  497. End If
  498. If result Then
  499. conn.setError("Error executing statement", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  500. Return False
  501. End If
  502. Local af:Long
  503. bmx_mysql_stmt_affected_rows(stmtHandle, Varptr af)
  504. _rowsAffected = af
  505. ' if this is set, then there is data returned from the statement execution
  506. ' in which case we need to bind the results for the result set
  507. If metaHandle Then
  508. result = bmx_mysql_stmt_bind_result(stmtHandle, selectBindings)
  509. If result Then
  510. conn.setError("Error binding result", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  511. Return False
  512. End If
  513. result = mysql_stmt_store_result(stmtHandle)
  514. If result Then
  515. conn.setError("Error storing result", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  516. Return False
  517. End If
  518. End If
  519. _isActive = True
  520. Return True
  521. End Method
  522. Method initRecord(size:Int)
  523. rec.clear()
  524. If size > 0 Then
  525. rec.init(size)
  526. End If
  527. resetValues(size)
  528. End Method
  529. Method firstRow:Int()
  530. If index = SQL_BeforeFirstRow Then
  531. Return nextRow()
  532. End If
  533. Return False
  534. End Method
  535. Method nextRow:Int()
  536. If preparedQuery Then
  537. If Not stmtHandle Then
  538. Return False
  539. End If
  540. Local result:Int = bmx_mysql_stmt_fetch(stmtHandle)
  541. If result Then
  542. Return False
  543. End If
  544. Else
  545. row = mysql_fetch_row(resultHandle)
  546. If Not row Then
  547. Return False
  548. End If
  549. End If
  550. ' now populate the values[] array with the fetched data !
  551. For Local i:Int = 0 Until rec.count()
  552. If values[i] Then
  553. values[i].clear()
  554. End If
  555. If preparedQuery Then
  556. If Not mySQLFields[i].isNull Then
  557. Local fieldLength:Int = mySQLFields[i].dataLength
  558. ' it seems that we need to retrieve all values as if they were "strings"...
  559. ' Don't ask... it doesn't work otherwise. (except on Windows... haw)
  560. Select rec.fields[i].fType
  561. Case DBTYPE_INT
  562. values[i] = New TDBInt
  563. values[i].setInt(bmx_mysql_char_to_int(mySQLFields[i].dataValue))
  564. Case DBTYPE_LONG
  565. values[i] = New TDBLong
  566. values[i].setLong(bmx_mysql_char_to_long(mySQLFields[i].dataValue))
  567. Case DBTYPE_FLOAT
  568. values[i] = New TDBFloat
  569. values[i].setFloat(bmx_mysql_char_to_float(mySQLFields[i].dataValue))
  570. Case DBTYPE_DOUBLE
  571. values[i] = New TDBDouble
  572. values[i].setDouble(bmx_mysql_char_to_double(mySQLFields[i].dataValue))
  573. Case DBTYPE_DATE
  574. values[i] = bmx_mysql_char_to_date(mySQLFields[i].dataValue)
  575. Case DBTYPE_DATETIME
  576. values[i] = bmx_mysql_char_to_datetime(mySQLFields[i].dataValue)
  577. Case DBTYPE_TIME
  578. values[i] = bmx_mysql_char_to_time(mySQLFields[i].dataValue)
  579. Case DBTYPE_BLOB
  580. values[i] = TDBBlob.Set(mySQLFields[i].dataValue, fieldLength)
  581. Default
  582. values[i] = New TDBString
  583. values[i].setString(sizedUTF8toISO8859(mySQLFields[i].dataValue, fieldLength))
  584. End Select
  585. End If
  586. Else
  587. ' a non-prepared query
  588. If Not bmx_mysql_rowField_isNull(row, i) Then
  589. Local fieldLength:Int = Int(mysql_fetch_lengths(resultHandle)[i])
  590. Select rec.fields[i].fType
  591. Case DBTYPE_INT
  592. values[i] = New TDBInt
  593. values[i].setInt(String.fromBytes(bmx_mysql_rowField_chars(row, i), fieldLength).toInt())
  594. Case DBTYPE_LONG
  595. values[i] = New TDBLong
  596. values[i].setLong(String.fromBytes(bmx_mysql_rowField_chars(row, i), fieldLength).toLong())
  597. Case DBTYPE_FLOAT
  598. values[i] = New TDBFloat
  599. values[i].setFloat(String.fromBytes(bmx_mysql_rowField_chars(row, i), fieldLength).toFloat())
  600. Case DBTYPE_DOUBLE
  601. values[i] = New TDBDouble
  602. values[i].setDouble(String.fromBytes(bmx_mysql_rowField_chars(row, i), fieldLength).toDouble())
  603. Case DBTYPE_DATE
  604. values[i] = TDBDate.SetFromString(String.FromUTF8Bytes(bmx_mysql_rowField_chars(row, i), fieldLength))
  605. Case DBTYPE_DATETIME
  606. values[i] = TDBDateTime.SetFromString(String.FromUTF8Bytes(bmx_mysql_rowField_chars(row, i), fieldLength))
  607. Case DBTYPE_TIME
  608. values[i] = TDBTime.SetFromString(String.FromUTF8Bytes(bmx_mysql_rowField_chars(row, i), fieldLength))
  609. Case DBTYPE_BLOB
  610. values[i] = TDBBlob.Set(bmx_mysql_rowField_chars(row, i), fieldLength)
  611. Default
  612. values[i] = New TDBString
  613. values[i].setString(String.FromUTF8Bytes(bmx_mysql_rowField_chars(row, i), fieldLength))
  614. End Select
  615. End If
  616. End If
  617. Next
  618. index:+ 1
  619. Return True
  620. End Method
  621. Method lastInsertedId:Long()
  622. If Not isActive()
  623. Return -1
  624. End If
  625. Local id:Long = -1
  626. If preparedQuery Then
  627. bmx_mysql_stmt_insert_id(stmtHandle, Varptr id)
  628. Else
  629. bmx_mysql_insert_id(conn.handle, Varptr id)
  630. End If
  631. Return id
  632. End Method
  633. Method rowsAffected:Int()
  634. Return _rowsAffected
  635. End Method
  636. Function dbTypeFromNative:Int(name:String, _type:Int = 0, _flags:Int = 0)
  637. Local dbType:Int
  638. Select _type
  639. Case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, MYSQL_TYPE_INT24
  640. dbType = DBTYPE_INT
  641. Case MYSQL_TYPE_YEAR
  642. dbType = DBTYPE_INT
  643. Case MYSQL_TYPE_LONGLONG
  644. dbType = DBTYPE_LONG
  645. Case MYSQL_TYPE_FLOAT
  646. dbType = DBTYPE_FLOAT
  647. Case MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DECIMAL
  648. dbType = DBTYPE_DOUBLE
  649. Case MYSQL_TYPE_DATE
  650. dbType = DBTYPE_DATE
  651. Case MYSQL_TYPE_TIME
  652. dbType = DBTYPE_TIME
  653. Case MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIMESTAMP
  654. dbType = DBTYPE_DATETIME
  655. Case MYSQL_TYPE_BLOB, MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB
  656. If _flags & 128 Then ' binary !
  657. dbType = DBTYPE_BLOB
  658. Else ' String!
  659. dbType = DBTYPE_STRING
  660. End If
  661. Default
  662. dbType = DBTYPE_STRING
  663. End Select
  664. Return dbType
  665. End Function
  666. Method cleanup()
  667. If resultHandle Then
  668. mysql_free_result(resultHandle)
  669. resultHandle = Null
  670. End If
  671. If metaHandle Then
  672. mysql_free_result(metaHandle)
  673. metaHandle = Null
  674. End If
  675. If stmtHandle Then
  676. If bmx_mysql_stmt_close(stmtHandle) Then
  677. '
  678. End If
  679. stmtHandle = Null
  680. End If
  681. If parameterBindings Then
  682. bmx_mysql_deleteBindings(parameterBindings)
  683. parameterBindings = Null
  684. End If
  685. If selectBindings Then
  686. bmx_mysql_deleteBindings(selectBindings)
  687. selectBindings = Null
  688. End If
  689. If mySQLFields Then
  690. For Local i:Int = 0 Until mySQLFields.length
  691. If mySQLFields[i] Then
  692. mySQLFields[i].clear()
  693. mySQLFields[i] = Null
  694. End If
  695. Next
  696. End If
  697. index = SQL_BeforeFirstRow
  698. rec.clear()
  699. _isActive = False
  700. preparedQuery = True
  701. End Method
  702. Method clear()
  703. cleanup()
  704. Super.clear()
  705. End Method
  706. Method reset()
  707. clear()
  708. End Method
  709. Method free()
  710. clear()
  711. End Method
  712. Method Delete()
  713. free()
  714. End Method
  715. End Type
  716. Type TMariaDBDatabaseLoader Extends TDatabaseLoader
  717. Method New()
  718. _type = "MARIADB"
  719. End Method
  720. Method LoadDatabase:TDBConnection( dbname:String = Null, host:String = Null, ..
  721. port:Int = Null, user:String = Null, password:String = Null, ..
  722. server:String = Null, options:String = Null )
  723. Return TDBMariaDB.Create(dbName, host, port, user, password, server, options)
  724. End Method
  725. End Type
  726. AddDatabaseLoader New TMariaDBDatabaseLoader