Skip to content

Commit

Permalink
Add native support to bitstring (#4328)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gigitsu authored Feb 27, 2024
1 parent 3280700 commit bed81b9
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 13 deletions.
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

0 comments on commit bed81b9

Please sign in to comment.