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

fixes for query_many and its variants #152

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 151 additions & 3 deletions lib/myxql.ex
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,43 @@ defmodule MyXQL do
* requires two roundtrips to the DB server: one for preparing the statement and one for executing it.
This can be alleviated by holding on to prepared statement and executing it multiple times.

## Stored procedures

A stored procedure containing statements that return rows, such as `SELECT`
statements, must use `query_many/4` and similar functions. This is because
stored procedures always return a final result with the number of rows affected
by the last statement.

Take, for example, the following stored procedure:

DELIMITER $$
CREATE PROCEDURE stored_procedure()
BEGIN
SELECT 1;
END$$
DELIMITER ;

There will be one result for the `SELECT 1` statement and another result
stating that 0 rows were affected by the last statement.

In contrast to this, a stored procedure that doesn't contain row-returning
statements can use this function. In this scenario, only the final result
with the number of rows affected by the last statement will be returned.

Take, for example, the following stored procedure:

DELIMITER $$
CREATE PROCEDURE stored_procedure()
BEGIN
CREATE TABLE IF NOT EXISTS integers (x int);
INSERT INTO integers VALUES (10);
END$$
DELIMITER ;

Because `CREATE` and `INSERT` statements do not return rows, there will be a
single result stating that 1 row was affected by the last statement. This is
referencing the row that was inserted.

## Options

* `:query_type` - use `:binary` for binary protocol (prepared statements), `:binary_then_text` to attempt
Expand Down Expand Up @@ -365,6 +402,43 @@ defmodule MyXQL do
but the statement isn't. If a new statement is given to an old name, the old
statement will be the one effectively used.

## Stored procedures

A stored procedure containing statements that return rows, such as `SELECT`
statements, must use `prepare_many/4` and similar functions. This is because
stored procedures always return a final result with the number of rows affected
by the last statement.

Take, for example, the following stored procedure:

DELIMITER $$
CREATE PROCEDURE stored_procedure()
BEGIN
SELECT 1;
END$$
DELIMITER ;

There will be one result for the `SELECT 1` statement and another result
stating that 0 rows were affected by the last statement.

In contrast to this, a stored procedure that doesn't contain row-returning
statements can use this function. In this scenario, only the final result
with the number of rows affected by the last statement will be returned.

Take, for example, the following stored procedure:

DELIMITER $$
CREATE PROCEDURE stored_procedure()
BEGIN
CREATE TABLE IF NOT EXISTS integers (x int);
INSERT INTO integers VALUES (10);
END$$
DELIMITER ;

Because `CREATE` and `INSERT` statements do not return rows, there will be a
single result stating that 1 row was affected by the last statement. This is
referencing the row that was inserted.

## Options

Options are passed to `DBConnection.prepare/3`, see it's documentation for
Expand Down Expand Up @@ -417,7 +491,7 @@ defmodule MyXQL do
## Examples

iex> {:ok, query} = MyXQL.prepare_many(conn, "", "CALL multi_procedure()")
iex> {:ok, [%MyXQL.Result{rows: [row1]}, %MyXQL.Result{rows: [row2]}]} = MyXQL.execute_many(conn, query, [2, 3])
iex> {:ok, [%MyXQL.Result{rows: [row1]}, %MyXQL.Result{rows: [row2]}, %MyXQL.Result{rows: nil}]} = MyXQL.execute_many(conn, query, [2, 3])
iex> row1
[2]
iex> row2
Expand Down Expand Up @@ -449,6 +523,43 @@ defmodule MyXQL do
@doc """
Prepares and executes a query that returns a single result, in a single step.

## Stored procedures

A stored procedure containing statements that return rows, such as `SELECT`
statements, must use `prepare_execute_many/5` and similar functions. This is because
stored procedures always return a final result with the number of rows affected
by the last statement.

Take, for example, the following stored procedure:

DELIMITER $$
CREATE PROCEDURE stored_procedure()
BEGIN
SELECT 1;
END$$
DELIMITER ;

There will be one result for the `SELECT 1` statement and another result
stating that 0 rows were affected by the last statement.

In contrast to this, a stored procedure that doesn't contain row-returning
statements can use this function. In this scenario, only the final result
with the number of rows affected by the last statement will be returned.

Take, for example, the following stored procedure:

DELIMITER $$
CREATE PROCEDURE stored_procedure()
BEGIN
CREATE TABLE IF NOT EXISTS integers (x int);
INSERT INTO integers VALUES (10);
END$$
DELIMITER ;

Because `CREATE` and `INSERT` statements do not return rows, there will be a
single result stating that 1 row was affected by the last statement. This is
referencing the row that was inserted.

## Options

Options are passed to `DBConnection.prepare_execute/4`, see it's documentation for
Expand Down Expand Up @@ -499,7 +610,7 @@ defmodule MyXQL do

## Examples

iex> {:ok, _, [%MyXQL.Result{rows: [row1]}, %MyXQL.Result{rows: [row2]}]} = MyXQL.prepare_execute(conn, "", "CALL multi_procedure()")
iex> {:ok, _, [%MyXQL.Result{rows: [row1]}, %MyXQL.Result{rows: [row2]}, %MyXQL.Result{rows: nil}]} = MyXQL.prepare_execute(conn, "", "CALL multi_procedure()")
iex> row1
[2]
iex> row2
Expand Down Expand Up @@ -533,6 +644,43 @@ defmodule MyXQL do
@doc """
Executes a prepared query that returns a single result.

## Stored procedures

A stored procedure containing statements that return rows, such as `SELECT`
statements, must use `execute_many/4` and similar functions. This is because
stored procedures always return a final result with the number of rows affected
by the last statement.

Take, for example, the following stored procedure:

DELIMITER $$
CREATE PROCEDURE stored_procedure()
BEGIN
SELECT 1;
END$$
DELIMITER ;

There will be one result for the `SELECT 1` statement and another result
stating that 0 rows were affected by the last statement.

In contrast to this, a stored procedure that doesn't contain row-returning
statements can use this function. In this scenario, only the final result
with the number of rows affected by the last statement will be returned.

Take, for example, the following stored procedure:

DELIMITER $$
CREATE PROCEDURE stored_procedure()
BEGIN
CREATE TABLE IF NOT EXISTS integers (x int);
INSERT INTO integers VALUES (10);
END$$
DELIMITER ;

Because `CREATE` and `INSERT` statements do not return rows, there will be a
single result stating that 1 row was affected by the last statement. This is
referencing the row that was inserted.

## Options

Options are passed to `DBConnection.execute/4`, see it's documentation for
Expand Down Expand Up @@ -575,7 +723,7 @@ defmodule MyXQL do
## Examples

iex> {:ok, query} = MyXQL.prepare_many(conn, "", "CALL multi_procedure()")
iex> {:ok, [%MyXQL.Result{rows: [row1]}, %MyXQL.Result{rows: [row2]}]} = MyXQL.execute_many(conn, query)
iex> {:ok, [%MyXQL.Result{rows: [row1]}, %MyXQL.Result{rows: [row2]}, %MyXQL.Result{rows: nil}]} = MyXQL.execute_many(conn, query)
iex> row1
[2]
iex> row2
Expand Down
31 changes: 9 additions & 22 deletions lib/myxql/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -192,28 +192,6 @@ defmodule MyXQL.Client do

defp recv_packets(data, decode, decoder_state, result_state, timeout, client, partial \\ <<>>)

defp recv_packets(
<<size::uint3, _seq::uint1, payload::string(size), rest::binary>> = data,
decoder,
{:more_results, resultset},
result_state,
timeout,
client,
partial
)
when size < @default_max_packet_size do
case decode_more_results(<<partial::binary, payload::binary>>, rest, resultset, result_state) do
{:cont, decoder_state, result_state} ->
recv_packets(data, decoder, decoder_state, result_state, timeout, client, partial)

{:halt, result} ->
{:ok, result}

{:error, _} = error ->
error
end
end

defp recv_packets(
<<size::uint3, _seq::uint1, payload::string(size), rest::binary>>,
decoder,
Expand All @@ -225,6 +203,15 @@ defmodule MyXQL.Client do
)
when size < @default_max_packet_size do
case decoder.(<<partial::binary, payload::binary>>, rest, decoder_state) do
{:cont, {:more_results, result}} ->
case result_state do
:single ->
{:error, :multiple_results}

{:many, results} ->
recv_packets(rest, decoder, :initial, {:many, [result | results]}, timeout, client)
end

{:cont, decoder_state} ->
recv_packets(rest, decoder, decoder_state, result_state, timeout, client)

Expand Down
Loading