[coverage] Conformance findings: STATEMENT-025,STATEMENT-026

Open
#480 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
52/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
go, sql
Domain
backend, database

Research direction

Start with TestWriteStatementReportsAffectedRowsWithoutAResultSet and TestReusedStatementClearsResultStateAfterAWrite in the coverage PR under tests/, then compare the intended behavior with reference PR 433. Trace the databricks-sql-go statement execution and result metadata paths; done means DML/DDL expose affected counts without a fetchable result set, reused handles report zero columns after writes, and ordinary SELECT results remain correct.

Written by the indexing model from the issue text.

Description

Summary

Surfaced by the multi-language coverage fan-out while conformance-testing these SPEC-IDs against databricks/databricks-sql-go. Each finding is committed as an expected-failure (xfail) test in the coverage PR — the test asserts the CORRECT (post-fix) behavior and stays red until THIS driver (databricks/databricks-sql-go) is fixed, then flips green as a tripwire.

Findings

  • STATEMENT-025 [thrift]: Write statements publish the protocol affected-row counter record as a fetchable application result set (insert/cte_insert -> [num_affected_rows num_inserted_rows], merge -> [num_affected_rows num_updated_rows num_deleted_rows num_inserted_rows], DDL -> [Result]) instead of treating it as transport metadata, so an INSERT looks like a one-row SELECT.
    • failing test: TestWriteStatementReportsAffectedRowsWithoutAResultSet (see the coverage PR diff under tests/)
  • STATEMENT-025 [sea]: On the SEA/kernel backend the DML counter row is likewise published as a fetchable application result set (insert/cte_insert -> [num_affected_rows num_inserted_rows], merge -> 4 counter columns), so a write is indistinguishable from a one-row SELECT.
    • failing test: TestWriteStatementReportsAffectedRowsWithoutAResultSet (see the coverage PR diff under tests/)
  • STATEMENT-026 [thrift]: On a reused connection handle a resultless DML/DDL statement still publishes the counter record as a fetchable result set (write_after_select/prepared_switch_execute -> [num_affected_rows num_inserted_rows], ddl_after_select -> [Result]), so the handle never reports zero result columns after a write.
    • failing test: TestReusedStatementClearsResultStateAfterAWrite (see the coverage PR diff under tests/)
  • STATEMENT-026 [sea]: Same reused-handle violation on the SEA/kernel backend: write_after_select and prepared_switch_execute publish [num_affected_rows num_inserted_rows] instead of zero result columns.
    • failing test: TestReusedStatementClearsResultStateAfterAWrite (see the coverage PR diff under tests/)
  • STATEMENT-025: A write statement's protocol affected-row counter record is published to the application as a fetchable result set (INSERT/cte_insert -> [num_affected_rows num_inserted_rows], MERGE -> [num_affected_rows num_updated_rows num_deleted_rows num_inserted_rows], DDL -> [Result] on thrift) instead of being treated as transport metadata, so an INSERT looks like a one-row SELECT and callers that branch on "did this statement return rows?" take the wrong branch. Affected on both thrift and sea; the affected-row counts themselves are correct.
  • STATEMENT-026: On a reused connection handle a resultless DML/DDL statement still publishes the protocol counter record as a fetchable result set (write_after_select / prepared_switch_execute -> [num_affected_rows num_inserted_rows]; thrift ddl_after_select -> [Result]), so the handle never reports zero result columns after a write. The stale-state half is correct — the previous query's columns are not inherited — but column_count: 0 is violated on both thrift and sea.

Reproduce & Expected

STATEMENT-025 — Validates the RESULT KIND of write statements: a DML statement exposes its affected-row count and NO fetchable result set, and a DDL statement exposes neither.

Reproduce:

INSERT INTO {tableName} VALUES (1, 'a'), (2, 'b')
UPDATE {tableName} SET name = 'c' WHERE id = 2
DELETE FROM {tableName} WHERE id = 1
DELETE FROM {tableName} WHERE id = -12345
MERGE INTO {tableName} AS t
USING (SELECT 2 AS id, 'merged' AS name) AS s
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET t.name = s.name
WHEN NOT MATCHED THEN INSERT (id, name) VALUES (s.id, s.name)
WITH src AS (SELECT 3 AS id, 'd' AS name) INSERT INTO {tableName} SELECT id, name FROM src
ALTER TABLE {tableName} ADD COLUMN extra STRING
TRUNCATE TABLE {tableName}
SELECT CAST(7 AS BIGINT) AS num_affected_rows
WITH c AS (SELECT CAST(7 AS BIGINT) AS num_affected_rows) SELECT * FROM c
TABLE {counterViewName}

Expected (per the shared spec):

  • completes without an exception
  • completes without an exception
  • completes without an exception
  • completes without an exception
  • completes without an exception
  • completes without an exception
  • completes without an exception
  • completes without an exception
  • completes without an exception
  • result has exactly 1 row(s)
  • result has 1 column(s)
  • col 0, row 0 == 7 (type Int64)
  • completes without an exception
  • result has exactly 1 row(s)
  • col 0, row 0 == 7 (type Int64)
  • completes without an exception
  • result has exactly 1 row(s)
  • col 0, row 0 == 7 (type Int64)
  • full assertion contract:
result:
- label: insert
  no_exception: true
- label: insert
  no_result_set: true
- label: insert
  affected_row_count: 2
- label: update
  no_exception: true
- label: update
  no_result_set: true
- label: update
  affected_row_count: 1
- label: delete
  no_exception: true
- label: delete
  no_result_set: true
- label: delete
  affected_row_count: 1
- label: delete_zero_rows
  no_exception: true
- label: delete_zero_rows
  no_result_set: true
- label: delete_zero_rows
  affected_row_count: 0
- label: merge
  no_exception: true
- label: merge
  no_result_set: true
- label: merge
  affected_row_count_min: 1
- label: cte_insert
  no_exception: true
- label: cte_insert
  no_result_set: true
- label: cte_insert
  affected_row_count: 1
- label: ddl_alter
  no_exception: true
- label: ddl_alter
  no_result_set: true
- label: ddl_alter
  affected_row_count_not_positive: true
- label: ddl_truncate
  no_exception: true
- label: ddl_truncate
  no_result_set: true
- label: ddl_truncate
  affected_row_count_not_positive: true
- label: counter_select
  no_exception: true
- label: counter_select
  row_count: 1
- label: counter_select
  column_count: 1
- label: counter_select
  column:
    index: 0
    row: 0
    type: Int64
    equals: 7
- label: counter_cte
  no_exception: true
- label: counter_cte
  row_count: 1
- label: counter_cte
  column:
    index: 0
    row: 0
    type: Int64
    equals: 7
- label: counter_table
  no_exception: true
- label: counter_table
  row_count: 1
- label: counter_table
  column:
    index: 0
    row: 0
    type: Int64
    equals: 7
STATEMENT-026 — Validates that executing a RESULTLESS statement (DML or DDL) on a statement/handle that previously ran a row-returning query REPLACES the statement's result state rather than leaving the earlier quer…

Reproduce:

SELECT 1 AS a, 'x' AS b
INSERT INTO {tableName} VALUES (1, 'a')
SELECT 42 AS only_col
ALTER TABLE {tableName} ADD COLUMN extra STRING
SELECT 1 AS a, 'x' AS b
INSERT INTO {tableName} VALUES (2, 'b')

Expected (per the shared spec):

  • completes without an exception
  • result has exactly 1 row(s)
  • result has 2 column(s)
  • completes without an exception
  • result has 0 column(s)
  • completes without an exception
  • result has exactly 1 row(s)
  • result has 1 column(s)
  • col 0 is named only_col
  • completes without an exception
  • result has 0 column(s)
  • completes without an exception
  • result has 2 column(s)
  • completes without an exception
  • result has 0 column(s)
  • completes without an exception
  • full assertion contract:
result:
- label: baseline_select
  no_exception: true
- label: baseline_select
  row_count: 1
- label: baseline_select
  column_count: 2
- label: write_after_select
  no_exception: true
- label: write_after_select
  no_result_set: true
- label: write_after_select
  column_count: 0
- label: write_after_select
  affected_row_count: 1
- label: select_after_write
  no_exception: true
- label: select_after_write
  row_count: 1
- label: select_after_write
  column_count: 1
- label: select_after_write
  column:
    index: 0
    name: only_col
- label: ddl_after_select
  no_exception: true
- label: ddl_after_select
  no_result_set: true
- label: ddl_after_select
  column_count: 0
- label: prepared_baseline
  no_exception: true
- label: prepared_baseline
  metadata_not_null: true
- label: prepared_baseline
  column_count: 2
- label: prepared_switch
  no_exception: true
- label: prepared_switch
  metadata_is_null: true
- label: prepared_switch
  column_count: 0
- label: prepared_switch_execute
  no_exception: true
- label: prepared_switch_execute
  no_result_set: true
- label: prepared_switch_execute
  affected_row_count: 1

Context

Dominant language
Go
Stars
53
Forks
66
Avg merge
1d 1h
Merged PRs (30d)
21

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from databricks/databricks-sql-go

All issues in databricks/databricks-sql-go

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.