Skip to content

Commit 8a1a877

Browse files
committed
fix: store NULL and empty string as NULL in DBMS_OUTPUT
Oracle treats empty strings as NULL. Both PUT_LINE('') and PUT_LINE(NULL) should store actual NULL values, which GET_LINE returns as SQL NULL. Verified against Oracle 23.26 Free. Fixes #25
1 parent 868a774 commit 8a1a877

File tree

2 files changed

+46
-12
lines changed

2 files changed

+46
-12
lines changed

contrib/ivorysql_ora/expected/ora_dbms_output.out

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ BEGIN
5050
RAISE NOTICE 'Test 1.3 - Empty string: [%], Status: %', line, status;
5151
END;
5252
/
53-
NOTICE: Test 1.3 - Empty string: [], Status: 0
53+
NOTICE: Test 1.3 - Empty string: [<NULL>], Status: 0
5454
-- Test 1.4: NULL handling (should output empty line)
5555
DECLARE
5656
line TEXT;
@@ -62,7 +62,7 @@ BEGIN
6262
RAISE NOTICE 'Test 1.4 - NULL input: [%], Status: %', line, status;
6363
END;
6464
/
65-
NOTICE: Test 1.4 - NULL input: [], Status: 0
65+
NOTICE: Test 1.4 - NULL input: [<NULL>], Status: 0
6666
-- Test 1.5: GET_LINE when buffer is empty (status should be 1)
6767
DECLARE
6868
line TEXT;

contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ ensure_lines_capacity(void)
201201
*
202202
* Add a completed line to the buffer.
203203
* Checks for buffer overflow based on byte usage (Oracle behavior).
204+
* NULL lines are stored as NULL pointers (Oracle behavior).
204205
*/
205206
static void
206207
add_line_to_buffer(const char *line)
@@ -210,7 +211,8 @@ add_line_to_buffer(const char *line)
210211
char *line_copy;
211212

212213
/* Calculate bytes for this line (Oracle counts actual bytes) */
213-
line_bytes = strlen(line);
214+
/* NULL lines count as 0 bytes */
215+
line_bytes = (line != NULL) ? strlen(line) : 0;
214216

215217
/* Check buffer overflow BEFORE adding (Oracle behavior) */
216218
if (output_buffer->buffer_used + line_bytes > output_buffer->buffer_size)
@@ -224,7 +226,8 @@ add_line_to_buffer(const char *line)
224226

225227
/* Store line in buffer memory context */
226228
oldcontext = MemoryContextSwitchTo(output_buffer->buffer_mcxt);
227-
line_copy = pstrdup(line);
229+
/* Store NULL as NULL pointer, not as empty string */
230+
line_copy = (line != NULL) ? pstrdup(line) : NULL;
228231
output_buffer->lines[output_buffer->line_count++] = line_copy;
229232
output_buffer->buffer_used += line_bytes;
230233
MemoryContextSwitchTo(oldcontext);
@@ -307,20 +310,24 @@ ora_dbms_output_disable(PG_FUNCTION_ARGS)
307310
*
308311
* Output a line to the buffer (with newline).
309312
* If there's pending PUT text, append it first (Oracle behavior).
310-
* Oracle behavior: NULL is treated as empty string.
313+
* Oracle behavior: NULL stores actual NULL in buffer (not empty string).
311314
*/
312315
Datum
313316
ora_dbms_output_put_line(PG_FUNCTION_ARGS)
314317
{
315318
char *line_str;
319+
bool is_null = false;
316320

317321
/* Silently discard if buffer not enabled (Oracle behavior) */
318322
if (output_buffer == NULL || !output_buffer->enabled)
319323
PG_RETURN_VOID();
320324

321-
/* Handle NULL argument - treat as empty string (Oracle behavior) */
325+
/* Handle NULL argument - Oracle stores actual NULL */
322326
if (PG_ARGISNULL(0))
323-
line_str = "";
327+
{
328+
line_str = NULL;
329+
is_null = true;
330+
}
324331
else
325332
{
326333
text *line_text = PG_GETARG_TEXT_PP(0);
@@ -330,13 +337,15 @@ ora_dbms_output_put_line(PG_FUNCTION_ARGS)
330337
/* If there's pending PUT text, append it first (Oracle behavior) */
331338
if (output_buffer->current_line->len > 0)
332339
{
333-
appendStringInfoString(output_buffer->current_line, line_str);
340+
/* Append non-NULL text to current line */
341+
if (!is_null)
342+
appendStringInfoString(output_buffer->current_line, line_str);
334343
add_line_to_buffer(output_buffer->current_line->data);
335344
resetStringInfo(output_buffer->current_line);
336345
}
337346
else
338347
{
339-
/* No pending PUT text, just add the line */
348+
/* No pending PUT text, just add the line (may be NULL) */
340349
add_line_to_buffer(line_str);
341350
}
342351

@@ -439,7 +448,16 @@ ora_dbms_output_get_line(PG_FUNCTION_ARGS)
439448
/* Return next line */
440449
char *line = output_buffer->lines[output_buffer->read_position++];
441450

442-
values[0] = CStringGetTextDatum(line);
451+
/* Handle NULL lines (Oracle behavior: PUT_LINE(NULL) stores actual NULL) */
452+
if (line == NULL)
453+
{
454+
nulls[0] = true;
455+
values[0] = (Datum) 0;
456+
}
457+
else
458+
{
459+
values[0] = CStringGetTextDatum(line);
460+
}
443461
values[1] = Int32GetDatum(0); /* status = 0 (success) */
444462
}
445463

@@ -493,17 +511,33 @@ ora_dbms_output_get_lines(PG_FUNCTION_ARGS)
493511

494512
if (actual_lines > 0)
495513
{
514+
bool *line_nulls;
515+
int lbound = 1; /* 1-based array indexing */
516+
496517
line_datums = (Datum *) palloc(sizeof(Datum) * actual_lines);
518+
line_nulls = (bool *) palloc(sizeof(bool) * actual_lines);
497519

498520
for (i = 0; i < actual_lines; i++)
499521
{
500522
char *line = output_buffer->lines[output_buffer->read_position++];
501523

502-
line_datums[i] = CStringGetTextDatum(line);
524+
/* Handle NULL lines (Oracle behavior) */
525+
if (line == NULL)
526+
{
527+
line_nulls[i] = true;
528+
line_datums[i] = (Datum) 0;
529+
}
530+
else
531+
{
532+
line_nulls[i] = false;
533+
line_datums[i] = CStringGetTextDatum(line);
534+
}
503535
}
504536

505-
lines_array = construct_array(line_datums, actual_lines, TEXTOID, -1, false, TYPALIGN_INT);
537+
lines_array = construct_md_array(line_datums, line_nulls, 1, &actual_lines, &lbound,
538+
TEXTOID, -1, false, TYPALIGN_INT);
506539
pfree(line_datums);
540+
pfree(line_nulls);
507541
}
508542
else
509543
{

0 commit comments

Comments
 (0)