2
0

postgresql.bmx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  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 name of the author nor the
  12. ' names of its contributors may be used to endorse or promote products
  13. ' derived from this software without specific prior written permission.
  14. '
  15. ' THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''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 THE AUTHOR 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 - PostgreSQL
  29. about: A PostgreSQL database driver for #Database
  30. End Rem
  31. Module Database.PostgreSQL
  32. ModuleInfo "Version: 1.05"
  33. ModuleInfo "Author: Bruce A Henderson"
  34. ModuleInfo "License: BSD"
  35. ModuleInfo "Copyright: 2007-2023 Bruce A Henderson"
  36. ModuleInfo "Modserver: BRL"
  37. ModuleInfo "History: 1.05"
  38. ModuleInfo "History: Linux/macOS uses pkg-config to configure libpq"
  39. ModuleInfo "History: dll no longer provided for Windows - Ensure libpq.dll is in the path"
  40. ModuleInfo "History: 1.04"
  41. ModuleInfo "History: Update to latest postgres client library."
  42. ModuleInfo "History: NG support."
  43. ModuleInfo "History: 1.03"
  44. ModuleInfo "History: isOpen() now checks the connection status."
  45. ModuleInfo "History: Sets active to false when all rows read."
  46. ModuleInfo "History: Resultset cleanup improvements."
  47. ModuleInfo "History: Fixed prepared statement dealloc case issue."
  48. ModuleInfo "History: Fixed invalid definition for float/double."
  49. ModuleInfo "History: Added blob support."
  50. ModuleInfo "History: Added date/time support."
  51. ModuleInfo "History: 1.02"
  52. ModuleInfo "History: Added hasPrepareSupport() and hasTransactionSupport() methods."
  53. ModuleInfo "History: 1.01"
  54. ModuleInfo "History: Fixed open() not closing if already open."
  55. ModuleInfo "History: 1.00 Initial Release"
  56. ?macos
  57. ' it would be nice not to have to be so version specific here...
  58. 'ModuleInfo "CC_OPTS: `pkg-config --cflags /opt/homebrew/Cellar/postgresql@14/14.10/lib/postgresql@14/pkgconfig/libpq.pc`"
  59. 'ModuleInfo "LD_OPTS: `pkg-config --libs /opt/homebrew/Cellar/postgresql@14/14.10/lib/postgresql@14/pkgconfig/libpq.pc`"
  60. 'ModuleInfo "CC_OPTS: `pkg-config --cflags /opt/homebrew/Cellar/postgresql@15/15.5/lib/pkgconfig/libpq.pc`"
  61. 'ModuleInfo "LD_OPTS: `pkg-config --libs /opt/homebrew/Cellar/postgresql@15/15.5/lib/pkgconfig/libpq.pc`"
  62. ModuleInfo "CC_OPTS: `pkg-config --cflags /opt/homebrew/Cellar/libpq/16.1/lib/pkgconfig/libpq.pc`"
  63. ModuleInfo "LD_OPTS: `pkg-config --libs /opt/homebrew/Cellar/libpq/16.1/lib/pkgconfig/libpq.pc`"
  64. ?linux
  65. ModuleInfo "CC_OPTS: `pkg-config --cflags libpq`"
  66. ModuleInfo "LD_OPTS: `pkg-config --libs libpq`"
  67. ?win32x86
  68. ModuleInfo "LD_OPTS: -L%PWD%/lib/win32x86"
  69. ?win32x64
  70. ModuleInfo "LD_OPTS: -L%PWD%/lib/win32x64"
  71. ?
  72. Import Database.Core
  73. Import "common.bmx"
  74. Type TDBPostgreSQL Extends TDBConnection
  75. Function Create:TDBConnection(dbname:String = Null, host:String = Null, ..
  76. port:Int = Null, user:String = Null, password:String = Null, ..
  77. server:String = Null, options:String = Null)
  78. Local this:TDBPostgreSQL = New TDBPostgreSQL
  79. this.init(dbname, host, port, user, password, server, options)
  80. If this._dbname Then
  81. this.open(user, password)
  82. End If
  83. Return this
  84. End Function
  85. Method close()
  86. If _isOpen Then
  87. If handle Then
  88. bmx_pgsql_PQfinish(handle)
  89. handle = Null
  90. End If
  91. _isOpen = False
  92. End If
  93. End Method
  94. Method isOpen:Int()
  95. If _isOpen Then
  96. ' really check that the database is open
  97. If bmx_pgsql_PQstatus(handle) Then
  98. _isOpen = False
  99. End If
  100. End If
  101. Return _isOpen
  102. End Method
  103. Method commit:Int()
  104. If Not _isOpen Or Not handle Then
  105. Return False
  106. End If
  107. Local result:Byte Ptr = bmx_pgsql_PQexec(handle, "COMMIT")
  108. If Not result Or bmx_pgsql_PQresultStatus(result) <> PGRES_COMMAND_OK Then
  109. setError("Error committing transaction", convertUTF8toISO8859(bmx_pgsql_PQerrorMessage(handle)), TDatabaseError.ERROR_TRANSACTION)
  110. bmx_pgsql_PQclear(result)
  111. Return False
  112. End If
  113. bmx_pgsql_PQclear(result)
  114. Return True
  115. End Method
  116. Method getTables:String[]()
  117. Local list:String[]
  118. If Not _isOpen Then
  119. Return list
  120. End If
  121. Local tables:TList = New TList
  122. Local query:TDatabaseQuery = TDatabaseQuery.Create(Self)
  123. Local sql:String = "Select tablename from pg_tables where schemaname Not in ('pg_catalog', 'information_schema')"
  124. If query.execute(sql) Then
  125. While query.nextRow()
  126. tables.addLast(query.value(0).getString())
  127. Wend
  128. End If
  129. If tables.count() > 0 Then
  130. list = New String[tables.count()]
  131. Local i:Int = 0
  132. For Local s:String = EachIn tables
  133. list[i] = s
  134. i:+ 1
  135. Next
  136. End If
  137. Return list
  138. End Method
  139. Method getTableInfo:TDBTable(tableName:String, withDDL:Int = False)
  140. End Method
  141. Method open:Int(user:String = Null, pass:String = Null)
  142. If _isOpen Then
  143. close()
  144. End If
  145. If user Then
  146. _user = user
  147. End If
  148. If pass Then
  149. _password = pass
  150. End If
  151. Local connect:String
  152. If _dbname Then
  153. connect :+ "dbname=" + _dbname + "~n"
  154. End If
  155. If _host Then
  156. connect :+ "host=" + _host + "~n"
  157. End If
  158. If _port Then
  159. connect :+ "port=" + _port + "~n"
  160. End If
  161. If _user Then
  162. connect :+ "user=" + _user + "~n"
  163. End If
  164. If _password Then
  165. connect :+ "password=" + _password + "~n"
  166. End If
  167. handle = bmx_pgsql_PQconnectdb(connect)
  168. If bmx_pgsql_PQstatus(handle) Then
  169. setError("Error connecting to database '" + _dbname + "'", convertUTF8toISO8859(bmx_pgsql_PQerrorMessage(handle)), TDatabaseError.ERROR_CONNECTION)
  170. Return False
  171. End If
  172. _isOpen = True
  173. Return True
  174. End Method
  175. Method rollback:Int()
  176. If Not _isOpen Or Not handle Then
  177. Return False
  178. End If
  179. Local result:Byte Ptr = bmx_pgsql_PQexec(handle, "ROLLBACK")
  180. If Not result Or bmx_pgsql_PQresultStatus(result) <> PGRES_COMMAND_OK Then
  181. setError("Error rolling back transaction", convertUTF8toISO8859(bmx_pgsql_PQerrorMessage(handle)), TDatabaseError.ERROR_TRANSACTION)
  182. bmx_pgsql_PQclear(result)
  183. Return False
  184. End If
  185. bmx_pgsql_PQclear(result)
  186. Return True
  187. End Method
  188. Method startTransaction:Int()
  189. If Not _isOpen Or Not handle Then
  190. Return False
  191. End If
  192. Local result:Byte Ptr = bmx_pgsql_PQexec(handle, "BEGIN")
  193. If Not result Or bmx_pgsql_PQresultStatus(result) <> PGRES_COMMAND_OK Then
  194. setError("Error starting transaction", convertUTF8toISO8859(bmx_pgsql_PQerrorMessage(handle)), TDatabaseError.ERROR_TRANSACTION)
  195. bmx_pgsql_PQclear(result)
  196. Return False
  197. End If
  198. bmx_pgsql_PQclear(result)
  199. Return True
  200. End Method
  201. Method databaseHandle:Byte Ptr()
  202. Return handle
  203. End Method
  204. Method createResultSet:TQueryResultSet()
  205. Return TPostgreSQLResultSet.Create(Self)
  206. End Method
  207. Method nativeErrorMessage:String(err:Int)
  208. End Method
  209. Method hasPrepareSupport:Int()
  210. Return True
  211. End Method
  212. Method hasTransactionSupport:Int()
  213. Return True
  214. End Method
  215. End Type
  216. Type TPostgreSQLResultSet Extends TQueryResultSet
  217. ' a pointer to a PGResult
  218. Field pgResult:Byte Ptr
  219. ' number of rows returned in the query
  220. Field _queryRows:Int
  221. Field _rowsAffected:Int
  222. Field _preparedStatementName:String
  223. Function Create:TQueryResultSet(db:TDBConnection, sql:String = Null)
  224. Local this:TPostgreSQLResultSet = New TPostgreSQLResultSet
  225. this.init(db, sql)
  226. this.rec = TQueryRecord.Create()
  227. Return this
  228. End Function
  229. Method Delete()
  230. If _preparedStatementName Then
  231. executeQuery("DEALLOCATE ~q" + _preparedStatementName + "~q")
  232. _preparedStatementName = Null
  233. End If
  234. cleanup()
  235. End Method
  236. Method clearResultSet()
  237. If pgResult Then
  238. bmx_pgsql_PQclear(pgResult)
  239. pgResult = Null
  240. End If
  241. End Method
  242. Method cleanup()
  243. clearResultSet()
  244. index = SQL_BeforeFirstRow
  245. _isActive = False
  246. _queryRows = -1
  247. End Method
  248. Method executeQuery:Int(statement:String)
  249. If Not conn.isOpen() Then
  250. Return False
  251. End If
  252. cleanup()
  253. Local q:String = convertISO8859toUTF8(statement)
  254. pgResult = bmx_pgsql_PQexec(conn.handle, q)
  255. If Not pgResult Then
  256. cleanup()
  257. Return False
  258. End If
  259. Local status:Int = bmx_pgsql_PQresultStatus(pgResult)
  260. Select status
  261. Case PGRES_TUPLES_OK
  262. ' returned some row data... probably a select!?
  263. ' how many ?
  264. _queryRows = bmx_pgsql_PQntuples(pgResult)
  265. Case PGRES_COMMAND_OK
  266. ' success but returned nothing. insert, update, delete etc
  267. ' nothing to see here...
  268. _queryRows = -1
  269. Default
  270. ' an error!
  271. conn.setError("Error executing statement", convertUTF8toISO8859(bmx_pgsql_PQerrorMessage(conn.handle)), TDatabaseError.ERROR_STATEMENT, 0)
  272. cleanup()
  273. Return False
  274. End Select
  275. Local fieldCount:Int = bmx_pgsql_PQnfields(pgResult)
  276. initRecord(fieldCount)
  277. ' PQcmdTuples returns an empty string for non-change statements, so we should
  278. ' get a zero in here for selects...
  279. _rowsAffected = String.fromCString(bmx_pgsql_PQcmdTuples(pgResult)).toInt()
  280. ' get the field descriptions
  281. If fieldCount <> 0 Then
  282. For Local i:Int = 0 Until fieldCount
  283. Local dtype:Int = bmx_pgsql_PQftype(pgResult, i)
  284. Local qf:TQueryField = TQueryField.Create(convertUTF8toISO8859(bmx_pgsql_PQfname(pgResult, i)), dbTypeFromNative(Null, dtype))
  285. qf.length = bmx_pgsql_PQfsize(pgResult, i)
  286. qf.precision = bmx_pgsql_PQfmod(pgResult, i)
  287. qf.dtype = dtype
  288. ' if length is -1, then precision field holds actual length value, and
  289. ' precision should be ignored.
  290. If qf.length = -1 Then
  291. qf.length = qf.precision - 4
  292. qf.precision = -1
  293. End If
  294. rec.setField(i, qf)
  295. Next
  296. End If
  297. If _queryRows = -1 Then
  298. cleanup()
  299. Else
  300. _isActive = True
  301. End If
  302. Return True
  303. End Method
  304. Method initRecord(size:Int)
  305. If rec Then
  306. rec.clear()
  307. If size > 0 Then
  308. rec.init(size)
  309. End If
  310. End If
  311. resetValues(size)
  312. End Method
  313. Method prepare:Int(statement:String)
  314. cleanup()
  315. If Not statement Or statement.length = 0 Then
  316. Return False
  317. End If
  318. If Not _preparedStatementName Then
  319. _preparedStatementName = "prep" + Self.toString()
  320. Else
  321. executeQuery("DEALLOCATE ~q" + _preparedStatementName + "~q")
  322. cleanup()
  323. End If
  324. Local q:String = convertISO8859toUTF8(statement)
  325. pgResult = bmx_pgsql_PQprepare(conn.handle, _preparedStatementName, q)
  326. If Not pgResult Then
  327. Return False
  328. End If
  329. If bmx_pgsql_PQresultStatus(pgResult) <> PGRES_COMMAND_OK Then
  330. conn.setError("Error preparing statement", convertUTF8toISO8859(bmx_pgsql_PQerrorMessage(conn.handle)), TDatabaseError.ERROR_STATEMENT, 0)
  331. cleanup()
  332. Return False
  333. End If
  334. Return True
  335. End Method
  336. Method execute:Int()
  337. cleanup()
  338. Local params:Byte Ptr
  339. Local lengths:Int Ptr
  340. Local formats:Int Ptr
  341. Local paramCount:Int
  342. Local length:Int
  343. Local s:String
  344. Local strings:Byte Ptr[]
  345. ' BIND stuff
  346. Local values:TDBType[] = boundValues
  347. If values Then
  348. paramCount = values.length
  349. ' ** NOTE **
  350. ' PQdescribePrepared is only available in more recent additions.
  351. ' It is useful in it lets us check validity of parameter count.
  352. ' Otherwise we hope that the database catches any issues... :-/
  353. 'Local result:Byte Ptr = bmx_pgsql_PQdescribePrepared(conn.handle, _preparedStatementName)
  354. 'If bmx_pgsql_PQresultStatus(pgResult) <> PGRES_COMMAND_OK Then
  355. ' conn.setError("Error getting prepared statement details", convertUTF8toISO8859(bmx_pgsql_PQerrorMessage(conn.handle)), TDatabaseError.ERROR_STATEMENT, 0)
  356. ' Return False
  357. 'End If
  358. 'If paramCount <> bmx_pgsql_PQnparams(result) Then
  359. ' conn.setError("Wrong number of bind parameters. Expected " + bmx_pgsql_PQnparams(result) + ..
  360. ' ". Actual " + paramCount, Null, TDatabaseError.ERROR_STATEMENT, 0)
  361. ' If result Then
  362. ' bmx_pgsql_PQclear(result)
  363. ' End If
  364. ' Return False
  365. 'End If
  366. strings = New Byte Ptr[paramCount]
  367. params = bmx_pgsql_createParamValues(paramCount)
  368. lengths = bmx_pgsql_createParamInts(paramCount)
  369. formats = bmx_pgsql_createParamInts(paramCount)
  370. For Local i:Int = 0 Until paramCount
  371. If Not values[i] Or values[i].isNull() Then
  372. bmx_pgsql_setNullParam(params, i)
  373. Else
  374. Select values[i].kind()
  375. Case DBTYPE_INT
  376. s = String.fromInt(TDBInt(values[i]).value)
  377. strings[i] = s.toCString()
  378. bmx_pgsql_setParam(params, lengths, formats, i, strings[i], s.length)
  379. Case DBTYPE_LONG
  380. s = String.fromLong(TDBLong(values[i]).value)
  381. strings[i] = s.toCString()
  382. bmx_pgsql_setParam(params, lengths, formats, i, strings[i], s.length)
  383. Case DBTYPE_FLOAT
  384. s = String.fromFloat(TDBFloat(values[i]).value)
  385. strings[i] = s.toCString()
  386. bmx_pgsql_setParam(params, lengths, formats, i, strings[i], s.length)
  387. Case DBTYPE_DOUBLE
  388. s = String.fromDouble(TDBDouble(values[i]).value)
  389. strings[i] = s.toCString()
  390. bmx_pgsql_setParam(params, lengths, formats, i, strings[i], s.length)
  391. Case DBTYPE_BLOB
  392. Local b:TDBBlob = TDBBlob(values[i])
  393. bmx_pgsql_setParamBinary(params, lengths, formats, i, b.value, b._size)
  394. Case DBTYPE_DATE
  395. s = TDBDate(values[i]).getString()
  396. strings[i] = s.toCString()
  397. bmx_pgsql_setParam(params, lengths, formats, i, strings[i], s.length)
  398. Case DBTYPE_DATETIME
  399. s = TDBDateTime(values[i]).getString()
  400. strings[i] = s.toCString()
  401. bmx_pgsql_setParam(params, lengths, formats, i, strings[i], s.length)
  402. Case DBTYPE_TIME
  403. s = TDBTime(values[i]).getString()
  404. strings[i] = s.toCString()
  405. bmx_pgsql_setParam(params, lengths, formats, i, strings[i], s.length)
  406. Default
  407. Local s:String = convertISO8859toUTF8(values[i].getString())
  408. strings[i] = s.toCString()
  409. bmx_pgsql_setParam(params, lengths, formats, i, strings[i], s.length)
  410. End Select
  411. End If
  412. Next
  413. 'If result Then
  414. ' bmx_pgsql_PQclear(result)
  415. 'End If
  416. End If
  417. If params Then
  418. pgResult = bmx_pgsql_PQexecPrepared(conn.handle, _preparedStatementName, ..
  419. paramCount, params, lengths, formats)
  420. Else
  421. pgResult = bmx_pgsql_PQexecPrepared(conn.handle, _preparedStatementName, ..
  422. paramCount, Null, Null, Null)
  423. End If
  424. ' free up the strings
  425. For Local i:Int = 0 Until paramCount
  426. If strings[i] Then
  427. MemFree(strings[i])
  428. End If
  429. Next
  430. If params Then
  431. bmx_pgsql_deleteParamValues(params)
  432. bmx_pgsql_deleteParamInts(lengths)
  433. bmx_pgsql_deleteParamInts(formats)
  434. End If
  435. If Not pgResult Then
  436. conn.setError("Error executing prepared statement", "", TDatabaseError.ERROR_STATEMENT, 0)
  437. cleanup()
  438. Return False
  439. End If
  440. Local status:Int = bmx_pgsql_PQresultStatus(pgResult)
  441. Select status
  442. Case PGRES_TUPLES_OK
  443. ' returned some row data... probably a select!?
  444. ' how many ?
  445. _queryRows = bmx_pgsql_PQntuples(pgResult)
  446. Case PGRES_COMMAND_OK
  447. ' success but returned nothing. insert, update, delete etc
  448. ' nothing to see here...
  449. _queryRows = -1
  450. Default
  451. ' an error!
  452. conn.setError("Error executing prepared statement", convertUTF8toISO8859(bmx_pgsql_PQerrorMessage(conn.handle)), TDatabaseError.ERROR_STATEMENT, 0)
  453. cleanup()
  454. Return False
  455. End Select
  456. Local fieldCount:Int = bmx_pgsql_PQnfields(pgResult)
  457. initRecord(fieldCount)
  458. ' PQcmdTuples returns an empty string for non-change statements, so we should
  459. ' get a zero in here for selects...
  460. _rowsAffected = String.fromCString(bmx_pgsql_PQcmdTuples(pgResult)).toInt()
  461. ' get the field descriptions
  462. If fieldCount <> 0 Then
  463. For Local i:Int = 0 Until fieldCount
  464. Local dtype:Int = bmx_pgsql_PQftype(pgResult, i)
  465. Local qf:TQueryField = TQueryField.Create(convertUTF8toISO8859(bmx_pgsql_PQfname(pgResult, i)), dbTypeFromNative(Null, dtype))
  466. qf.length = bmx_pgsql_PQfsize(pgResult, i)
  467. qf.precision = bmx_pgsql_PQfmod(pgResult, i)
  468. qf.dtype = dtype
  469. ' if length is -1, then precision field holds actual length value, and
  470. ' precision should be ignored.
  471. If qf.length = -1 Then
  472. qf.length = qf.precision - 4
  473. qf.precision = -1
  474. End If
  475. rec.setField(i, qf)
  476. Next
  477. End If
  478. ' did we return any data?
  479. ' if we didn't, then we may as well cleanup now
  480. If _queryRows < 1 Then
  481. cleanup()
  482. Else
  483. _isActive = True
  484. End If
  485. Return True
  486. End Method
  487. Method firstRow:Int()
  488. If index = SQL_BeforeFirstRow Then
  489. Return nextRow()
  490. End If
  491. Return False
  492. End Method
  493. Method nextRow:Int()
  494. If Not _isActive
  495. cleanup()
  496. Return False
  497. End If
  498. If index >= _queryRows - 1 Then
  499. cleanup()
  500. Return False
  501. End If
  502. ' now populate the values[] array with the fetched data !
  503. For Local i:Int = 0 Until rec.count()
  504. If values[i] Then
  505. values[i].clear()
  506. End If
  507. If Not bmx_pgsql_PQgetisnull(pgResult, index + 1, i)
  508. Local fieldLength:Int = bmx_pgsql_PQgetlength(pgResult, index + 1, i)
  509. Select rec.fields[i].fType
  510. Case DBTYPE_INT
  511. values[i] = New TDBInt
  512. values[i].setInt(String.fromBytes(bmx_pgsql_PQgetvalue(pgResult, index + 1, i), fieldLength).toInt())
  513. Case DBTYPE_LONG
  514. values[i] = New TDBLong
  515. values[i].setLong(String.fromBytes(bmx_pgsql_PQgetvalue(pgResult, index + 1, i), fieldLength).toLong())
  516. Case DBTYPE_FLOAT
  517. values[i] = New TDBFloat
  518. values[i].SetFloat(String.fromBytes(bmx_pgsql_PQgetvalue(pgResult, index + 1, i), fieldLength).toFloat())
  519. Case DBTYPE_DOUBLE
  520. values[i] = New TDBDouble
  521. values[i].setDouble(String.fromBytes(bmx_pgsql_PQgetvalue(pgResult, index + 1, i), fieldLength).toDouble())
  522. Case DBTYPE_DATE
  523. values[i] = TDBDate.SetFromString(String.fromBytes(bmx_pgsql_PQgetvalue(pgResult, index + 1, i), fieldLength))
  524. Case DBTYPE_DATETIME
  525. values[i] = TDBDateTime.SetFromString(String.fromBytes(bmx_pgsql_PQgetvalue(pgResult, index + 1, i), fieldLength))
  526. Case DBTYPE_TIME
  527. values[i] = TDBTime.SetFromString(String.fromBytes(bmx_pgsql_PQgetvalue(pgResult, index + 1, i), fieldLength))
  528. Case DBTYPE_BLOB
  529. ' get the escaped data
  530. Local b:Byte Ptr = bmx_pgsql_PQgetvalue(pgResult, index + 1, i)
  531. ' now... unescape!
  532. Local c:Byte Ptr = bmx_pgsql_PQunescapeBytea(b, Varptr fieldLength)
  533. values[i] = TDBBlob.Set(c, fieldLength)
  534. ' free :-)
  535. bmx_pgsql_PQfreemem(c)
  536. Default
  537. values[i] = New TDBString
  538. values[i].setString(sizedUTF8toISO8859(bmx_pgsql_PQgetvalue(pgResult, index + 1, i), fieldLength))
  539. End Select
  540. End If
  541. Next
  542. index:+ 1
  543. If index >= _queryRows - 1 Then
  544. clearResultSet()
  545. End If
  546. Return True
  547. End Method
  548. Method lastInsertedId:Long()
  549. If _isActive And pgResult Then
  550. Return Long(bmx_pgsql_PQoidValue(pgResult))
  551. End If
  552. Return -1
  553. End Method
  554. Method rowsAffected:Int()
  555. Return _rowsAffected
  556. End Method
  557. Function dbTypeFromNative:Int(name:String, _type:Int = 0, _flags:Int = 0)
  558. Local dbType:Int
  559. Select _type
  560. Case BOOLOID, INT2OID, INT4OID, VOIDOID, REGPROCOID, XIDOID, CIDOID
  561. dbType = DBTYPE_INT
  562. Case INT8OID
  563. dbType = DBTYPE_LONG
  564. Case FLOAT4OID
  565. dbType = DBTYPE_FLOAT
  566. Case NUMERICOID, FLOAT8OID
  567. dbType = DBTYPE_DOUBLE
  568. Case DATEOID
  569. dbType = DBTYPE_DATE
  570. Case TIMEOID, TIMETZOID
  571. dbType = DBTYPE_TIME
  572. Case TIMESTAMPOID, TIMESTAMPTZOID, ABSTIMEOID, RELTIMEOID
  573. dbType = DBTYPE_DATETIME
  574. Case BYTEAOID
  575. dbType = DBTYPE_BLOB
  576. Default
  577. dbType = DBTYPE_STRING
  578. End Select
  579. Return dbType
  580. End Function
  581. End Type
  582. Type TPostgreSQLDatabaseLoader Extends TDatabaseLoader
  583. Method New()
  584. _type = "POSTGRESQL"
  585. End Method
  586. Method LoadDatabase:TDBConnection( dbname:String = Null, host:String = Null, ..
  587. port:Int = Null, user:String = Null, password:String = Null, ..
  588. server:String = Null, options:String = Null )
  589. Return TDBPostgreSQL.Create(dbName, host, port, user, password, server, options)
  590. End Method
  591. End Type
  592. AddDatabaseLoader New TPostgreSQLDatabaseLoader