Skip to content
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

JTDS Driver support #2320

Open
wants to merge 28 commits into
base: BABEL_4_X_DEV
Choose a base branch
from

Conversation

staticlibs
Copy link
Contributor

Description

This patch contains initial working implementation of JTDS support. With it applied, Babelfish DB can be opened and browsed from DBeaver (using "MS SQL Server / SQL Server (Old driver, jTDS)" connection setup included there). Both ssl=off and ssl=require modes are supported.

Two problems with JTDS were identified:

  1. no PRELOGIN request sent when SSL is off

  2. Babelfsh uses Partially Length-Prefixed bytes (PLP) [1] for returning varchar and nvarchar fields. PLP was introduced in TDS 7.2, but JTDS only supports TDS 7.1

Patch adds a message type check before reading PRELOGIN and a TDS version check to decide whether to use PLP responses.

Known shortcomings of the patch:

  1. no test runs with JTDS driver (would appreciate pointers how to introduce them, perhaps run a subset of existing JDBC tests with JTDS?)

  2. not clear whether the patch covers all necessary PLP cases (should Spatial responses be changed too?)

[1] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/3f983fde-0509-485a-8c40-a9fa6679a828

Issues Resolved

#2137

Check List

  • Commits are signed per the DCO using --signoff

By submitting this pull request, I confirm that my contribution is under the terms of the Apache 2.0 and PostgreSQL licenses, and grant any person obtaining a copy of the contribution permission to relicense all or a portion of my contribution to the PostgreSQL License solely to contribute all or a portion of my contribution to the PostgreSQL open source project.

For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@coveralls
Copy link
Collaborator

coveralls commented Jan 30, 2024

Pull Request Test Coverage Report for Build 9709042874

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 130 of 354 (36.72%) changed or added relevant lines in 7 files are covered.
  • 1642 unchanged lines in 19 files lost coverage.
  • Overall coverage decreased (-0.1%) to 72.971%

Changes Missing Coverage Covered Lines Changed/Added Lines %
contrib/babelfishpg_tsql/src/pl_exec-2.c 8 12 66.67%
contrib/babelfishpg_tds/src/backend/tds/tdslogin.c 13 18 72.22%
contrib/babelfishpg_tsql/src/cursor.c 41 53 77.36%
contrib/babelfishpg_tds/src/backend/tds/tdsrpc.c 6 26 23.08%
contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c 12 36 33.33%
contrib/babelfishpg_tds/src/backend/tds/tdstimestamp.c 1 77 1.3%
contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c 49 132 37.12%
Files with Coverage Reduction New Missed Lines %
contrib/babelfishpg_tsql/src/cursor.c 1 73.85%
contrib/babelfishpg_common/src/typecode.c 1 96.84%
contrib/babelfishpg_tds/src/backend/tds/tdstimestamp.c 2 72.7%
contrib/babelfishpg_tds/src/backend/tds/tdsrpc.c 2 70.85%
contrib/babelfishpg_tds/src/backend/tds/tdslogin.c 2 76.28%
contrib/babelfishpg_tds/src/backend/tds/tdscomm.c 3 73.33%
contrib/babelfishpg_tsql/src/codegen.c 6 95.32%
contrib/babelfishpg_tsql/src/stmt_walker.c 13 67.23%
contrib/babelfishpg_tsql/src/iterative_exec.c 22 83.52%
contrib/babelfishpg_tsql/src/hooks.c 23 79.71%
Totals Coverage Status
Change from base Build 9596046248: -0.1%
Covered Lines: 42864
Relevant Lines: 58741

💛 - Coveralls

@staticlibs staticlibs force-pushed the jtds_issue_2137 branch 2 times, most recently from 37d2152 to 29ead71 Compare February 20, 2024 21:18
@staticlibs
Copy link
Contributor Author

Thanks for your comments and code pointers!

In patch update I've added support for running JDBC tests with JTDS. When useJTDSInsteadOfMSSQLJDBC test config options s set to true, then tests are read from a separate jtds_jdbc_schedule instead of the main jdbc_schedule file. And JTDS driver is used instead of mssql-jdbc one in all places in test suite where mssql-jdbc was used. It is not possible to have a mixed test that will use both JTDS and mssql-jdbc, I assumed this scenario is not necessary.

JTDS-specific tests jtds-varchar-max-length-check.sql and jtds-nvarchar-max-length-check.sql are added to check insert and select behaviour for varchar(max) and nvarchar(max). Failures are currently commented out there, need to uncommented before running. Server handling of oversized varchar(max) and nvarchar(max) appeared to be different (added notes on this in test comments). I was not able to quickly reproduce oversized insert failures I've seen in ad-hoc tests, going to experiment more with them.

The intention here is to have a minimal working test-suite (should it be enabled as an additional GitHub action run?) with a minimal working JTDS patch, and then to iterate on the JTDS specific problems gradually extending the test suite. Please let me know whether such approach (basically to merge PR with the current backend patch and follow up in subsequent PRs) is acceptable, or whether JTDS support needs to be substantially improved (with the details from your previous comments) before merging the PR? In the latter case I can iterate on test suite in PR branch. I also can improve/adjust test harness changes as needed (tried to keep its changes to minimum).

@Deepesh125
Copy link
Contributor

Thanks for your comments and code pointers!

In patch update I've added support for running JDBC tests with JTDS. When useJTDSInsteadOfMSSQLJDBC test config options s set to true, then tests are read from a separate jtds_jdbc_schedule instead of the main jdbc_schedule file. And JTDS driver is used instead of mssql-jdbc one in all places in test suite where mssql-jdbc was used. It is not possible to have a mixed test that will use both JTDS and mssql-jdbc, I assumed this scenario is not necessary.

JTDS-specific tests jtds-varchar-max-length-check.sql and jtds-nvarchar-max-length-check.sql are added to check insert and select behaviour for varchar(max) and nvarchar(max). Failures are currently commented out there, need to uncommented before running. Server handling of oversized varchar(max) and nvarchar(max) appeared to be different (added notes on this in test comments). I was not able to quickly reproduce oversized insert failures I've seen in ad-hoc tests, going to experiment more with them.

The intention here is to have a minimal working test-suite (should it be enabled as an additional GitHub action run?) with a minimal working JTDS patch, and then to iterate on the JTDS specific problems gradually extending the test suite. Please let me know whether such approach (basically to merge PR with the current backend patch and follow up in subsequent PRs) is acceptable, or whether JTDS support needs to be substantially improved (with the details from your previous comments) before merging the PR? In the latter case I can iterate on test suite in PR branch. I also can improve/adjust test harness changes as needed (tried to keep its changes to minimum).

I agree with your suggested iterative approach. But there are still some areas where we dont have clarity yet, such as, what all is not working for JTDS?, We can file separate issues for them but who will be owner to drive that? How are we going to track all such details?

And on side note, we should have separate GH action to test with JTDS driver.

@staticlibs
Copy link
Contributor Author

I agree with your suggested iterative approach. But there are still some areas where we dont have clarity yet, such as, what all is not working for JTDS?

I don't have the full picture yet. Basic DDL/DML/selects are working fine. Introspection also working (at least it is enough for DBeaver DB browser). But there are failures with inserts and selects for (n)varchar types, for lengths less then 0xfffe so they seem to be not directly related to (N)TEXTTYPE. Known failures so far with test coverage:

  1. nvarchar inserts with the length 4096 or longer fail on backend

  2. nvarchar selected data with length 16384 or more is returned by backend, but cannot be read by JTDS

  3. varchar selected data with length over 65535 is not returned by backend, instead of data the backend error message is returned to client

  4. connection handling in JTDS is somehow different from mssql-jdbc and pgjdbc, when 2. and 3. failures above happen, JTDS cannot reopen the connection for subsequent test statements, I assume this needs to be investigated and fixed in the test harness

I intend to add more existing datatype tests to JTDS runs.

We can file separate issues for them but who will be owner to drive that?

I am ready to continue with this task, cannot do that full time, but will be able to spare a few hours per week for it.

How are we going to track all such details?

I see 2 options: either commit the basic working test harness and then track each failure in a separate PR, or add more commits to this PR without merging it. I am fine with doing it either way.

And on side note, we should have separate GH action to test with JTDS driver.

I've added GH action run with JTDS, it is a copy of existing JDBC workflow with useJTDSInsteadOfMSSQLJDBC variable set.

@thephantomthief
Copy link
Contributor

Hi @staticlibs,

Thanks for adding support for the jTDS driver in JDBC Test Framework. You have already added tests for binary, varchar(max) and nvarchar(max) but to ensure we are fully able to support jTDS driver capabilities for Babelfish we will need to test the following:

  • Datatypes
  • Prepared Statements
  • Callable Statements
  • Transactions
  • Cursors

We already have tests added for the above, and you can reuse the same tests to test Babelfish with jTDS driver. Similar to how you are reusing the TestBinary file. You can try adding the following files in your jTDS schedule file as a start:

  • Datatypes
    • TestInt.txt
    • TestSmallInt.txt
    • TestTinyInt.txt
    • TestBigInt.txt
    • TestBit.txt
    • TestDecimal.txt
    • TestNumeric.txt
    • TestMoney.txt
    • TestFloat.txt
    • TestReal.txt
    • TestDatetime.txt
    • TestDate.txt
    • TestDatetime2.txt
    • TestSmallDatetime.txt
    • TestTime.txt
    • TestDateTimeOffset.txt
    • TestVarChar.txt
    • TestText.txt
    • TestChar.txt
    • TestBinary.txt
    • TestImage.txt
    • TestSqlVariant.txt
    • TestXML.txt
    • TestTableTypeFile.txt
    • TestUniqueIdentifier.txt
  • SQL Batch
    • TestSQLQueries.txt
    • TestTransactionsSQLBatch.txt
    • TestSimpleErrorHandling.sql
    • TestErrorsWithTryCatch.sql
  • Callable Statements
    • TestStoredProcedures.txt
  • Cursors
    • TestCursorFetchNext.txt
    • TestCursorFetchPrev.txt
    • TestCursorPrepExecFetchNext.txt
    • TestCursorPrepExecFetchPrev.txt
  • Transactions
    • TestTransactions.txt
    • TestTransactionSupportForProcedure.txt

Also, we need to make sure that the APIs we use in the JDBC test framework are compatible with the jTDS driver. If that is not the case, we will need to add support for the jTDSised version of the APIs in the framework. Additionally support for jTDS specific APIs (if any) should be added with tests for the same.

@staticlibs
Copy link
Contributor Author

Thanks for the details! And apologies for lack of activity on this, I was side-tracked by other Babelfish problems (prompted by usage of Cognos Analytics (#2404) and pgAgent (#2413) with Babelfish). I intend to return to this issue next week.

About the JDBC APIs: I am not too concerned about jTDS problems in the test harness (going to adjust the test harness as needed), but there is a concern about the public JDBC API (besides query execution) that is used by client apps. Foremost is the DatabaseMetaData, it can run non-trivial SQL for some of the calls and currently Babelfish is not fully compatible with it on mssql-jdbc (#997), let alone jTDS. I have a separate PR in preparation to add test coverage to these metadata calls (with new dbmeta#!#... commands in .txt tests) to be able to test these calls on both mssql-jdbc and jTDS (will add more details to that new PR when it is ready). Agree that driver-specific API coverage may be needed after the public API is covered.

@staticlibs
Copy link
Contributor Author

I've added all suggested existing tests to jTDS runs. For tests that have different output on jTDS, but behave the same on both MSSQL and Babelfish, I've created copies with jtds- prefix and adjusted output.

The following tests are found to be causing issues when run on jTDS:

  • TestTinyInt
  • TestDecimal
  • TestDatetime2
  • TestDatetimeoffset
  • TestVarChar
  • TestText
  • TestSQLVariant
  • TestXML
  • TestTableType
  • TestSimpleErrors
  • TestErrorsWithTryCatch
  • TestCursorFetchNext
  • TestCursorPrepExecFetchNext
  • TestTransactionSupportForProcedure

Currently test runs are disabled for these tests, I will follow-up on them one by one.

@@ -61,6 +61,9 @@ else if(e instanceof SQLServerException) {
}
bw.write("~~ERROR (Message: "+ errorMsg + ")~~");
}
else { // JTDS throws plain SQLServerException
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean SQLException? We already handle SQLServerException in above else if condition

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a typo, it throws plain SQLException. Will fix this and give harness changes another pass.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. The test framework changes look good to me. Could you let us know how you plan on following up on the tests that are failing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the following tests:

  • TestSimpleErrors
  • TestErrorsWithTryCatch
  • TestTransactionSupportForProcedure

They work correctly on jTDS, but behaviour of JDBC API methods (and the test output because of this) is slightly different. I intend to proceed with Cursors, XML and Varchar tests (that use methods unsupported on jTDS) and with remaining tests after that.

@staticlibs
Copy link
Contributor Author

Added TableType tests, they work correctly with slightly different output.

Investigated Cursor tests: cursors usage with jTDS is not supported on Babelfish. jTDS calls sp_cursorfetch procedure with unsupported FETCH_INFO option.

@staticlibs
Copy link
Contributor Author

Added TestXML, it works correctly with minor differences in metadata output.

@staticlibs staticlibs force-pushed the jtds_issue_2137 branch 3 times, most recently from a543b53 to fe7419a Compare May 5, 2024 18:06
@staticlibs
Copy link
Contributor Author

Remaining test failures (with comments for corresponding test groups):

  • TestDecimal
  • TestDatetime2

Fractional parts of records inserted with jTDS differ from ones inserted with mssql-jdbc.

  • TestDatetimeoffset

select output differs when datetimeoffset is being cast to time or datetime2.

  • TestVarChar
  • TestText

insert failure with invalid data length or metadata length for long input values.

  • TestSQLVariant

Multiple failures on both insert and select, have not looked deeper yet.

  • TestCursorFetchNext
  • TestCursorPrepExecFetchNext

These tests use cursor options unsupported on Babelfish.

@staticlibs
Copy link
Contributor Author

Fixed the problems with time and datetime2 fractional seconds and fixed the length check for incoming (n)varchar(max) long strings. Remaining failures:

  • TestDecimal
  • TestDatetime2
  • TestSQLVariant
  • TestCursorFetchNext
  • TestCursorPrepExecFetchNext

@staticlibs
Copy link
Contributor Author

Added support for returning sql_variant date values as nvarchar, it is clear now how to fix remaining TestSQLVariant failures with other date/time types.

@staticlibs
Copy link
Contributor Author

Added support for sp_cursorprepare, this fixes TestCursorPrepExecFetchNext with the exception of cursor.last() that is excluded the same way as with TestCursorFetchNext.

Remaining failures (fractional parts of records inserted with jTDS differ from ones inserted with mssql-jdbc):

  • TestDecimal
  • TestDatetime2

@staticlibs
Copy link
Contributor Author

Fixed the remaining tests:

For TestDecimal filed #2678 and added a workaround described there (don't know if this workaround should go in - up to reviewers).

For TestDatetime2, added rounding up for fractional seconds conversion - there seems to be no side effects from it and MSSQL is doing it.

With all tests from the initial list passing now on jTDS - requesting a review: @Deepesh125 , @thephantomthief .

@staticlibs
Copy link
Contributor Author

Just for the record (if someone will need this for release notes in future), useCursors jTDS option (all options list) is not supported on Babelfish. This option is false by default. When it is explicitly enabled in connection string, then jTDS sends the following query to sp_cursoropen RPC call when connection is opened:

SELECT @@MAX_PRECISION
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SET IMPLICIT_TRANSACTIONS OFF
SET QUOTED_IDENTIFIER ON
SET TEXTSIZE 2147483647

This query is failing with syntax error at or near "TRANSACTION" when it is passed to SPI_prepare_cursor.

@@ -1989,41 +1989,45 @@ TdsProcessLogin(Port *port, bool loadedSsl)
TdsErrorContext->phase = 0;
TdsErrorContext->reqType = TDS_LOGIN7;

PG_TRY();
if (!TdsReadNextBuffer() && TdsCheckMessageType(TDS_PRELOGIN))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Let's add a comment why we may not expecting TDS_PRELOGIN as the first request.
  2. The check should be
if (TdsReadNextBuffer() != EOF && TdsCheckMessageType(TDS_PRELOGIN))
  1. If the prelogin message is not sent, what's the SSL behavior - TDS_ENCRYPT_NOT_SUP or TDS_ENCRYPT_OFF? OFF means the login will still happen in an encrypted manner.
  2. According the code, the loadEncryption defaults to 0 which is TDS_ENCRYPT_OFF. So, we're freeing the ssl structure towards the end of the function without setting up the socket. Also, according to the code, the login is probably not happening in an encrypted manner.

Actually, let's not modify the SSL code at all. Something like as following (after confirming the SSL behavior):

/* comment why this is needed */
if (TdsReadNextBuffer() != EOF && TdsCheckMessageType(TDS_PRELOGIN))
{
        TdsErrorContext->err_text = "Parsing PreLogin Request";
        /* Pre-Login */
        rc = ParsePreLoginRequest();
        if (rc < 0)
            return rc;

        TdsErrorContext->err_text = "Make PreLogin Response";

        loadEncryption = MakePreLoginResponse(port, loadedSsl);
        TdsFlush();
}
else
{
        TDS_DEBUG(TDS_DEBUG3, "TDS7 Prelogin Message Skipped");
        loadEncryption = TDS_ENCRYPT_OFF;
}

@@ -1785,6 +1785,7 @@ PrepareRowDescription(TupleDesc typeinfo, PlannedStmt *plannedstmt, List *target
}

SetAttributesForColmetada(col);
col->atttypmod = atttypmod;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? The actual typmod is calculated based on the datatype. Can we rely on this value?

* [+|-]hh:mm'). and Making use of default collation Oid.
*/
SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 64);
SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 68);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we added a testcase for this with TDS version lower than 7.3A?

Comment on lines +2397 to +2432
/*
* SendReturnValueIntInternal
*
* status - stored procedure (0x01) or UDF (0x02)
*/
void
SendReturnValueIntInternal(uint8 status, int32 value)
{
SendPendingDone(true);

/* token type */
TDS_DEBUG(TDS_DEBUG2, "SendReturnValueIntInternal: token=0x%02x", TDS_TOKEN_RETURNVALUE);

/* TokenType */
TdsPutUInt8(TDS_TOKEN_RETURNVALUE);
/* ParamOrdinal */
TdsPutUInt16LE(0x0d);
/* ParamName */
TdsPutUInt8(0);
/* Status */
TdsPutUInt8(status);
/* UserType */
if (GetClientTDSVersion() <= TDS_VERSION_7_1_1)
TdsPutUInt16LE(0);
else
TdsPutUInt32LE(0);
/* Flags */
TdsPutUInt16LE(0);
/* TypeInfo */
TdsPutUInt8(TDS_TYPE_INTEGER);
TdsPutUInt8(0x04);
/* Value */
TdsPutUInt8(0x04);
TdsPutInt32LE(value);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we not using SendReturnValueTokenInternal()? We can create an empty parameter token using MakeEmptyParameterToken() and call the same function -

returnToken = MakeEmptyParameterToken("", TDS_TYPE_INTEGER, 4, 0);
returnToken->paramOrdinal = 0;

SendReturnValueTokenInternal(returnToken, 0x01, NULL,
                                 UInt32GetDatum(value), false, false);

Comment on lines 3627 to +3632
GenerateBindParamsData(req);
HandleSPCursorOpenCommon(req);
break;
case SP_CURSORPREPARE:
HandleSPCursorOpenCommon(req);
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GenerateBindParamsData() inititlizes some critical member of TDSRequest structure. Why are we not calling the same?

Comment on lines +2444 to +2450
if (req->spType == SP_CURSORPREPARE)
{
SendReturnValueIntInternal(1, req->cursorPreparedHandle);
TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0);
}
else
SendCursorResponse(req);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not modify SendCursorResponse and handle the scenarios if it's not handling a case already?

Comment on lines +2664 to +2665
SendReturnValueIntInternal(1, rownum);
SendReturnValueIntInternal(1, nrows);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be

  if (req->cursorExtraArg2 && (req->cursorExtraArg2->flags & 0x01) == 1)
        SendReturnValueTokenInternal(req->cursorExtraArg2 0x01, NULL,
                                     UInt32GetDatum(rownum), false, false);

  if (req->cursorExtraArg3 && (req->cursorExtraArg3->flags & 0x01) == 1)
        SendReturnValueTokenInternal(req->cursorExtraArg3, 0x01, NULL,
                                     UInt32GetDatum(nrows), false, false);

Comment on lines +2786 to +2789
if (GetClientTDSVersion() <= TDS_VERSION_7_1_1 &&
col->metaEntry.type3.tdsTypeId == TDS_TYPE_TEXT)
return TdsSendTypeText(finfo, value, vMetaData);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're handling it here, why do we need to handle in PrepareRowDescription as well? Also, this is not setting sendTableName to true although we're sending as text.

@@ -1989,41 +1989,45 @@ TdsProcessLogin(Port *port, bool loadedSsl)
TdsErrorContext->phase = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really really large PR. We shouldn't merge this PR as a single commit as this is handling several new features together. Each feature requires its own explanation in the commit message:

Let's split the PR as per following:

  1. JDBC test framework changes for jTDS driver testing
  2. Prelogin changes
  3. SP_cursorprepare
  4. SP_cursorfetch with fetchinfo
  5. Datatype changes (Individual datatypes should be addressed independently if the fixes are not related)

Each PR should be backed by respective testcases instead of putting all the test case in a single PR.

if (req->dataParameter->isNull || req->metaDataParameterValue->len == 0)
paramdefstr = NULL;
else
paramdefstr = req->metaDataParameterValue->data;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my understanding -- what is the test case? and how would this paramdefstr look?

Comment on lines +2444 to +2450
if (req->spType == SP_CURSORPREPARE)
{
SendReturnValueIntInternal(1, req->cursorPreparedHandle);
TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0);
}
else
SendCursorResponse(req);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This basically states that we never tested this SP_CURSORPREAPRE code? how are we ensuring that we have enough test cases for this now?

@@ -1989,41 +1989,45 @@ TdsProcessLogin(Port *port, bool loadedSsl)
TdsErrorContext->phase = 0;
TdsErrorContext->reqType = TDS_LOGIN7;

PG_TRY();
if (!TdsReadNextBuffer() && TdsCheckMessageType(TDS_PRELOGIN))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can have more safer check by checking whether client has ssl=off. If that's the case then only we should skip pre-login msg. Throw an FATAL error otherwise and should terminate connection

Comment on lines +50 to +51
InlineCodeBlockArgs *create_args(int numargs);
void read_param_def(InlineCodeBlockArgs *args, const char *paramdefstr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extern?

@@ -2051,10 +2083,10 @@ PrepareRowDescription(TupleDesc typeinfo, PlannedStmt *plannedstmt, List *target
/*
* If client being connected is using TDS version lower
* than 7.3A then TSQL treats DATETIMEOFFSET as NVARCHAR.
* Max len here would be 64('YYYY-MM-DD hh:mm:ss[.nnnnnnn]
* Max len here would be 68('YYYY-MM-DD hh:mm:ss[.nnnnnnn]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

Comment on lines +630 to +650
char*
TdsTimeGetTimeAsString(TimeADT value, int scale)
{
struct pg_tm tt,
*tm = &tt;
fsec_t fsec;
char *buf;

memset(tm, '\0', sizeof(struct pg_tm));
time2tm(value, tm, &fsec);

buf = palloc0(16 + 1);
if (!pg_strftime(buf, 10 + 1, "%H:%M:%S", tm))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range")));

AppendFractionalSeconds(buf, fsec, scale);

return buf;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explain why is this needed instead of directly calling time_out? Worth adding comment explaining the same. Only thing I could make out this is to match the provided scale by appending extra zeros which can be very well achieved by appending zeros to output of time_out. What am I missing?

buf[idx] = '0';
}

for (idx = 0; idx < scale; idx++)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If column is defined with TIME(7) then atttypmod = -1 and hence scale will also be -1. we will never ever get into this loop. But this part of code comes into picture only for TDS client with version < 7.3 for which we always need to set scale to 7 (DATETIMEOFFSETMAXSCALE) if stored attypmod is -1. Our defined buf is also not able to handle this length 7.

I am little bit more worried now in terms of how are testing this code, if existing test coverage good enough to save some seg fault. I am still inclined towards reusing time_out and either append zeros or truncate if required. Truncate will never be needed though.

@@ -3355,6 +3411,33 @@ TdsSendTypeTime(FmgrInfo *finfo, Datum value, void *vMetaData)
return rc;
}

static int
TdsSendDatetime2AsNVarcharHelper(FmgrInfo *finfo, Datum value, void *vMetaData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as TdsSendTimeAsNVarcharHelper, please evaluate if we really need to do this?

@@ -4155,6 +4325,33 @@ TdsRecvTypeDatetimeoffset(const char *message, const ParameterToken token)
return result;
}

static int
TdsSendDatetimeoffsetAsNVarcharHelper(FmgrInfo *finfo, Datum value, void *vMetaData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please reconsider if this is really needed?

Comment on lines +3858 to +3863
/*
* dataformat: totalLen(4B) + baseType(1B) + metadatalen(1B) +
* encodingLen(5B) + dataLen(2B) + data(dataLen)
*/
static int
TdsSendTypeSqlvariantAsNVarcharHelper(const char *st)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can cause confusion with regular nvarchar sender for sql_variant datatype. Please add comment stating why is it needed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of this code shares common code with TdsSendTypeSqlvariant to send string data, please consider if we can move this into some common helper code. It will help maintain code in long term

@staticlibs
Copy link
Contributor Author

@kuntalghosh , @Deepesh125 , thanks for the reviews! I've filed #2860 with the PRELOGIN change and copied review comments there. Going to follow up with JDBC test framework changes.

@staticlibs
Copy link
Contributor Author

Submitted test harness changes in #2861.

@staticlibs
Copy link
Contributor Author

Just FYI, I will be able to respond to review comments and follow up with cursor and datatype changes in about 2 weeks.

@sftim
Copy link

sftim commented Oct 3, 2024

@staticlibs, what support would help you best to move this forward?

@staticlibs
Copy link
Contributor Author

@sftim

#2860 needs to be pushed (AFAIU it passed the review) to proceed with #2861 and the following parts of this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants