Skip to content

Commit 645a518

Browse files
committed
Add support for Decimal, DateTime, NaiveDateTime, Date, Time, and :null defaults
Support for missing DEFAULT types: - Decimal: Converts to string representation (e.g., '19.99') - DateTime: Converts to ISO8601 string (e.g., '2026-01-16T14:30:00Z') - NaiveDateTime: Converts to ISO8601 string (e.g., '2026-01-16T14:30:00') - Date: Converts to ISO8601 string (e.g., '2026-01-16') - Time: Converts to ISO8601 string (e.g., '14:30:45.123456') - :null atom: Treated same as nil (no DEFAULT clause) All conversions match the parameter encoding behavior in query.ex for consistency. Add comprehensive test coverage: - Basic Decimal defaults with various precision levels - DateTime defaults with microsecond precision - NaiveDateTime, Date, and Time defaults - Edge cases: negative decimals, many decimal places - :null atom behavior parity with nil This closes the gap between what types the library accepts in parameters and what types it accepts as column defaults in migrations.
1 parent e6202ca commit 645a518

2 files changed

Lines changed: 112 additions & 0 deletions

File tree

lib/ecto/adapters/libsql/connection.ex

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,12 +468,35 @@ defmodule Ecto.Adapters.LibSql.Connection do
468468
end
469469

470470
defp column_default(nil), do: ""
471+
defp column_default(:null), do: ""
471472
defp column_default(true), do: " DEFAULT 1"
472473
defp column_default(false), do: " DEFAULT 0"
473474
defp column_default(value) when is_binary(value), do: " DEFAULT '#{escape_string(value)}'"
474475
defp column_default(value) when is_number(value), do: " DEFAULT #{value}"
475476
defp column_default({:fragment, expr}), do: " DEFAULT #{expr}"
476477

478+
# Temporal types - convert to ISO8601 strings
479+
defp column_default(%DateTime{} = dt) do
480+
" DEFAULT '#{escape_string(DateTime.to_iso8601(dt))}'"
481+
end
482+
483+
defp column_default(%NaiveDateTime{} = dt) do
484+
" DEFAULT '#{escape_string(NaiveDateTime.to_iso8601(dt))}'"
485+
end
486+
487+
defp column_default(%Date{} = d) do
488+
" DEFAULT '#{escape_string(Date.to_iso8601(d))}'"
489+
end
490+
491+
defp column_default(%Time{} = t) do
492+
" DEFAULT '#{escape_string(Time.to_iso8601(t))}'"
493+
end
494+
495+
# Decimal type - convert to string representation
496+
defp column_default(%Decimal{} = d) do
497+
" DEFAULT '#{escape_string(Decimal.to_string(d))}'"
498+
end
499+
477500
defp column_default(value) when is_map(value) or is_list(value) do
478501
type_name = if is_map(value), do: "map", else: "list"
479502
encode_json_default(value, type_name)

test/ecto_migration_test.exs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,95 @@ defmodule Ecto.Adapters.LibSql.MigrationTest do
10941094

10951095
assert log_output =~ "Failed to JSON encode list default value in migration"
10961096
end
1097+
1098+
test "handles :null atom defaults (same as nil)" do
1099+
table = %Table{name: :users, prefix: nil}
1100+
columns = [{:add, :bio, :text, [default: :null]}]
1101+
1102+
[sql] = Connection.execute_ddl({:create, table, columns})
1103+
1104+
# :null should result in no DEFAULT clause (same as nil)
1105+
refute sql =~ "DEFAULT"
1106+
end
1107+
1108+
test "handles Decimal defaults" do
1109+
table = %Table{name: :products, prefix: nil}
1110+
columns = [{:add, :price, :decimal, [default: Decimal.new("19.99")]}]
1111+
1112+
[sql] = Connection.execute_ddl({:create, table, columns})
1113+
1114+
# Decimal should be converted to string representation
1115+
assert sql =~ ~r/"price".*DECIMAL.*DEFAULT/
1116+
assert sql =~ "'19.99'"
1117+
end
1118+
1119+
test "handles DateTime defaults" do
1120+
table = %Table{name: :events, prefix: nil}
1121+
dt = DateTime.new!(~D[2026-01-16], ~T[14:30:00.000000], "UTC")
1122+
columns = [{:add, :created_at, :utc_datetime, [default: dt]}]
1123+
1124+
[sql] = Connection.execute_ddl({:create, table, columns})
1125+
1126+
# DateTime should be converted to ISO8601 string
1127+
assert sql =~ ~r/"created_at".*DATETIME.*DEFAULT/
1128+
assert sql =~ "2026-01-16T14:30:00Z"
1129+
end
1130+
1131+
test "handles NaiveDateTime defaults" do
1132+
table = %Table{name: :logs, prefix: nil}
1133+
dt = ~N[2026-01-16 14:30:00.000000]
1134+
columns = [{:add, :recorded_at, :naive_datetime, [default: dt]}]
1135+
1136+
[sql] = Connection.execute_ddl({:create, table, columns})
1137+
1138+
# NaiveDateTime should be converted to ISO8601 string
1139+
assert sql =~ ~r/"recorded_at".*DATETIME.*DEFAULT/
1140+
assert sql =~ "2026-01-16T14:30:00"
1141+
end
1142+
1143+
test "handles Date defaults" do
1144+
table = %Table{name: :schedules, prefix: nil}
1145+
date = ~D[2026-01-16]
1146+
columns = [{:add, :event_date, :date, [default: date]}]
1147+
1148+
[sql] = Connection.execute_ddl({:create, table, columns})
1149+
1150+
# Date should be converted to ISO8601 string
1151+
assert sql =~ ~r/"event_date".*DATE.*DEFAULT/
1152+
assert sql =~ "'2026-01-16'"
1153+
end
1154+
1155+
test "handles Time defaults" do
1156+
table = %Table{name: :schedules, prefix: nil}
1157+
time = ~T[14:30:45.123456]
1158+
columns = [{:add, :event_time, :time, [default: time]}]
1159+
1160+
[sql] = Connection.execute_ddl({:create, table, columns})
1161+
1162+
# Time should be converted to ISO8601 string
1163+
assert sql =~ ~r/"event_time".*TIME.*DEFAULT/
1164+
assert sql =~ "14:30:45.123456"
1165+
end
1166+
1167+
test "handles Decimal with many decimal places" do
1168+
table = %Table{name: :data, prefix: nil}
1169+
columns = [{:add, :value, :decimal, [default: Decimal.new("123.456789012345")]}]
1170+
1171+
[sql] = Connection.execute_ddl({:create, table, columns})
1172+
1173+
assert sql =~ ~r/"value".*DECIMAL.*DEFAULT/
1174+
assert sql =~ "'123.456789012345'"
1175+
end
1176+
1177+
test "handles negative Decimal defaults" do
1178+
table = %Table{name: :balances, prefix: nil}
1179+
columns = [{:add, :amount, :decimal, [default: Decimal.new("-42.50")]}]
1180+
1181+
[sql] = Connection.execute_ddl({:create, table, columns})
1182+
1183+
assert sql =~ ~r/"amount".*DECIMAL.*DEFAULT/
1184+
assert sql =~ "'-42.50'"
1185+
end
10971186
end
10981187

10991188
describe "CHECK constraints" do

0 commit comments

Comments
 (0)