@@ -56,7 +56,7 @@ class PostgresNativeDriver(private var conn: CPointer<PGconn>) : SqlDriver {
56
56
paramValues = preparedStatement?.values(this ),
57
57
paramFormats = preparedStatement?.formats?.refTo(0 ),
58
58
paramLengths = preparedStatement?.lengths?.refTo(0 ),
59
- resultFormat = TEXT_RESULT_FORMAT
59
+ resultFormat = BINARY_RESULT_FORMAT
60
60
)
61
61
}
62
62
} else {
@@ -68,7 +68,7 @@ class PostgresNativeDriver(private var conn: CPointer<PGconn>) : SqlDriver {
68
68
paramValues = preparedStatement?.values(this ),
69
69
paramFormats = preparedStatement?.formats?.refTo(0 ),
70
70
paramLengths = preparedStatement?.lengths?.refTo(0 ),
71
- resultFormat = TEXT_RESULT_FORMAT ,
71
+ resultFormat = BINARY_RESULT_FORMAT ,
72
72
paramTypes = preparedStatement?.types?.refTo(0 )
73
73
)
74
74
}
@@ -85,7 +85,7 @@ class PostgresNativeDriver(private var conn: CPointer<PGconn>) : SqlDriver {
85
85
parameters : Int ,
86
86
binders : (SqlPreparedStatement .() -> Unit )?
87
87
): R {
88
- val cursorName = identifier?.toString() ? : " myCursor"
88
+ val cursorName = if (identifier == null ) " myCursor" else " cursor $identifier "
89
89
val cursor = " DECLARE $cursorName CURSOR FOR"
90
90
val preparedStatement = if (parameters != 0 ) {
91
91
PostgresPreparedStatement (parameters).apply {
@@ -109,7 +109,7 @@ class PostgresNativeDriver(private var conn: CPointer<PGconn>) : SqlDriver {
109
109
paramValues = preparedStatement?.values(this ),
110
110
paramLengths = preparedStatement?.lengths?.refTo(0 ),
111
111
paramFormats = preparedStatement?.formats?.refTo(0 ),
112
- resultFormat = TEXT_RESULT_FORMAT
112
+ resultFormat = BINARY_RESULT_FORMAT
113
113
)
114
114
}
115
115
} else {
@@ -123,7 +123,7 @@ class PostgresNativeDriver(private var conn: CPointer<PGconn>) : SqlDriver {
123
123
paramLengths = preparedStatement?.lengths?.refTo(0 ),
124
124
paramFormats = preparedStatement?.formats?.refTo(0 ),
125
125
paramTypes = preparedStatement?.types?.refTo(0 ),
126
- resultFormat = TEXT_RESULT_FORMAT
126
+ resultFormat = BINARY_RESULT_FORMAT
127
127
)
128
128
}
129
129
}.check(conn)
@@ -179,7 +179,7 @@ private fun CPointer<PGconn>.exec(sql: String) {
179
179
180
180
private fun CPointer<PGresult>?.check (conn : CPointer <PGconn >): CPointer <PGresult > {
181
181
val status = PQresultStatus (this )
182
- require (status == PGRES_TUPLES_OK || status == PGRES_COMMAND_OK ) {
182
+ check (status == PGRES_TUPLES_OK || status == PGRES_COMMAND_OK ) {
183
183
conn.error()
184
184
}
185
185
return this !!
@@ -192,8 +192,7 @@ class PostgresCursor(
192
192
private var result : CPointer <PGresult >,
193
193
private val name : String ,
194
194
private val conn : CPointer <PGconn >
195
- ) :
196
- SqlCursor , Closeable {
195
+ ) : SqlCursor, Closeable {
197
196
override fun close () {
198
197
result.clear()
199
198
conn.exec(" CLOSE $name " )
@@ -202,7 +201,36 @@ class PostgresCursor(
202
201
203
202
override fun getBoolean (index : Int ) = getString(index)?.toBoolean()
204
203
205
- override fun getBytes (index : Int ) = getString(index)?.encodeToByteArray()
204
+ override fun getBytes (index : Int ): ByteArray? {
205
+ val isNull = PQgetisnull (result, tup_num = 0 , field_num = index) == 1
206
+ return if (isNull) {
207
+ null
208
+ } else {
209
+ val bytes = PQgetvalue (result, tup_num = 0 , field_num = index)!!
210
+ val length = PQgetlength (result, tup_num = 0 , field_num = index)
211
+ bytes.fromHex(length)
212
+ }
213
+ }
214
+
215
+ private inline fun Int.fromHex (): Int = if (this in 48 .. 57 ) {
216
+ this - 48
217
+ } else {
218
+ this - 97
219
+ }
220
+
221
+ // because "normal" CPointer<ByteVar>.toByteArray() functions does not support hex (2 Bytes) bytes
222
+ private fun CPointer<ByteVar>.fromHex (length : Int ): ByteArray {
223
+ val array = ByteArray ((length - 2 ) / 2 )
224
+ var index = 0
225
+ for (i in 2 until length step 2 ) {
226
+ val first = this [i].toInt().fromHex()
227
+ val second = this [i + 1 ].toInt().fromHex()
228
+ val octet = first.shl(4 ).or (second)
229
+ array[index] = octet.toByte()
230
+ index++
231
+ }
232
+ return array
233
+ }
206
234
207
235
override fun getDouble (index : Int ) = getString(index)?.toDouble()
208
236
@@ -226,17 +254,26 @@ class PostgresCursor(
226
254
227
255
class PostgresPreparedStatement (private val parameters : Int ) : SqlPreparedStatement {
228
256
fun values (scope : AutofreeScope ): CValuesRef <CPointerVar <ByteVar >> = createValues(parameters) {
229
- value = _values [it]?.cstr?.getPointer(scope)
257
+ value = when (val value = _values [it]) {
258
+ null -> null
259
+ is Data .Bytes -> value.bytes.refTo(0 ).getPointer(scope)
260
+ is Data .Text -> value.text.cstr.getPointer(scope)
261
+ }
230
262
}
231
263
232
- private val _values = arrayOfNulls<String >(parameters)
264
+ private sealed interface Data {
265
+ inline class Bytes (val bytes : ByteArray ) : Data
266
+ inline class Text (val text : String ) : Data
267
+ }
268
+
269
+ private val _values = arrayOfNulls<Data >(parameters)
233
270
val lengths = IntArray (parameters)
234
271
val formats = IntArray (parameters)
235
272
val types = UIntArray (parameters)
236
273
237
274
private fun bind (index : Int , value : String? , oid : UInt ) {
238
275
lengths[index] = if (value != null ) {
239
- _values [index] = value
276
+ _values [index] = Data . Text ( value)
240
277
value.length
241
278
} else 0
242
279
formats[index] = PostgresNativeDriver .TEXT_RESULT_FORMAT
@@ -248,7 +285,12 @@ class PostgresPreparedStatement(private val parameters: Int) : SqlPreparedStatem
248
285
}
249
286
250
287
override fun bindBytes (index : Int , bytes : ByteArray? ) {
251
- bind(index, bytes?.decodeToString(), byteaOid)
288
+ lengths[index] = if (bytes != null && bytes.isNotEmpty()) {
289
+ _values [index] = Data .Bytes (bytes)
290
+ bytes.size
291
+ } else 0
292
+ formats[index] = PostgresNativeDriver .BINARY_RESULT_FORMAT
293
+ types[index] = byteaOid
252
294
}
253
295
254
296
override fun bindDouble (index : Int , double : Double? ) {
@@ -278,12 +320,12 @@ fun PostgresNativeDriver(
278
320
database : String ,
279
321
user : String ,
280
322
password : String ,
281
- port : Int? = null ,
323
+ port : Int = 5432 ,
282
324
options : String? = null
283
325
): PostgresNativeDriver {
284
326
val conn = PQsetdbLogin (
285
327
pghost = host,
286
- pgport = port? .toString(),
328
+ pgport = port.toString(),
287
329
pgtty = null ,
288
330
dbName = database,
289
331
login = user,
0 commit comments