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 #577

Merged
merged 38 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
9f44bc2
Add basic bitwise operators support
Gigitsu Nov 25, 2023
0156746
Add unary operator
Gigitsu Nov 25, 2023
a0a46a7
Add bitwise function and better handling parenthesis
Gigitsu Nov 25, 2023
5ecfbf2
Add bitwise support to myxql
Gigitsu Nov 25, 2023
6ffd8d1
Format
Gigitsu Nov 25, 2023
b869345
Add migration support to binary types
Gigitsu Nov 25, 2023
253837d
Change ecto main repo and branch
Gigitsu Nov 26, 2023
f59bc9d
Handle typecast parenthesis for bnot
Gigitsu Nov 26, 2023
c80c2a6
Add bnot op_to_binary case
Gigitsu Nov 26, 2023
1ca7cb6
Reorder functions
Gigitsu Nov 26, 2023
894082d
Use bit as type for bitstring
Gigitsu Nov 26, 2023
ae107b8
Implement type casting with size
Gigitsu Nov 26, 2023
ce31aef
Revert "Implement type casting with size"
Gigitsu Nov 26, 2023
676a662
Fix test
Gigitsu Nov 26, 2023
1f1627e
Change Postgres type for bitstring
Gigitsu Nov 28, 2023
8772573
Fix tests
Gigitsu Nov 28, 2023
54ace51
Fix integration test for arm powered host
Gigitsu Dec 4, 2023
42ba521
Remove bitwise operators support
Gigitsu Feb 17, 2024
8a6da1d
Remove remaining bitwise operator tests
Gigitsu Feb 17, 2024
61fb5a8
Add defaults
Gigitsu Feb 17, 2024
6fe3c05
Update ecto dep
Gigitsu Feb 17, 2024
d979882
Add migration bitstring support
Gigitsu Feb 18, 2024
2578a12
Raise when defining a bitstring field without a size with mysql
Gigitsu Feb 19, 2024
d2961b0
Update mix.lock
greg-rychlewski Feb 19, 2024
d5938ea
Remove mysql bitstring support
Gigitsu Feb 25, 2024
6b1cbbe
Remove TDS bitstring support
Gigitsu Feb 25, 2024
24d6719
Add bitstring table with mysql exclusion
Gigitsu Feb 25, 2024
7a552fa
Remove bitstring type from `posts` table
Gigitsu Feb 25, 2024
0cf5023
Improve bitstring tests
Gigitsu Feb 25, 2024
a9d6d46
Update ecto dep
Gigitsu Feb 25, 2024
44d466d
Update ecto
Gigitsu Feb 25, 2024
cf89b25
Exclude bitstring for tds too
Gigitsu Feb 25, 2024
1a6df91
Add bitstring literal support
Gigitsu Feb 25, 2024
661830f
Improve bitstring literal support
Gigitsu Feb 26, 2024
a0c3a9e
Update bitstring support
Gigitsu Feb 27, 2024
e9396a4
Update ecto dependency
Gigitsu Feb 27, 2024
0558803
Update mix.exs
greg-rychlewski Feb 27, 2024
fe85b6f
Update mix.lock
Gigitsu Feb 27, 2024
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
21 changes: 11 additions & 10 deletions Earthfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
VERSION 0.6

all:
ARG ELIXIR_BASE=1.15.6-erlang-25.3.2.6-alpine-3.17.4
ARG ELIXIR_BASE=1.15.6-erlang-25.3.2.6-alpine-3.18.4
BUILD \
--build-arg POSTGRES=15.0 \
--build-arg POSTGRES=11.11 \
Expand All @@ -20,7 +20,7 @@ all:
+integration-test-mssql

setup-base:
ARG ELIXIR_BASE=1.15.6-erlang-25.3.2.6-alpine-3.17.4
ARG ELIXIR_BASE=1.15.6-erlang-25.3.2.6-alpine-3.18.4
FROM hexpm/elixir:$ELIXIR_BASE
RUN apk add --no-progress --update git build-base
ENV ELIXIR_ASSERT_TIMEOUT=10000
Expand Down Expand Up @@ -62,7 +62,7 @@ integration-test-postgres:

# then run the tests
WITH DOCKER \
--pull "postgres:$POSTGRES"
--pull "postgres:$POSTGRES" --platform linux/amd64
RUN set -e; \
timeout=$(expr $(date +%s) + 30); \
docker run --name pg --network=host -d -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres "postgres:$POSTGRES"; \
Expand All @@ -84,7 +84,7 @@ integration-test-mysql:

ARG MYSQL="5.7"
WITH DOCKER \
--pull "mysql:$MYSQL"
--pull "mysql:$MYSQL" --platform linux/amd64
RUN set -e; \
timeout=$(expr $(date +%s) + 30); \
docker run --name mysql --network=host -d -e MYSQL_ROOT_PASSWORD=root "mysql:$MYSQL" \
Expand All @@ -103,25 +103,26 @@ integration-test-mysql:


integration-test-mssql:
ARG TARGETARCH
FROM +setup-base

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}"

DO +COMMON_SETUP_AND_MIX

ARG MSSQL="2017"
WITH DOCKER \
--pull "mcr.microsoft.com/mssql/server:$MSSQL-latest"
--pull "mcr.microsoft.com/mssql/server:$MSSQL-latest" --platform linux/amd64
RUN set -e; \
timeout=$(expr $(date +%s) + 30); \
docker run -d -p 1433:1433 --name mssql -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=some!Password' "mcr.microsoft.com/mssql/server:$MSSQL-latest"; \
# 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 mssql"; exit 1); \
echo "waiting for mssql"; \
sleep 1; \
Expand Down
2 changes: 2 additions & 0 deletions integration_test/myxql/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ version =
end

excludes = [
# not sure how to support this yet
:bitstring_type,
# MySQL does not have an array type
:array_type,
# The next two features rely on RETURNING, which MySQL does not support
Expand Down
8 changes: 8 additions & 0 deletions integration_test/support/migration.exs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ defmodule Ecto.Integration.Migration do
end
end

unless :bitstring_type in ExUnit.configuration()[:exclude] do
create table(:bitstrings) do
add :bs, :bitstring
add :bs_with_default, :bitstring, default: <<42::6>>
add :bs_with_size, :bitstring, size: 10
end
end

create table(:composite_pk, primary_key: false) do
add :a, :integer, primary_key: true
add :b, :integer, primary_key: true
Expand Down
1 change: 1 addition & 0 deletions integration_test/tds/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ ExUnit.start(
exclude: [
# not sure how to support this yet
:aggregate_filters,
:bitstring_type,
# subquery contains ORDER BY and that is not supported
:subquery_aggregates,
# sql don't have array type
Expand Down
17 changes: 17 additions & 0 deletions lib/ecto/adapters/postgres/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,11 @@ if Code.ensure_loaded?(Postgrex) do
["'\\x", Base.encode16(binary, case: :lower) | "'::bytea"]
end

defp expr(%Ecto.Query.Tagged{value: bitstring, type: :bitstring}, _sources, _query)
when is_bitstring(bitstring) do
bitstring_literal(bitstring)
end

defp expr(%Ecto.Query.Tagged{value: other, type: type}, sources, query) do
[maybe_paren(other, sources, query), ?:, ?: | tagged_to_db(type)]
end
Expand Down Expand Up @@ -1580,6 +1585,10 @@ if Code.ensure_loaded?(Postgrex) do
end
end

defp default_type(literal, _type) when is_bitstring(literal) do
bitstring_literal(literal)
end

defp default_type(literal, _type) when is_number(literal), do: to_string(literal)
defp default_type(literal, _type) when is_boolean(literal), do: to_string(literal)

Expand Down Expand Up @@ -1824,6 +1833,13 @@ if Code.ensure_loaded?(Postgrex) do

defp single_quote(value), do: [?', escape_string(value), ?']

defp bitstring_literal(value) do
size = bit_size(value)
<<val::size(size)>> = value

[?b, ?', val |> Integer.to_string(2) |> String.pad_leading(size, ["0"]), ?']
end

defp intersperse_reduce(list, separator, user_acc, reducer, acc \\ [])

defp intersperse_reduce([], _separator, user_acc, _reducer, acc),
Expand Down Expand Up @@ -1870,6 +1886,7 @@ if Code.ensure_loaded?(Postgrex) do
defp ecto_to_db(:bigserial), do: "bigserial"
defp ecto_to_db(:binary_id), do: "uuid"
defp ecto_to_db(:string), do: "varchar"
defp ecto_to_db(:bitstring), do: "varbit"
defp ecto_to_db(:binary), do: "bytea"
defp ecto_to_db(:map), do: Application.fetch_env!(:ecto_sql, :postgres_map_type)
defp ecto_to_db({:map, _}), do: Application.fetch_env!(:ecto_sql, :postgres_map_type)
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ defmodule EctoSQL.MixProject do
if path = System.get_env("ECTO_PATH") do
{:ecto, path: path}
else
{:ecto, "~> 3.11.0"}
{:ecto, github: "elixir-ecto/ecto"}
end
end

Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ecto": {:hex, :ecto, "3.11.0", "ff8614b4e70a774f9d39af809c426def80852048440e8785d93a6e91f48fec00", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7769dad267ef967310d6e988e92d772659b11b09a0c015f101ce0fff81ce1f81"},
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "bed81b9a69f3425147fb57df1b3dd5fb4c95792c", []},
"ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
Expand Down
6 changes: 6 additions & 0 deletions test/ecto/adapters/postgres_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@
query = "schema" |> where(foo: "abc") |> select([], true) |> plan()
assert all(query) == ~s{SELECT TRUE FROM "schema" AS s0 WHERE (s0."foo" = 'abc')}

query = "schema" |> where(foo: <<0, ?a, ?b, ?c>>) |> select([], true) |> plan()

Check warning on line 832 in test/ecto/adapters/postgres_test.exs

View workflow job for this annotation

GitHub Actions / unittest (1.15.6, 24.3.4.13)

this check/guard will always yield the same result

Check warning on line 832 in test/ecto/adapters/postgres_test.exs

View workflow job for this annotation

GitHub Actions / unittest (1.15.6, 26.1.2, lint)

this check/guard will always yield the same result

assert all(query) ==
~s{SELECT TRUE FROM "schema" AS s0 WHERE (s0."foo" = '\\x00616263'::bytea)}
Expand Down Expand Up @@ -1990,6 +1990,9 @@
{:add, :on_hand, :integer, [default: 0, null: true]},
{:add, :published_at, :"time without time zone", [null: true]},
{:add, :is_active, :boolean, [default: true]},
{:add, :flags, :bitstring, [null: false]},
{:add, :flags_with_default, :bitstring, [default: <<42::10>>]},
{:add, :flags_with_size, :bitstring, [size: 10]},
{:add, :tags, {:array, :string}, [default: []]},
{:add, :languages, {:array, :string}, [default: ["pt", "es"]]},
{:add, :limits, {:array, :integer}, [default: [100, 30_000]]}
Expand All @@ -2002,6 +2005,9 @@
"on_hand" integer DEFAULT 0 NULL,
"published_at" time without time zone NULL,
"is_active" boolean DEFAULT true,
"flags" varbit NOT NULL,
"flags_with_default" varbit DEFAULT b'0000101010',
"flags_with_size" varbit(10),
"tags" varchar(255)[] DEFAULT ARRAY[]::varchar[],
"languages" varchar(255)[] DEFAULT ARRAY['pt','es']::varchar[],
"limits" integer[] DEFAULT ARRAY[100,30000]::integer[])
Expand Down
Loading