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

Proposal: Add native support to bitstring #4328

Merged
merged 21 commits into from
Feb 27, 2024
Merged
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
19 changes: 10 additions & 9 deletions Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ VERSION 0.6

all:
BUILD \
--build-arg ELIXIR_BASE=1.15.6-erlang-25.3.2.6-alpine-3.16.7 \
--build-arg ELIXIR_BASE=1.15.6-erlang-24.3.4.14-alpine-3.16.7 \
--build-arg ELIXIR_BASE=1.15.6-erlang-25.3.2.6-alpine-3.18.4 \
--build-arg ELIXIR_BASE=1.15.6-erlang-24.3.4.14-alpine-3.18.4 \
+integration-test

integration-test-base:
ARG ELIXIR_BASE=1.15.6-erlang-25.3.2.6-alpine-3.16.7
ARG ELIXIR_BASE=1.15.6-erlang-25.3.2.6-alpine-3.18.4
ARG TARGETARCH
FROM hexpm/elixir:$ELIXIR_BASE
RUN apk add --no-progress --update git build-base
RUN mix local.rebar --force
Expand All @@ -17,11 +18,11 @@ integration-test-base:
RUN apk add --no-progress --update docker docker-compose git postgresql-client mysql-client

RUN apk add --no-cache curl gnupg --virtual .build-dependencies -- && \
curl -O https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_17.5.2.1-1_amd64.apk && \
curl -O https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/mssql-tools_17.5.2.1-1_amd64.apk && \
echo y | apk add --allow-untrusted msodbcsql17_17.5.2.1-1_amd64.apk mssql-tools_17.5.2.1-1_amd64.apk && \
curl -O https://download.microsoft.com/download/3/5/5/355d7943-a338-41a7-858d-53b259ea33f5/msodbcsql18_18.3.2.1-1_${TARGETARCH}.apk && \
curl -O https://download.microsoft.com/download/3/5/5/355d7943-a338-41a7-858d-53b259ea33f5/mssql-tools18_18.3.1.1-1_${TARGETARCH}.apk && \
echo y | apk add --allow-untrusted msodbcsql18_18.3.2.1-1_${TARGETARCH}.apk mssql-tools18_18.3.1.1-1_${TARGETARCH}.apk && \
apk del .build-dependencies && rm -f msodbcsql*.sig mssql-tools*.apk
ENV PATH="/opt/mssql-tools/bin:${PATH}"
ENV PATH="/opt/mssql-tools18/bin:${PATH}"

GIT CLONE https://github.com/elixir-ecto/ecto_sql.git /src/ecto_sql
WORKDIR /src/ecto_sql
Expand All @@ -44,7 +45,7 @@ integration-test:
ARG MYSQL_IMG="mysql:5.7"

# then run the tests
WITH DOCKER --pull "$PG_IMG" --pull "$MCR_IMG" --pull "$MYSQL_IMG"
WITH DOCKER --pull "$PG_IMG" --pull "$MCR_IMG" --pull "$MYSQL_IMG" --platform linux/amd64
RUN set -e; \
timeout=$(expr $(date +%s) + 60); \

Expand All @@ -54,7 +55,7 @@ integration-test:
docker run --name mysql --network=host -d -e MYSQL_ROOT_PASSWORD=root "$MYSQL_IMG"; \

# wait for mssql to start
while ! sqlcmd -S tcp:127.0.0.1,1433 -U sa -P 'some!Password' -Q "SELECT 1" >/dev/null 2>&1; do \
while ! sqlcmd -C -S tcp:127.0.0.1,1433 -U sa -P 'some!Password' -Q "SELECT 1" >/dev/null 2>&1; do \
test "$(date +%s)" -le "$timeout" || (echo "timed out waiting for mysql"; exit 1); \
echo "waiting for mssql"; \
sleep 1; \
Expand Down
15 changes: 14 additions & 1 deletion integration_test/cases/type.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Ecto.Integration.TypeTest do
use Ecto.Integration.Case, async: Application.compile_env(:ecto, :async_integration_tests, true)

alias Ecto.Integration.{Comment, Custom, Item, ItemColor, Order, Post, User, Tag, Usec}
alias Ecto.Integration.{Bitstring, Comment, Custom, Item, ItemColor, Order, Post, User, Tag, Usec}
alias Ecto.Integration.TestRepo
import Ecto.Query

Expand Down Expand Up @@ -67,6 +67,19 @@ defmodule Ecto.Integration.TypeTest do
assert [^datetime] = TestRepo.all(from u in Usec, where: u.utc_datetime_usec == ^datetime, select: u.utc_datetime_usec)
end

@tag :bitstring_type
test "bitstring type" do
bitstring = <<2::3>>

TestRepo.insert!(%Bitstring{bs: bitstring, bs_with_size: <<5::10>>})

# Bitstrings
assert [^bitstring] = TestRepo.all(from p in Bitstring, where: p.bs == ^bitstring, select: p.bs)
assert [^bitstring] = TestRepo.all(from p in Bitstring, where: p.bs == <<2::3>>, select: p.bs)

assert [<<42::6>>] = TestRepo.all(from p in Bitstring, limit: 1, select: p.bs_with_default)
end

@tag :select_not
test "primitive types boolean negate" do
TestRepo.insert!(%Post{public: true})
Expand Down
16 changes: 16 additions & 0 deletions integration_test/support/schemas.exs
Original file line number Diff line number Diff line change
Expand Up @@ -388,3 +388,19 @@ defmodule Ecto.Integration.ArrayLogging do
timestamps()
end
end

defmodule Ecto.Integration.Bitstring do
@moduledoc """
This module is used to test:

* Bitstring type

"""
use Ecto.Integration.Schema

schema "bitstrings" do
field :bs, :bitstring
field :bs_with_default, :bitstring
field :bs_with_size, :bitstring
end
end
5 changes: 4 additions & 1 deletion lib/ecto/query/builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ defmodule Ecto.Query.Builder do
do: do_literal(value, expected, quoted_type(value, vars))

defp do_literal(value, _, current) when current in @always_tagged,
do: {:%, [], [Ecto.Query.Tagged, {:%{}, [], [value: value, type: current]}]}
do: {:%, [], [Ecto.Query.Tagged, {:%{}, [], [value: value, type: normalize_type(value, current)]}]}
defp do_literal(value, :any, _current),
do: value
defp do_literal(value, expected, expected),
Expand Down Expand Up @@ -1228,6 +1228,9 @@ defmodule Ecto.Query.Builder do
defp get_env({env, _}), do: env
defp get_env(env), do: env

defp normalize_type(value, :binary),
do: quote(do: is_binary(unquote(value)) && :binary || :bitstring)

@doc """
Raises a query building error.
"""
Expand Down
1 change: 1 addition & 0 deletions lib/ecto/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ defmodule Ecto.Schema do
`:boolean` | `boolean` | true, false
`:string` | UTF-8 encoded `string` | "hello"
`:binary` | `binary` | `<<int, int, int, ...>>`
`:bitstring` | `bitstring` | `<<_::size>>
`{:array, inner_type}` | `list` | `[value, value, value, ...]`
`:map` | `map` |
`{:map, inner_type}` | `map` |
Expand Down
12 changes: 11 additions & 1 deletion lib/ecto/type.ex
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ defmodule Ecto.Type do
| :float
| :boolean
| :string
| :bitstring
| :map
| :binary
| :decimal
Expand All @@ -227,7 +228,7 @@ defmodule Ecto.Type do
@typep private_composite :: {:maybe, t} | {:in, t} | {:param, :any_datetime}

@base ~w(
integer float decimal boolean string map binary id binary_id any
integer float decimal boolean string bitstring map binary id binary_id any
utc_datetime naive_datetime date time
utc_datetime_usec naive_datetime_usec time_usec
)a
Expand Down Expand Up @@ -550,6 +551,7 @@ defmodule Ecto.Type do
def dump(:map, value, _dumper), do: same_map(value)
def dump(:string, value, _dumper), do: same_binary(value)
def dump(:binary, value, _dumper), do: same_binary(value)
def dump(:bitstring, value, _dumper), do: same_bitstring(value)
def dump(:id, value, _dumper), do: same_integer(value)
def dump(:binary_id, value, _dumper), do: same_binary(value)
def dump(:decimal, value, _dumper), do: same_decimal(value)
Expand Down Expand Up @@ -644,6 +646,7 @@ defmodule Ecto.Type do
def load(:map, value, _loader), do: same_map(value)
def load(:string, value, _loader), do: same_binary(value)
def load(:binary, value, _loader), do: same_binary(value)
def load(:bitstring, value, _loader), do: same_bitstring(value)
def load(:id, value, _loader), do: same_integer(value)
def load(:binary_id, value, _loader), do: same_binary(value)
def load(:decimal, value, _loader), do: same_decimal(value)
Expand Down Expand Up @@ -814,6 +817,7 @@ defmodule Ecto.Type do
defp cast_fun(:map), do: &cast_map/1
defp cast_fun(:string), do: &cast_binary/1
defp cast_fun(:binary), do: &cast_binary/1
defp cast_fun(:bitstring), do: &cast_bitstring/1
defp cast_fun(:id), do: &cast_integer/1
defp cast_fun(:binary_id), do: &cast_binary/1
defp cast_fun(:any), do: &{:ok, &1}
Expand Down Expand Up @@ -897,6 +901,9 @@ defmodule Ecto.Type do
defp cast_binary(term) when is_binary(term), do: {:ok, term}
defp cast_binary(_), do: :error

defp cast_bitstring(term) when is_bitstring(term), do: {:ok, term}
defp cast_bitstring(_), do: :error

defp cast_map(term) when is_map(term), do: {:ok, term}
defp cast_map(_), do: :error

Expand All @@ -912,6 +919,9 @@ defmodule Ecto.Type do
defp same_binary(term) when is_binary(term), do: {:ok, term}
defp same_binary(_), do: :error

defp same_bitstring(term) when is_bitstring(term), do: {:ok, term}
defp same_bitstring(_), do: :error

defp same_map(term) when is_map(term), do: {:ok, term}
defp same_map(_), do: :error

Expand Down
2 changes: 1 addition & 1 deletion test/ecto/query/builder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defmodule Ecto.Query.BuilderTest do
assert {quote(do: ~s"123"), []} ==
escape(quote do ~s"123" end, [], __ENV__)

assert {{:%, [], [Ecto.Query.Tagged, {:%{}, [], [value: {:<<>>, [], [0, 1, 2]}, type: :binary]}]}, []} ==
assert {{:%, [], [Ecto.Query.Tagged, {:%{}, [], [value: {:<<>>, [], [0, 1, 2]}, type: {:||, _, [{:&&, _, [{:is_binary, _, [{:<<>>, [], [0, 1, 2]}]}, :binary]}, :bitstring]}]}]}, []} =
escape(quote do <<0, 1, 2>> end, [], __ENV__)

assert {:some_atom, []} ==
Expand Down
Loading