mariadb.bmx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  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. Local query:Int = mysql_real_query(conn.handle, q, _strlen(q)) Then
  328. MemFree(q)
  329. If query
  330. conn.setError("Error executing query", String.FromUTF8String(mysql_error(conn.handle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(conn.handle))
  331. Return False
  332. End If
  333. resultHandle = mysql_store_result(conn.handle)
  334. If Not resultHandle And mysql_field_count(conn.handle) > 0 Then
  335. conn.setError("Error storing result set", String.FromUTF8String(mysql_error(conn.handle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(conn.handle))
  336. Return False
  337. End If
  338. Local fieldCount:Int = mysql_field_count(conn.handle)
  339. initRecord(fieldCount)
  340. Local af:Long
  341. bmx_mysql_affected_rows(conn.handle, Varptr af)
  342. _rowsAffected = af
  343. If fieldCount <> 0 Then
  344. For Local i:Int = 0 Until fieldCount
  345. Local _field:Byte Ptr = mysql_fetch_field_direct(resultHandle, i)
  346. Local qf:TQueryField = TQueryField.Create(bmx_mysql_field_name(_field), dbTypeFromNative(Null, bmx_mysql_field_type(_field), bmx_mysql_field_flags(_field)))
  347. qf.length = bmx_mysql_field_length(_field)
  348. qf.precision = bmx_mysql_field_decimals(_field)
  349. rec.setField(i, qf)
  350. Next
  351. End If
  352. _isActive = True
  353. Return True
  354. End Method
  355. Method prepare:Int(statement:String)
  356. cleanup()
  357. If Not statement Or statement.length = 0 Then
  358. Return False
  359. End If
  360. ' initialize the statement if required
  361. If Not stmtHandle Then
  362. stmtHandle = mysql_stmt_init(conn.handle)
  363. End If
  364. If Not stmtHandle Then
  365. conn.setError("Error preparing statement", String.FromUTF8String(mysql_error(conn.handle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(conn.handle))
  366. Return False
  367. End If
  368. ' prepare the statement
  369. Local q:Byte Ptr = statement.ToUTF8String()
  370. Local result:Int = mysql_stmt_prepare(stmtHandle, q, _strlen(q))
  371. MemFree(q)
  372. If result Then
  373. conn.setError("Error preparing statement", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  374. cleanup()
  375. Return False
  376. End If
  377. ' if the param count > 0 there are "?" in the SQL that need to be bound
  378. If mysql_stmt_param_count(stmtHandle) > 0 Then
  379. parameterBindings = bmx_mysql_makeBindings(mysql_stmt_param_count(stmtHandle))
  380. End If
  381. ' **********************************
  382. ' setup bindings for inbound data...
  383. If Not metaHandle Then
  384. metaHandle = mysql_stmt_result_metadata(stmtHandle)
  385. End If
  386. If metaHandle Then
  387. Local fieldCount:Int = mysql_num_fields(metaHandle)
  388. initRecord(fieldCount)
  389. mySQLFields = New TMySQLField[fieldCount]
  390. selectBindings = bmx_mysql_makeBindings(fieldCount)
  391. For Local i:Int = 0 Until fieldCount
  392. Local _field:Byte Ptr = mysql_fetch_field(metaHandle)
  393. mySQLFields[i] = New TMySQLField
  394. mySQLFields[i].mySQLField = _field
  395. mySQLFields[i].dataLength = bmx_mysql_field_length(_field) + 1
  396. ' make some space for the data...
  397. Local size:Size_T = bmx_mysql_length_for_field(_field)
  398. mySQLFields[i].dataValue = MemAlloc(size)
  399. Local ty:Int = bmx_mysql_field_type(_field)
  400. ' build result set field information
  401. Local qf:TQueryField = TQueryField.Create(bmx_mysql_field_name(_field), dbTypeFromNative(Null, ty, bmx_mysql_field_flags(_field)))
  402. qf.length = bmx_mysql_field_length(_field)
  403. qf.precision = bmx_mysql_field_decimals(_field)
  404. rec.setField(i, qf)
  405. bmx_mysql_inbind(selectBindings, i, _field, mySQLFields[i].dataValue, Varptr mySQLFields[i].dataLength, Varptr mySQLFields[i].isNull, ty)
  406. Next
  407. End If
  408. Return True
  409. End Method
  410. Method execute:Int()
  411. If Not preparedQuery Then
  412. Return False
  413. End If
  414. If Not stmtHandle Then
  415. Return False
  416. End If
  417. index = SQL_BeforeFirstRow
  418. Local result:Int = 0
  419. result = bmx_mysql_stmt_reset(stmtHandle)
  420. If result Then
  421. conn.setError("Error resetting statement", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  422. Return False
  423. End If
  424. ' BIND stuff
  425. Local values:TDBType[] = boundValues
  426. Local paramCount:Int = mysql_stmt_param_count(stmtHandle)
  427. Local strings:Byte Ptr[]
  428. Local times:Byte Ptr[]
  429. Local nulls:Byte[]
  430. If paramCount = bindCount Then
  431. strings = New Byte Ptr[paramCount]
  432. times = New Byte Ptr[paramCount]
  433. nulls = New Byte[paramCount]
  434. For Local i:Int = 0 Until paramCount
  435. Local isNull:Int = False
  436. Local nullsPtr:Byte Ptr
  437. If Not values[i] Or values[i].isNull() Then
  438. isNull = True
  439. nulls[i] = 1
  440. nullsPtr = Byte Ptr(nulls) + i
  441. End If
  442. Select values[i].kind()
  443. Case DBTYPE_INT
  444. bmx_mysql_bind_int(parameterBindings, i, Varptr TDBInt(values[i]).value, nullsPtr)
  445. Case DBTYPE_FLOAT
  446. bmx_mysql_bind_float(parameterBindings, i, Varptr TDBFloat(values[i]).value, nullsPtr)
  447. Case DBTYPE_DOUBLE
  448. bmx_mysql_bind_double(parameterBindings, i, Varptr TDBDouble(values[i]).value, nullsPtr)
  449. Case DBTYPE_LONG
  450. bmx_mysql_bind_long(parameterBindings, i, Varptr TDBLong(values[i]).value, nullsPtr)
  451. Case DBTYPE_STRING
  452. local s:Byte Ptr = values[i].getString().ToUTF8String()
  453. strings[i] = s
  454. bmx_mysql_bind_string(parameterBindings, i, s, _strlen(s), nullsPtr)
  455. Case DBTYPE_BLOB
  456. bmx_mysql_bind_blob(parameterBindings, i, TDBBlob(values[i]).value, TDBBlob(values[i])._size, nullsPtr)
  457. Case DBTYPE_DATE
  458. Local date:TDBDate = TDBDate(values[i])
  459. times[i] = bmx_mysql_makeTime()
  460. bmx_mysql_bind_date(parameterBindings, i, times[i], date.getYear(), date.getMonth(), date.getDay(), nullsPtr)
  461. Case DBTYPE_DATETIME
  462. Local date:TDBDateTime = TDBDateTime(values[i])
  463. times[i] = bmx_mysql_makeTime()
  464. bmx_mysql_bind_datetime(parameterBindings, i, times[i], date.getYear(), date.getMonth(), date.getDay(), date.getHour(), date.getMinute(), date.getSecond(), nullsPtr)
  465. Case DBTYPE_TIME
  466. Local date:TDBTime = TDBTime(values[i])
  467. times[i] = bmx_mysql_makeTime()
  468. bmx_mysql_bind_time(parameterBindings, i, times[i], date.getHour(), date.getMinute(), date.getSecond(), nullsPtr)
  469. End Select
  470. Next
  471. ' actually bind the parameters
  472. result = bmx_mysql_stmt_bind_param(stmtHandle, parameterBindings)
  473. If result Then
  474. conn.setError("Error binding values", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  475. ' free up the strings
  476. For Local i:Int = 0 Until paramCount
  477. If strings[i] Then
  478. MemFree(strings[i])
  479. End If
  480. If times[i] Then
  481. bmx_mysql_deleteTime(times[i])
  482. End If
  483. Next
  484. Return False
  485. End If
  486. End If
  487. ' execute the statement
  488. result = mysql_stmt_execute(stmtHandle)
  489. ' free up the strings
  490. If strings Or times Then
  491. For Local i:Int = 0 Until paramCount
  492. If strings[i] Then
  493. MemFree(strings[i])
  494. End If
  495. If times[i] Then
  496. bmx_mysql_deleteTime(times[i])
  497. End If
  498. Next
  499. End If
  500. If result Then
  501. conn.setError("Error executing statement", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  502. Return False
  503. End If
  504. Local af:Long
  505. bmx_mysql_stmt_affected_rows(stmtHandle, Varptr af)
  506. _rowsAffected = af
  507. ' if this is set, then there is data returned from the statement execution
  508. ' in which case we need to bind the results for the result set
  509. If metaHandle Then
  510. result = bmx_mysql_stmt_bind_result(stmtHandle, selectBindings)
  511. If result Then
  512. conn.setError("Error binding result", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  513. Return False
  514. End If
  515. result = mysql_stmt_store_result(stmtHandle)
  516. If result Then
  517. conn.setError("Error storing result", String.FromUTF8String(mysql_stmt_error(stmtHandle)), TDatabaseError.ERROR_STATEMENT, mysql_errno(stmtHandle))
  518. Return False
  519. End If
  520. End If
  521. _isActive = True
  522. Return True
  523. End Method
  524. Method initRecord(size:Int)
  525. rec.clear()
  526. If size > 0 Then
  527. rec.init(size)
  528. End If
  529. resetValues(size)
  530. End Method
  531. Method firstRow:Int()
  532. If index = SQL_BeforeFirstRow Then
  533. Return nextRow()
  534. End If
  535. Return False
  536. End Method
  537. Method nextRow:Int()
  538. If preparedQuery Then
  539. If Not stmtHandle Then
  540. Return False
  541. End If
  542. Local result:Int = bmx_mysql_stmt_fetch(stmtHandle)
  543. If result Then
  544. Return False
  545. End If
  546. Else
  547. row = mysql_fetch_row(resultHandle)
  548. If Not row Then
  549. Return False
  550. End If
  551. End If
  552. ' now populate the values[] array with the fetched data !
  553. For Local i:Int = 0 Until rec.count()
  554. If values[i] Then
  555. values[i].clear()
  556. End If
  557. If preparedQuery Then
  558. If Not mySQLFields[i].isNull Then
  559. Local fieldLength:Int = mySQLFields[i].dataLength
  560. ' it seems that we need to retrieve all values as if they were "strings"...
  561. ' Don't ask... it doesn't work otherwise. (except on Windows... haw)
  562. Select rec.fields[i].fType
  563. Case DBTYPE_INT
  564. values[i] = New TDBInt
  565. values[i].setInt(bmx_mysql_char_to_int(mySQLFields[i].dataValue))
  566. Case DBTYPE_LONG
  567. values[i] = New TDBLong
  568. values[i].setLong(bmx_mysql_char_to_long(mySQLFields[i].dataValue))
  569. Case DBTYPE_FLOAT
  570. values[i] = New TDBFloat
  571. values[i].setFloat(bmx_mysql_char_to_float(mySQLFields[i].dataValue))
  572. Case DBTYPE_DOUBLE
  573. values[i] = New TDBDouble
  574. values[i].setDouble(bmx_mysql_char_to_double(mySQLFields[i].dataValue))
  575. Case DBTYPE_DATE
  576. values[i] = bmx_mysql_char_to_date(mySQLFields[i].dataValue)
  577. Case DBTYPE_DATETIME
  578. values[i] = bmx_mysql_char_to_datetime(mySQLFields[i].dataValue)
  579. Case DBTYPE_TIME
  580. values[i] = bmx_mysql_char_to_time(mySQLFields[i].dataValue)
  581. Case DBTYPE_BLOB
  582. values[i] = TDBBlob.Set(mySQLFields[i].dataValue, fieldLength)
  583. Default
  584. values[i] = New TDBString
  585. values[i].setString(sizedUTF8toISO8859(mySQLFields[i].dataValue, fieldLength))
  586. End Select
  587. End If
  588. Else
  589. ' a non-prepared query
  590. If Not bmx_mysql_rowField_isNull(row, i) Then
  591. Local fieldLength:Int = Int(mysql_fetch_lengths(resultHandle)[i])
  592. Select rec.fields[i].fType
  593. Case DBTYPE_INT
  594. values[i] = New TDBInt
  595. values[i].setInt(String.fromBytes(bmx_mysql_rowField_chars(row, i), fieldLength).toInt())
  596. Case DBTYPE_LONG
  597. values[i] = New TDBLong
  598. values[i].setLong(String.fromBytes(bmx_mysql_rowField_chars(row, i), fieldLength).toLong())
  599. Case DBTYPE_FLOAT
  600. values[i] = New TDBFloat
  601. values[i].setFloat(String.fromBytes(bmx_mysql_rowField_chars(row, i), fieldLength).toFloat())
  602. Case DBTYPE_DOUBLE
  603. values[i] = New TDBDouble
  604. values[i].setDouble(String.fromBytes(bmx_mysql_rowField_chars(row, i), fieldLength).toDouble())
  605. Case DBTYPE_DATE
  606. values[i] = TDBDate.SetFromString(String.FromUTF8Bytes(bmx_mysql_rowField_chars(row, i), fieldLength))
  607. Case DBTYPE_DATETIME
  608. values[i] = TDBDateTime.SetFromString(String.FromUTF8Bytes(bmx_mysql_rowField_chars(row, i), fieldLength))
  609. Case DBTYPE_TIME
  610. values[i] = TDBTime.SetFromString(String.FromUTF8Bytes(bmx_mysql_rowField_chars(row, i), fieldLength))
  611. Case DBTYPE_BLOB
  612. values[i] = TDBBlob.Set(bmx_mysql_rowField_chars(row, i), fieldLength)
  613. Default
  614. values[i] = New TDBString
  615. values[i].setString(String.FromUTF8Bytes(bmx_mysql_rowField_chars(row, i), fieldLength))
  616. End Select
  617. End If
  618. End If
  619. Next
  620. index:+ 1
  621. Return True
  622. End Method
  623. Method lastInsertedId:Long()
  624. If Not isActive()
  625. Return -1
  626. End If
  627. Local id:Long = -1
  628. If preparedQuery Then
  629. bmx_mysql_stmt_insert_id(stmtHandle, Varptr id)
  630. Else
  631. bmx_mysql_insert_id(conn.handle, Varptr id)
  632. End If
  633. Return id
  634. End Method
  635. Method rowsAffected:Int()
  636. Return _rowsAffected
  637. End Method
  638. Function dbTypeFromNative:Int(name:String, _type:Int = 0, _flags:Int = 0)
  639. Local dbType:Int
  640. Select _type
  641. Case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, MYSQL_TYPE_INT24
  642. dbType = DBTYPE_INT
  643. Case MYSQL_TYPE_YEAR
  644. dbType = DBTYPE_INT
  645. Case MYSQL_TYPE_LONGLONG
  646. dbType = DBTYPE_LONG
  647. Case MYSQL_TYPE_FLOAT
  648. dbType = DBTYPE_FLOAT
  649. Case MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DECIMAL
  650. dbType = DBTYPE_DOUBLE
  651. Case MYSQL_TYPE_DATE
  652. dbType = DBTYPE_DATE
  653. Case MYSQL_TYPE_TIME
  654. dbType = DBTYPE_TIME
  655. Case MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIMESTAMP
  656. dbType = DBTYPE_DATETIME
  657. Case MYSQL_TYPE_BLOB, MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB
  658. If _flags & 128 Then ' binary !
  659. dbType = DBTYPE_BLOB
  660. Else ' String!
  661. dbType = DBTYPE_STRING
  662. End If
  663. Default
  664. dbType = DBTYPE_STRING
  665. End Select
  666. Return dbType
  667. End Function
  668. Method cleanup()
  669. If resultHandle Then
  670. mysql_free_result(resultHandle)
  671. resultHandle = Null
  672. End If
  673. If metaHandle Then
  674. mysql_free_result(metaHandle)
  675. metaHandle = Null
  676. End If
  677. If stmtHandle Then
  678. If bmx_mysql_stmt_close(stmtHandle) Then
  679. '
  680. End If
  681. stmtHandle = Null
  682. End If
  683. If parameterBindings Then
  684. bmx_mysql_deleteBindings(parameterBindings)
  685. parameterBindings = Null
  686. End If
  687. If selectBindings Then
  688. bmx_mysql_deleteBindings(selectBindings)
  689. selectBindings = Null
  690. End If
  691. If mySQLFields Then
  692. For Local i:Int = 0 Until mySQLFields.length
  693. If mySQLFields[i] Then
  694. mySQLFields[i].clear()
  695. mySQLFields[i] = Null
  696. End If
  697. Next
  698. End If
  699. index = SQL_BeforeFirstRow
  700. rec.clear()
  701. _isActive = False
  702. preparedQuery = True
  703. End Method
  704. Method clear()
  705. cleanup()
  706. Super.clear()
  707. End Method
  708. Method reset()
  709. clear()
  710. End Method
  711. Method free()
  712. clear()
  713. End Method
  714. Method Delete()
  715. free()
  716. End Method
  717. End Type
  718. Type TMariaDBDatabaseLoader Extends TDatabaseLoader
  719. Method New()
  720. _type = "MARIADB"
  721. End Method
  722. Method LoadDatabase:TDBConnection( dbname:String = Null, host:String = Null, ..
  723. port:Int = Null, user:String = Null, password:String = Null, ..
  724. server:String = Null, options:String = Null )
  725. Return TDBMariaDB.Create(dbName, host, port, user, password, server, options)
  726. End Method
  727. End Type
  728. AddDatabaseLoader New TMariaDBDatabaseLoader