-
Notifications
You must be signed in to change notification settings - Fork 160
Add Oracle-compatible DBMS_OUTPUT package implementation #998
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Implement Oracle-compatible DBMS_OUTPUT package providing: - PUT_LINE, PUT, NEW_LINE for buffered output - GET_LINE, GET_LINES for retrieving output - ENABLE/DISABLE for buffer control - Buffer overflow detection (ORU-10027) Located in contrib/ivorysql_ora/src/builtin_packages/dbms_output/ following IvorySQL maintainer guidance (discussion IvorySQL#988). Includes design documentation and Oracle compatibility comparison with 82% test compatibility rate (27/33 tests pass). Known differences from Oracle: - NULL stored as empty string vs NULL - Re-ENABLE clears buffer vs preserves - Buffer size range 2000-1000000 (Oracle: 2000-unlimited) - No 32767 byte line length limit
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
Oracle enforces a maximum line length of 32767 bytes per line. Exceeding this limit raises ORU-10028 error. - Check line length in PUT_LINE before adding to buffer - Check accumulated line length in PUT before appending - Add test cases for line length boundary conditions Verified against Oracle 23.26 Free. Fixes #21
DBMS_OUTPUT Oracle Compatibility Test ResultsTest Date: 2025-12-03 Test MethodologyEquivalent test cases were executed on both IvorySQL and Oracle Database to verify behavioral compatibility. Tests use PUT followed by GET to verify actual buffer content rather than just syntax validation. Section 1: Basic PUT_LINE and GET_LINETest 1.1: Simple PUT_LINE verified by GET_LINEIvorySQL: Oracle: Result: ✅ MATCH Test 1.2: Multiple PUT_LINE calls verified by GET_LINESIvorySQL: Oracle: Result: ✅ MATCH Test 1.3: Empty string handlingIvorySQL: Oracle: Result: ✅ MATCH Analysis: Oracle treats empty strings as NULL. Both IvorySQL and Oracle store empty string as NULL and return it via GET_LINE. Test 1.4: NULL handlingIvorySQL: Oracle: Result: ✅ MATCH Analysis: Both IvorySQL and Oracle preserve NULL values. Fixed in commit c9aa53f (Fixes #25). Test 1.5: GET_LINE when buffer is emptyIvorySQL: Oracle: Result: ✅ MATCH Section 2: PUT and NEW_LINETest 2.1: PUT followed by NEW_LINEIvorySQL: Oracle: Result: ✅ MATCH Test 2.2: PUT with NULLIvorySQL: Oracle: Result: ✅ MATCH Test 2.3: Multiple PUT calls building one lineIvorySQL: Oracle: Result: ✅ MATCH Test 2.4: PUT + NEW_LINE + PUT_LINE creates two linesIvorySQL: Oracle: Result: ✅ MATCH Section 3: ENABLE and DISABLE behaviorTest 3.1: DISABLE prevents output from being bufferedIvorySQL: Oracle: Result: ✅ MATCH Test 3.2: DISABLE clears existing bufferIvorySQL: Oracle: Result: ✅ MATCH Test 3.3: Re-ENABLE clears bufferIvorySQL: Oracle: Result: ❌ DIFFERENCE Analysis: This is a significant behavioral difference:
This affects applications that call ENABLE() multiple times during execution. See Issue #26 for tracking. Test 3.4: Output while disabled is silently ignoredIvorySQL: Oracle: Result: ✅ MATCH Section 4: Buffer size limitsTest 4.1: Buffer size below minimum (1000)IvorySQL: Oracle: Result: ❌ DIFFERENCE Analysis: IvorySQL rejects values below 2000 with an error. Oracle silently adjusts values below 2000 up to 2000 (the minimum). See Issue #22 for tracking. Test 4.2: Buffer size at minimum (2000)IvorySQL: Oracle: Result: ✅ MATCH Test 4.3: Buffer size at maximum (1000000)IvorySQL: Oracle: Result: ✅ MATCH Test 4.4: Buffer size above maximum (1000001)IvorySQL: Oracle: Result: ❌ DIFFERENCE Analysis: IvorySQL enforces maximum buffer size of 1000000, Oracle accepts larger values. This is documented as an intentional IvorySQL limitation. Test 4.5: NULL buffer size uses defaultIvorySQL: Oracle: Result: ✅ MATCH Section 5: Buffer overflowTest 5.1: Buffer overflow produces errorIvorySQL: Oracle: Result: ✅ MATCH Analysis: Both produce the same Oracle-compatible error code (ORU-10027) and overflow occurs at approximately the same line count. Section 6: GET_LINE and GET_LINES behaviorTest 6.1: GET_LINE returns lines in orderIvorySQL: Oracle: Result: ✅ MATCH Test 6.2: GET_LINES with numlines larger than availableIvorySQL: Oracle: Result: ✅ MATCH Test 6.3: GET_LINES with numlines smaller than availableIvorySQL: Oracle: Result: ✅ MATCH Section 7: Usage in procedures and functionsTest 7.1: Output from procedureIvorySQL: Oracle: Result: ✅ MATCH Test 7.2: Output from functionIvorySQL: Oracle: Result: ✅ MATCH Section 8: Special casesTest 8.1: Special charactersIvorySQL: Oracle: Result: ✅ MATCH Test 8.2: Numeric values via concatenationIvorySQL: Oracle: Result: ✅ MATCH Test 8.3: Very long lineIvorySQL: Oracle: Result: ✅ MATCH Test 8.4: Exception handling preserves bufferIvorySQL: Oracle: Result: ✅ MATCH (error message format differs but behavior matches) Test 8.5: Nested blocksIvorySQL: Oracle: Result: ✅ MATCH Test 8.6: Loop outputIvorySQL: Oracle: Result: ✅ MATCH Section 9: Line Length LimitsTest 9.1: Maximum line length (32767 bytes)IvorySQL: Oracle: Result: ✅ MATCH Test 9.2: Exceeding max line length (32768 bytes via PUT_LINE)IvorySQL: Oracle: Result: ✅ MATCH Analysis: Both IvorySQL and Oracle enforce the 32767 byte line length limit with ORU-10028 error. Fixed in commit 80355d1 (Fixes #21). Test 9.3: PUT accumulating to exceed 32767 bytesIvorySQL: Oracle: Result: ✅ MATCH Analysis: Both IvorySQL and Oracle detect line length overflow during PUT accumulation. Fixed in commit 80355d1 (Fixes #21). Summary
Compatibility Rate: 91% (30/33 tests) Differences Summary
Fixed Issues
Recommendations
|
📝 WalkthroughWalkthroughAdds an Oracle-compatible DBMS_OUTPUT feature: new C backend implementing buffering and SQL-callable functions, PL/SQL package spec/body, tests, and build/merge entries to compile and register the module. Changes
Sequence DiagramsequenceDiagram
autonumber
participant Client
participant PL_SQL as "dbms_output (PL/SQL)"
participant C_Backend as "C Backend\n(ora_dbms_output_*)"
participant Buffer as "DbmsOutputBuffer"
participant Txn as "Transaction Manager"
Client->>PL_SQL: ENABLE(size?)
PL_SQL->>C_Backend: ora_dbms_output_enable()
C_Backend->>Buffer: init_output_buffer()
Buffer->>Txn: register txn callback
Client->>PL_SQL: PUT(text) / PUT_LINE(text)
PL_SQL->>C_Backend: ora_dbms_output_put/_put_line
C_Backend->>Buffer: accumulate/append (enforce limits)
Client->>PL_SQL: NEW_LINE()
PL_SQL->>C_Backend: ora_dbms_output_new_line()
C_Backend->>Buffer: flush current PUT into lines array
Client->>PL_SQL: GET_LINE() / GET_LINES(n)
PL_SQL->>C_Backend: ora_dbms_output_get_line/get_lines
C_Backend->>Buffer: read and advance position
Buffer-->>C_Backend: line(s) + status/count
C_Backend-->>PL_SQL: composite result
PL_SQL-->>Client: return values
Client->>PL_SQL: DISABLE()
PL_SQL->>C_Backend: ora_dbms_output_disable()
Txn->>Buffer: on commit/abort -> cleanup_output_buffer()
Buffer->>Txn: unregister callback / free memory
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
contrib/ivorysql_ora/sql/ora_dbms_output.sql (1)
147-189: Strong DBMS_OUTPUT coverage; consider clarifying DISABLE vs re‑ENABLE commentsThe tests around
DISABLE/re‑ENABLE(3.1–3.3) correctly verify that:
- Output is not buffered while disabled.
- A subsequent
ENABLEcall effectively resets the buffer.However, comments like “DISABLE clears existing buffer” in Test 3.2 can be misread as “DISABLE alone clears,” whereas the behavior is really “re‑ENABLE starts with a fresh buffer.” Consider tweaking those comments to avoid confusion for future readers; the test logic itself is fine.
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c (1)
82-146: Minor behavioral/doc consistency around ENABLE/DISABLE and callback registrationTwo small nits:
The comment above
init_output_buffersays “Oracle behavior – ENABLE always clears existing buffer,” while your own tests intentionally validate a non‑Oracle behavior (re‑ENABLE clears, whereas Oracle typically preserves). It would be clearer to phrase this as “Our behavior: ENABLE always clears existing buffer (differs from Oracle)” to avoid confusion for future maintainers.Tracking
callback_registeredin the buffer struct and immediately unregistering insidecleanup_output_bufferworks, but given that the callback itself simply callscleanup_output_buffer(), you could simplify by:
- Keeping the callback permanently registered for the backend, and
- Letting it no‑op when
output_buffer == NULL.Not required for correctness, but it would reduce state bookkeeping and the chance of subtle registration/unregistration bugs later.
Also applies to: 296-309
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
contrib/ivorysql_ora/expected/ora_dbms_output.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_nested_subproc2.outis excluded by!**/*.out
📒 Files selected for processing (5)
contrib/ivorysql_ora/Makefile(2 hunks)contrib/ivorysql_ora/ivorysql_ora_merge_sqls(1 hunks)contrib/ivorysql_ora/sql/ora_dbms_output.sql(1 hunks)contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output--1.0.sql(1 hunks)contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T10:43:25.257Z
Learnt from: syasirs
Repo: IvorySQL/IvorySQL PR: 858
File: src/Makefile.global.in:513-514
Timestamp: 2025-09-02T10:43:25.257Z
Learning: In IvorySQL Makefile.global.in, $(wildcard t/oracle/*.pl) is used instead of t/oracle/*.pl in Oracle TAP test discovery because the t/oracle directory is optional and may not exist in most builds. The wildcard function safely returns empty string when the directory is missing, preventing shell expansion errors.
Applied to files:
contrib/ivorysql_ora/Makefile
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: meson_build (ubuntu-latest)
- GitHub Check: contrib_regression
- GitHub Check: oracle_regression (ubuntu-latest)
- GitHub Check: pg_regression (ubuntu-latest)
- GitHub Check: build (ubuntu-latest)
🔇 Additional comments (3)
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output--1.0.sql (1)
26-127: DBMS_OUTPUT package spec/body and C wrappers look consistent and well‑aligned
- C functions, composite types, and package procedures line up correctly with the C signatures and intended OUT/IN OUT semantics.
- Using
VOLATILEfor these session/transaction‑scoped buffers is appropriate.
No issues from a correctness or integration standpoint.contrib/ivorysql_ora/ivorysql_ora_merge_sqls (1)
3-3: New dbms_output merge entry is consistent with file layoutThe added path
src/builtin_packages/dbms_output/dbms_outputmatches the new package’s location and naming convention; no further changes needed here.contrib/ivorysql_ora/Makefile (1)
28-29: Build and regression wiring for DBMS_OUTPUT looks correct
- Object file path
src/builtin_packages/dbms_output/dbms_output.omatches the new C source.ora_dbms_outputentry inORA_REGRESScorresponds to the new SQL test file and is placed consistently with existing tests.
No further Makefile changes appear necessary.Also applies to: 73-75
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
Outdated
Show resolved
Hide resolved
- Fix potential integer overflow in buffer check by using subtraction instead of addition (line_bytes > buffer_size - buffer_used) - Normalize negative numlines parameter to 0 in GET_LINES
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (2)
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c (2)
175-200: Consider guardinglines_allocatedgrowth against pathological overflow
ensure_lines_capacitygrowslines_allocatedby 50% (with a +100 floor), which is a good amortized strategy. In the “unlimited” case, it’s theoretically possible to push a huge number of zero-byte lines (e.g., repeatedNEW_LINEcalls with no text), causinglines_allocatedto grow very large. At extreme scales,new_capacity(anint) could overflow beforerepallocis called.This is extremely pathological, but you could defensively cap
new_capacity(e.g., check fornew_capacity <= output_buffer->lines_allocatedand throw an error) to avoid ever wrapping theint.
241-262: Optional: handle parallel xact events if IvorySQL’s base Postgres exposes them
dbms_output_xact_callbackcurrently cleans up only onXACT_EVENT_ABORT,XACT_EVENT_COMMIT, andXACT_EVENT_PREPARE, and ignores all other events. On newer Postgres versions there are alsoXACT_EVENT_PARALLEL_*variants commonly handled alongside the base events.If IvorySQL’s underlying Postgres has those events, you may want to route
XACT_EVENT_PARALLEL_COMMIT/PARALLEL_ABORT/PARALLEL_PREPAREthrough the same cleanup path to avoid leaking buffers in parallel contexts; if not, this can be left as-is.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c (7)
src/backend/utils/mmgr/mcxt.c (6)
palloc0(1350-1370)MemoryContextDelete(453-488)pfree(1527-1541)repalloc(1547-1581)pstrdup(1702-1706)palloc(1320-1348)src/common/stringinfo.c (2)
makeStringInfo(71-75)resetStringInfo(125-134)src/backend/utils/fmgr/funcapi.c (1)
get_call_result_type(283-293)src/backend/executor/execTuples.c (1)
BlessTupleDesc(2314-2322)src/include/postgres.h (2)
Int32GetDatum(216-220)PointerGetDatum(326-330)src/include/funcapi.h (1)
HeapTupleGetDatum(336-340)src/backend/utils/adt/arrayfuncs.c (2)
construct_empty_array(3599-3610)construct_md_array(3513-3594)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: oracle_regression (ubuntu-latest)
- GitHub Check: build (ubuntu-latest)
- GitHub Check: oracle_pg_regression (ubuntu-latest)
- GitHub Check: meson_build (ubuntu-latest)
- GitHub Check: pg_regression (ubuntu-latest)
- GitHub Check: contrib_regression
🔇 Additional comments (5)
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c (5)
39-57: Buffer struct design and memory-context split look solidThe
DbmsOutputBufferlayout (separatebuffer_mcxt, byte-basedbuffer_used,read_position, and preallocatedlinesarray) is clear and aligns well with Postgres memory management patterns. No issues here.
148-173: Cleanup and xact-callback unregistration pattern looks correct
cleanup_output_buffercorrectly:
- No-ops when
output_buffer == NULL,- Unregisters the xact callback only when it was registered,
- Deletes
buffer_mcxt(freeing all lines andcurrent_line), and- Frees the
DbmsOutputBufferstruct and resets the global pointer.This matches typical Postgres extension patterns; no changes needed.
202-238: Overflow check and NULL-line handling inadd_line_to_bufferlook goodThe revised overflow check using subtraction (
line_bytes > buffer_size - buffer_used) avoids signed-addition overflow, and treating NULL lines as zero bytes with a NULL pointer inlines[]matches the intended semantics. Allocating copies inbuffer_mcxtis also correct.No further issues here.
297-444: Core PUT/PUT_LINE/NEW_LINE semantics and limits look correctThe
ora_dbms_output_disable,ora_dbms_output_put_line,ora_dbms_output_put, andora_dbms_output_new_linepaths:
- Correctly ignore output when the buffer is disabled or not yet enabled.
- Enforce the 32767-byte per-line limit on both pure
PUT_LINEcalls and accumulatedPUTtext, raising ORU-10028 consistently.- Treat
PUT(NULL)as a no-op, andPUT_LINE(NULL)/NULL lines as actual NULL entries in the buffer.- Flush pending
PUTcontent beforePUT_LINE, and makeNEW_LINEcreate an empty line when there’s no pending text.Given the regression tests you described, these match the intended behavior; I don’t see any functional issues here.
446-590: GET_LINE/GET_LINES tuple and array construction looks correct; negativenumlineshandling is cleanFor
ora_dbms_output_get_lineandora_dbms_output_get_lines:
- The
get_call_result_type+BlessTupleDesc+heap_form_tuplepattern is standard and correct.- NULL lines are preserved (NULL in buffer → NULL element / field in result).
- Status codes and counts are consistent (
status = 1when no more lines, empty TEXT[] + count 0 when requested_lines ≤ 0 or buffer is empty).- Normalizing negative
requested_linesto 0 avoids surprising negative counts and aligns with the prior review feedback.No further changes needed here.
Address CodeRabbit review feedback: - Change "full Oracle compatibility" to "high Oracle compatibility" - Clarify that re-ENABLE clearing buffer is IvorySQL behavior (not Oracle) - Clarify that buffer size range is IvorySQL-enforced (stricter than Oracle) - Reference GitHub issues #22 and #26 for tracking differences
Add dbms_output_line and dbms_output_lines composite types to the expected output for the collation sanity check query.
|
@bigplaice following up #971, this one is real implementation. |
| * | ||
| * IvorySQL-enforced range: 2000 to 1000000 bytes when explicitly specified. | ||
| * Note: Oracle silently clamps below-min values to 2000 and has no upper limit. | ||
| * See GitHub issue #22 for tracking this difference. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue #22 here is not of IvorySQL. Suggest to delete this line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed, was for rophy#22
| { | ||
| buffer_size = PG_GETARG_INT32(0); | ||
|
|
||
| /* IvorySQL-enforced range (stricter than Oracle, see issue #22) */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrong issue number?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
| * Note: Oracle silently clamps below-min values to 2000 and has no upper limit. | ||
| * See GitHub issue #22 for tracking this difference. | ||
| * | ||
| * Default (from SQL wrapper): 20000 bytes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default is unlimited when argument is null, not 20000.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed NULL to 1000000 to align with the scope of this PR.
| * | ||
| * IvorySQL behavior: ENABLE always clears existing buffer. | ||
| * Note: Oracle preserves buffer on re-ENABLE; this is an intentional | ||
| * IvorySQL simplification. See GitHub issue #26 for tracking. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that issue #26 is from somewhere else instead of IvorySQL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed, was meant to refer rophy#26
| else | ||
| { | ||
| output_buffer->lines_allocated = output_buffer->buffer_size / 80; | ||
| if (output_buffer->lines_allocated < 100) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be better to define macros for value 80 and 100.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment for the min/max size of buffer, i.e. 2000 and 1000000
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
| { | ||
| case XACT_EVENT_ABORT: | ||
| case XACT_EVENT_COMMIT: | ||
| case XACT_EVENT_PREPARE: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add XACT_EVENT_PARALLEL_COMMIT/XACT_EVENT_PARALLEL_ABORT, just like what plpgsql_xact_cb() does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
|
Thanks @rophy for this comprehensive DBMS_OUTPUT implementation! The code quality is good with clear structure, proper memory management, and thorough test coverage. I've identified a few issues that need to be addressed before merging: Oracle's Signature: Although IvorySQL has implicit casts between TEXT and VARCHAR2, these casts don't work for OUT parameters because the value cannot be "written back" through the cast. Suggest to add overloaded procedures for both
|
| * dbms_output_xact_callback | ||
| * | ||
| * Transaction callback to clean up buffer on COMMIT/ROLLBACK. | ||
| * Oracle behavior: buffer is transaction-scoped, not session-scoped. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Oracle, a message buffer is local to a single session. That means the buffer is session-scoped. Following test can be verified on Oracle 21c. The test result is "Read after COMMIT: Before COMMIT", which confirms the buffer is session-scoped instead of transaction-scoped.
SET SERVEROUTPUT OFF
-- 1. Test: What happens to buffer content after COMMIT?
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
DBMS_OUTPUT.PUT_LINE('Before COMMIT');
END;
/
COMMIT;
SET SERVEROUTPUT ON
-- 2. Read the buffer content and display it.
DECLARE
v_line VARCHAR2(32767);
v_status INTEGER;
BEGIN
DBMS_OUTPUT.GET_LINE(v_line, v_status);
IF v_status = 0 THEN
DBMS_OUTPUT.PUT_LINE('Read after COMMIT: ' || v_line);
ELSE
DBMS_OUTPUT.PUT_LINE('Buffer is empty after COMMIT');
END IF;
END;
/
The comment needs to be corrected.
Oracle behavior: buffer is transaction-scoped, not session-scoped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When SET SERVEROUTPUT ON is enabled in SQLPlus, the client automatically calls DBMS_OUTPUT.GET_LINES() after each PL/SQL block execution to retrieve and display all buffered output. This means the buffer is effectively cleared after each block, which may affect cross-block buffer persistence testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
contrib/ivorysql_ora/expected/ora_dbms_output.outis excluded by!**/*.outsrc/oracle_test/regress/expected/opr_sanity.outis excluded by!**/*.outsrc/test/regress/expected/opr_sanity_1.outis excluded by!**/*.out
📒 Files selected for processing (2)
contrib/ivorysql_ora/sql/ora_dbms_output.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:82-146
Timestamp: 2025-12-03T06:50:27.755Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the behavior where ENABLE() clears the existing buffer (rather than preserving it like Oracle does) is an intentional known difference tracked in issue #26 at https://github.com/rophy/IvorySQL/issues/26 (medium impact, open).
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:264-295
Timestamp: 2025-12-03T06:42:16.044Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the buffer size validation (2000–1000000 bytes) is an intentional IvorySQL-enforced constraint that diverges from Oracle behavior. The minimum constraint behavior is tracked in issue #22, and the maximum limit discussion is deferred as future work per rophy's conservative approach for initial implementation.
📚 Learning: 2025-12-03T06:42:16.044Z
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:264-295
Timestamp: 2025-12-03T06:42:16.044Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the buffer size validation (2000–1000000 bytes) is an intentional IvorySQL-enforced constraint that diverges from Oracle behavior. The minimum constraint behavior is tracked in issue #22, and the maximum limit discussion is deferred as future work per rophy's conservative approach for initial implementation.
Applied to files:
contrib/ivorysql_ora/sql/ora_dbms_output.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
📚 Learning: 2025-12-03T06:50:27.755Z
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:82-146
Timestamp: 2025-12-03T06:50:27.755Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the behavior where ENABLE() clears the existing buffer (rather than preserving it like Oracle does) is an intentional known difference tracked in issue #26 at https://github.com/rophy/IvorySQL/issues/26 (medium impact, open).
Applied to files:
contrib/ivorysql_ora/sql/ora_dbms_output.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
🧬 Code graph analysis (1)
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c (10)
src/include/utils/palloc.h (1)
MemoryContextSwitchTo(123-130)src/backend/utils/mmgr/mcxt.c (3)
palloc0(1350-1370)pfree(1527-1541)palloc(1320-1348)src/backend/access/transam/xact.c (2)
RegisterXactCallback(3804-3815)UnregisterXactCallback(3817-3836)src/backend/utils/error/elog.c (2)
errcode(853-864)errmsg(1070-1086)src/backend/utils/adt/varlena.c (1)
text_to_cstring(226-242)src/backend/executor/execTuples.c (1)
BlessTupleDesc(2314-2322)src/include/postgres.h (2)
Int32GetDatum(216-220)PointerGetDatum(326-330)src/backend/access/common/heaptuple.c (1)
heap_form_tuple(1149-1235)src/include/funcapi.h (1)
HeapTupleGetDatum(336-340)src/backend/utils/adt/arrayfuncs.c (2)
construct_empty_array(3599-3610)construct_md_array(3513-3594)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: meson_build (ubuntu-latest)
- GitHub Check: oracle_pg_regression (ubuntu-latest)
- GitHub Check: oracle_regression (ubuntu-latest)
- GitHub Check: contrib_regression
- GitHub Check: build (ubuntu-latest)
- GitHub Check: pg_regression (ubuntu-latest)
🔇 Additional comments (2)
contrib/ivorysql_ora/sql/ora_dbms_output.sql (1)
1-549: Comprehensive test coverage with clear structure.The test suite provides excellent coverage of DBMS_OUTPUT functionality across 9 well-organized sections with 33 test cases. The design principle of verifying every PUT with a corresponding GET operation ensures proper validation of buffering behavior.
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c (1)
1-601: Well-implemented Oracle-compatible DBMS_OUTPUT package.The C implementation demonstrates solid engineering:
- Proper memory management using dedicated memory contexts
- Correct transaction callback lifecycle management
- Oracle-compatible error handling (ORU-10027, ORU-10028)
- Faithful NULL semantics and line length enforcement (32767 bytes)
- All previously identified issues (overflow checks, negative numlines handling) have been addressed
The documented intentional differences from Oracle (buffer size validation and re-ENABLE behavior) are clearly commented and tracked in issues #22 and #26.
|
Another issue: Buffer memory is not recycled Currently,
This means:
Consider implementing immediate memory release on read or ring buffer is better. Any thought? :) |
|
This memory management issue may become worse when using enable(NULL). Currently enable(NULL) could allocate at most 2GB memory for debugging information output. This may bring safty risk to the backend process. Suggest changing 2GB to a smaller value. How about 200MB? |
Buffer now persists across COMMIT/ROLLBACK instead of being cleared. This matches Oracle's behavior where the buffer is local to the session. - Remove transaction callback registration - Remove dbms_output_xact_callback function - Remove callback_registered field from struct - Remove unused xact.h include
Test 10.1: Verify buffer persists after block ends (implicit commit) Test 10.2: Verify multiple blocks accumulate in buffer
Section 11 tests document current limitation where VARCHAR2 OUT parameters fail with "corresponding argument is not writable" error. Oracle's GET_LINE uses VARCHAR2 for OUT parameters, but IvorySQL's implementation uses TEXT. Implicit casts don't work for OUT parameters. Tests expect ERROR output to document current behavior before fix.
Oracle uses VARCHAR2 for GET_LINE and GET_LINES OUT parameters. When users declare variables as VARCHAR2 (common Oracle practice), implicit casts don't work for OUT parameters because the value cannot be written back through the cast. Add overloaded procedures: - get_line(line OUT VARCHAR2, status OUT INTEGER) - get_lines(lines OUT VARCHAR2[], numlines IN OUT INTEGER) This allows migrated Oracle code using VARCHAR2 to work correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
contrib/ivorysql_ora/expected/ora_dbms_output.outis excluded by!**/*.out
📒 Files selected for processing (3)
contrib/ivorysql_ora/sql/ora_dbms_output.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output--1.0.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
🚧 Files skipped from review as they are similar to previous changes (1)
- contrib/ivorysql_ora/sql/ora_dbms_output.sql
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:82-146
Timestamp: 2025-12-03T06:50:27.755Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the behavior where ENABLE() clears the existing buffer (rather than preserving it like Oracle does) is an intentional known difference tracked in issue #26 at https://github.com/rophy/IvorySQL/issues/26 (medium impact, open).
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:264-295
Timestamp: 2025-12-03T06:42:16.044Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the buffer size validation (2000–1000000 bytes) is an intentional IvorySQL-enforced constraint that diverges from Oracle behavior. The minimum constraint behavior is tracked in issue #22, and the maximum limit discussion is deferred as future work per rophy's conservative approach for initial implementation.
📚 Learning: 2025-12-03T06:50:27.755Z
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:82-146
Timestamp: 2025-12-03T06:50:27.755Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the behavior where ENABLE() clears the existing buffer (rather than preserving it like Oracle does) is an intentional known difference tracked in issue #26 at https://github.com/rophy/IvorySQL/issues/26 (medium impact, open).
Applied to files:
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output--1.0.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
📚 Learning: 2025-12-03T06:42:16.044Z
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:264-295
Timestamp: 2025-12-03T06:42:16.044Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the buffer size validation (2000–1000000 bytes) is an intentional IvorySQL-enforced constraint that diverges from Oracle behavior. The minimum constraint behavior is tracked in issue #22, and the maximum limit discussion is deferred as future work per rophy's conservative approach for initial implementation.
Applied to files:
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output--1.0.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: oracle_pg_regression (ubuntu-latest)
- GitHub Check: pg_regression (ubuntu-latest)
- GitHub Check: contrib_regression
- GitHub Check: oracle_regression (ubuntu-latest)
- GitHub Check: meson_build (ubuntu-latest)
- GitHub Check: build (ubuntu-latest)
🔇 Additional comments (3)
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c (1)
18-23: Documentation accurately reflects implementation scope.The header correctly describes the implementation as providing "high Oracle compatibility" (not "full") and directs readers to the test suite for behavioral differences. This addresses previous feedback about clarity.
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output--1.0.sql (2)
26-30: Default buffer size mismatch with implementation intent.The SQL wrapper defines the default as 20000 bytes (line 27), but the C implementation documents at lines 230-232 that NULL should map to the maximum (1000000 bytes) for safety. However, line 244-245 in the C code shows NULL actually does map to
DBMS_OUTPUT_MAX_BUFFER_SIZE(1000000).The issue: when callers use the SQL default (omit the argument), they get 20000, not 1000000. This means:
DBMS_OUTPUT.ENABLE()→ 20000 bytes (SQL default)DBMS_OUTPUT.ENABLE(NULL)→ 1000000 bytes (C NULL handling)If the intent is for the default to be 1000000 (as suggested by the C code comments and the PR objective to reduce the default for NULL to 200MB in future work), consider changing line 27 to
DEFAULT NULLso the C function handles it.Alternatively, if 20000 is the intentional default and NULL is just for "maximum when explicitly requested", the C comment at lines 231-232 should be updated to clarify this distinction.
113-143: OUT parameter wrapper implementation is correct.The package body properly implements OUT parameters for GET_LINE and GET_LINES by:
- Calling the C function via SELECT INTO a local composite variable
- Assigning the composite fields to the OUT parameters
The VARCHAR2 overloads (lines 121-127, 137-143) correctly reuse the same C functions, relying on PostgreSQL's implicit TEXT↔VARCHAR2 compatibility. This addresses the OUT parameter compatibility concern raised in the PR objectives.
- Ring buffer with length-prefixed strings (2 bytes per line overhead) - Dynamic growth: expands by min(buffer_size, max(DEFAULT_BUFFER_SIZE, capacity * GROWTH_FACTOR)) - ENABLE(NULL) creates unlimited buffer starting at DEFAULT_BUFFER_SIZE - Values < MIN_BUFFER_SIZE silently clamped to minimum (Oracle behavior) - Buffer space recycled when lines are read via GET_LINE/GET_LINES - Per-line overhead not counted toward user-specified limit (Oracle behavior)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c (1)
54-66: Consider potential overflow of head/tail pointers in extreme long-running sessions.The ring buffer uses
intforheadandtailpointers (lines 58-59), which are continuously incremented (lines 419, 670, 762) without resetting. While wrap-around is handled via modulo during access (line 287), the pointers themselves could theoretically overflowINT_MAX(~2.1B) in an extremely long-running session with massive data throughput.This is a minor theoretical concern with negligible real-world impact (would require writing/reading ~2GB worth of buffer operations), but for defensive robustness you could consider:
- Using
int64for head/tail to make overflow practically impossible- Or periodically normalizing head/tail (subtract tail from both) when the buffer is empty
Given the extremely low likelihood and the complexity of normalization, this is optional.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
contrib/ivorysql_ora/expected/ora_dbms_output.outis excluded by!**/*.out
📒 Files selected for processing (2)
contrib/ivorysql_ora/sql/ora_dbms_output.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:264-295
Timestamp: 2025-12-03T06:42:16.044Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the buffer size validation (2000–1000000 bytes) is an intentional IvorySQL-enforced constraint that diverges from Oracle behavior. The minimum constraint behavior is tracked in issue #22, and the maximum limit discussion is deferred as future work per rophy's conservative approach for initial implementation.
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:82-146
Timestamp: 2025-12-03T06:50:27.755Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the behavior where ENABLE() clears the existing buffer (rather than preserving it like Oracle does) is an intentional known difference tracked in issue #26 at https://github.com/rophy/IvorySQL/issues/26 (medium impact, open).
📚 Learning: 2025-12-03T06:42:16.044Z
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:264-295
Timestamp: 2025-12-03T06:42:16.044Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the buffer size validation (2000–1000000 bytes) is an intentional IvorySQL-enforced constraint that diverges from Oracle behavior. The minimum constraint behavior is tracked in issue #22, and the maximum limit discussion is deferred as future work per rophy's conservative approach for initial implementation.
Applied to files:
contrib/ivorysql_ora/sql/ora_dbms_output.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
📚 Learning: 2025-12-03T06:50:27.755Z
Learnt from: rophy
Repo: IvorySQL/IvorySQL PR: 998
File: contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c:82-146
Timestamp: 2025-12-03T06:50:27.755Z
Learning: In contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c, the behavior where ENABLE() clears the existing buffer (rather than preserving it like Oracle does) is an intentional known difference tracked in issue #26 at https://github.com/rophy/IvorySQL/issues/26 (medium impact, open).
Applied to files:
contrib/ivorysql_ora/sql/ora_dbms_output.sqlcontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: oracle_pg_regression (ubuntu-latest)
- GitHub Check: meson_build (ubuntu-latest)
- GitHub Check: oracle_regression (ubuntu-latest)
- GitHub Check: build (ubuntu-latest)
- GitHub Check: pg_regression (ubuntu-latest)
- GitHub Check: contrib_regression
🔇 Additional comments (14)
contrib/ivorysql_ora/sql/ora_dbms_output.sql (1)
1-719: Comprehensive test suite with excellent coverage.The test file is well-structured with 12 sections covering basic operations, edge cases, buffer limits, overflow scenarios, session scope, VARCHAR2 compatibility, and buffer space recycling. The tests align with the implementation's known behavioral differences from Oracle (re-ENABLE clearing buffer, buffer size constraints) and validate the critical ring buffer recycling behavior.
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.c (13)
18-28: LGTM - Header documentation is accurate.The header correctly describes session-level buffering and appropriately states "high Oracle compatibility" with a reference to tests for known differences. This addresses the previous feedback about avoiding "full compatibility" claims.
106-170: LGTM - Initialization logic is sound.The function correctly implements the intentional IvorySQL behavior (clearing buffer on re-ENABLE) with clear documentation. Memory context management is appropriate, and the initial capacity strategy (DEFAULT_BUFFER_SIZE for unlimited, full size for fixed) is efficient.
Based on learnings: The ENABLE clearing behavior is a known intentional difference tracked in issue #26.
172-190: LGTM - Cleanup is correct.The cleanup logic properly deletes the memory context (which automatically frees buffer and current_line) and resets the global pointer.
192-277: LGTM - Buffer expansion and packing logic is correct.The dynamic growth strategy is well-designed, and the ring buffer linearization correctly handles both contiguous and wrapped cases. The growth factor ensures reasonable allocation behavior.
279-354: LGTM - Ring buffer operations correctly handle wrap-around.All ring buffer read/write functions properly handle wrap-around cases, and uint16 encoding/decoding is consistent with little-endian byte order.
367-422: LGTM - Line addition logic is correct with proper overflow protection.The function correctly checks user-perceived limits using safe subtraction (avoiding integer overflow), expands internal storage when needed, and properly updates all accounting fields. The ring buffer space recycling is implemented (buffer_used is decremented in GET functions).
424-456: LGTM - ENABLE function correctly implements Oracle-compatible behavior.The function properly handles NULL (unlimited) and silently clamps below-minimum values to 2000 bytes, matching Oracle behavior.
Based on learnings: The buffer size constraints are intentional IvorySQL design choices tracked in issue #22.
458-471: LGTM - DISABLE function is correct.The function correctly implements Oracle behavior: disables buffering but preserves buffer content for subsequent GET operations.
473-536: LGTM - PUT_LINE function correctly implements Oracle semantics.The function properly handles NULL arguments, pending PUT text, and enforces the 32767-byte line length limit with appropriate error codes. The logic for combining pending PUT text with PUT_LINE is correct.
538-578: LGTM - PUT function correctly accumulates text without creating lines.The function properly handles NULL (treats as empty string), checks line length limits before appending, and accumulates text in current_line until NEW_LINE or PUT_LINE is called.
580-607: LGTM - NEW_LINE function correctly flushes accumulated PUT text.The function properly flushes current_line to the buffer and creates empty lines when no PUT text was accumulated, matching Oracle behavior.
609-676: LGTM - GET_LINE function correctly implements buffer space recycling.The function properly reads lines from the ring buffer and crucially decrements buffer_used and advances the tail pointer, allowing buffer space to be recycled for new writes (Oracle behavior). This addresses the memory reclamation concern raised in PR feedback.
678-783: LGTM - GET_LINES function correctly implements batch retrieval with recycling.The function properly handles negative numlines (normalizes to 0), reads multiple lines efficiently, implements buffer space recycling, and constructs 1-based arrays for Oracle compatibility.
|
Thanks for all the professional feedbacks. Commit 5fad8c8 changed to session-memory, removed the 100000 bytes limit. implemented ring buffer, and implemented ENABLE(NULL) unlimited expansion. I tried to test how oracle allocates memory. It seems oracle actually implemented some kind of linked list for dynamic memory allocation, while the implementation in this PR is different. A detailed report is here: rophy#29 With latest commit:
|
|
Hello @rophy , |
bigplaice
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no comments.
| * This file contains the native implementation of Oracle's DBMS_OUTPUT | ||
| * package for IvorySQL. | ||
| * | ||
| * Provides session-level buffering for PUT_LINE, PUT, NEW_LINE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, Rophy, session-level buffering is more reasonable and compatible with Oracle. However, this patch implements transaction-level buffer. Is there some reason to do so?
| /* Just add the line (may be NULL) */ | ||
| add_line_to_buffer(line_str); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need release the memory of line_str here.
| if (PG_ARGISNULL(0)) | ||
| PG_RETURN_VOID(); /* NULL appends nothing */ | ||
|
|
||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the brace can be removed.
| bool nulls[2] = {false, false}; | ||
| HeapTuple tuple; | ||
|
|
||
| requested_lines = PG_GETARG_INT32(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should consider the case of no input argument, and give requested_lines a default value?
Overview
DBMS_OUTPUT is an Oracle-compatible package that provides a simple interface for displaying output from PL/SQL (PL/iSQL) blocks, stored procedures, functions, and triggers. It buffers text output during execution and allows retrieval via GET_LINE/GET_LINES procedures.
Architecture
Module Location
Design Decision: DBMS_OUTPUT is implemented within the
ivorysql_oraextension because:Extension ordering:
plisqlloads beforeivorysql_oraduring database initialization (seeinitdb.c). This allowsivorysql_orato usePACKAGEsyntax which requires PL/iSQL.Oracle package grouping: DBMS_OUTPUT belongs with other Oracle-compatible built-in packages in
ivorysql_ora.Type compatibility: Uses PostgreSQL native
TEXTtype instead ofVARCHAR2to avoid circular dependencies. Implicit casts between TEXT and VARCHAR2 ensure transparent compatibility.Component Diagram
Memory Management
TopMemoryContextfor session-level persistenceRegisterXactCallbackto clear buffer on transaction endListstructureStringInfoAPI Reference
ENABLE
Enables output buffering with specified buffer size.
Notes:
DISABLE
Disables output buffering and clears the buffer.
PUT
PROCEDURE put(a TEXT);Appends text to current line without newline.
Notes:
PUT_LINE
PROCEDURE put_line(a TEXT);Appends text and completes the line.
Notes:
NEW_LINE
Completes current line (adds newline to buffer).
GET_LINE
Retrieves one line from the buffer.
GET_LINES
Retrieves multiple lines from the buffer.
Implementation Details
Buffer Structure
Error Handling
Transaction Behavior
Test Coverage
Tests located in
contrib/ivorysql_ora/sql/ora_dbms_output.sqlTotal: 33 test cases
Oracle Compatibility
Comparison Summary
Compatibility Rate: 91% (30/33 tests pass)
Detailed Differences
1. Re-ENABLE Behavior
IvorySQL:
Oracle:
Impact: Medium. Applications that call ENABLE() multiple times may see different behavior.
2. Buffer Size Limits (#22)
IvorySQL: Enforces strict range 2000-1000000 bytes, rejects values outside.
Oracle: Minimum 2000 bytes (values below silently adjusted up), maximum unlimited. Per Oracle docs.
Impact: Low. IvorySQL is stricter but catches invalid values early.
Status: Open issue - should silently adjust values below 2000 instead of rejecting.
Compatibility Recommendations
For maximum compatibility:
Migration considerations:
Files Modified
contrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output.ccontrib/ivorysql_ora/src/builtin_packages/dbms_output/dbms_output--1.0.sqlcontrib/ivorysql_ora/Makefilecontrib/ivorysql_ora/meson.buildcontrib/ivorysql_ora/ivorysql_ora_merge_sqlsFuture Enhancements
Summary by CodeRabbit
New Features
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.