Current section
Files
Jump to
Current section
Files
src/cquill@schema.erl
-module(cquill@schema).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/cquill/schema.gleam").
-export([new/1, new_with_schema/2, add_field/2, field/2, primary_key/2, single_primary_key/2, in_schema/2, add_constraint/2, unique_constraint/3, index/4, table_check/3, with_comment/2, get_source/1, get_qualified_name/1, get_fields/1, field_count/1, get_primary_key/1, has_primary_key/1, get_field/2, has_field/2, field_names/1, primary_key_fields/1, non_primary_key_fields/1, nullable_fields/1, required_fields/1, auto_increment_fields/1, foreign_key_fields/1, select_fields/2, exclude_fields/2, insertable_fields/1, rename_field/3, validate/1, is_valid/1, diff/2]).
-export_type([schema/0, table_constraint/0, schema_error/0, schema_diff/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type schema() :: {schema,
binary(),
gleam@option:option(binary()),
list(cquill@schema@field:field()),
list(binary()),
list(table_constraint()),
gleam@option:option(binary())}.
-type table_constraint() :: {unique_constraint, binary(), list(binary())} |
{index, binary(), list(binary()), boolean()} |
{table_check, binary(), binary()} |
{composite_foreign_key, binary(), list(binary()), binary(), list(binary())}.
-type schema_error() :: {invalid_primary_key, binary()} |
{duplicate_field, binary()} |
empty_schema |
{invalid_table_name, binary()} |
{invalid_constraint_column, binary(), binary()}.
-type schema_diff() :: {field_added, cquill@schema@field:field()} |
{field_removed, binary()} |
{field_type_changed,
binary(),
cquill@schema@field:field_type(),
cquill@schema@field:field_type()} |
{primary_key_changed, list(binary()), list(binary())}.
-file("src/cquill/schema.gleam", 83).
?DOC(" Create a new empty schema for a table\n").
-spec new(binary()) -> schema().
new(Source) ->
{schema, Source, none, [], [], [], none}.
-file("src/cquill/schema.gleam", 95).
?DOC(" Create a new schema with a specific database schema/namespace\n").
-spec new_with_schema(binary(), binary()) -> schema().
new_with_schema(Source, Table_schema) ->
{schema, Source, {some, Table_schema}, [], [], [], none}.
-file("src/cquill/schema.gleam", 112).
?DOC(
" Add a field to the schema\n"
" Fields are added in order and the order is preserved\n"
).
-spec add_field(schema(), cquill@schema@field:field()) -> schema().
add_field(Schema, New_field) ->
{schema,
erlang:element(2, Schema),
erlang:element(3, Schema),
lists:append(erlang:element(4, Schema), [New_field]),
erlang:element(5, Schema),
erlang:element(6, Schema),
erlang:element(7, Schema)}.
-file("src/cquill/schema.gleam", 118).
?DOC(" Alias for add_field - more concise in pipelines\n").
-spec field(schema(), cquill@schema@field:field()) -> schema().
field(Schema, New_field) ->
add_field(Schema, New_field).
-file("src/cquill/schema.gleam", 124).
?DOC(
" Set the primary key columns\n"
" Supports composite primary keys\n"
).
-spec primary_key(schema(), list(binary())) -> schema().
primary_key(Schema, Columns) ->
{schema,
erlang:element(2, Schema),
erlang:element(3, Schema),
erlang:element(4, Schema),
Columns,
erlang:element(6, Schema),
erlang:element(7, Schema)}.
-file("src/cquill/schema.gleam", 129).
?DOC(" Set a single-column primary key\n").
-spec single_primary_key(schema(), binary()) -> schema().
single_primary_key(Schema, Column) ->
{schema,
erlang:element(2, Schema),
erlang:element(3, Schema),
erlang:element(4, Schema),
[Column],
erlang:element(6, Schema),
erlang:element(7, Schema)}.
-file("src/cquill/schema.gleam", 134).
?DOC(" Set the table schema/namespace\n").
-spec in_schema(schema(), binary()) -> schema().
in_schema(Schema, Table_schema) ->
{schema,
erlang:element(2, Schema),
{some, Table_schema},
erlang:element(4, Schema),
erlang:element(5, Schema),
erlang:element(6, Schema),
erlang:element(7, Schema)}.
-file("src/cquill/schema.gleam", 139).
?DOC(" Add a table-level constraint\n").
-spec add_constraint(schema(), table_constraint()) -> schema().
add_constraint(Schema, Constraint) ->
{schema,
erlang:element(2, Schema),
erlang:element(3, Schema),
erlang:element(4, Schema),
erlang:element(5, Schema),
lists:append(erlang:element(6, Schema), [Constraint]),
erlang:element(7, Schema)}.
-file("src/cquill/schema.gleam", 147).
?DOC(" Add a multi-column unique constraint\n").
-spec unique_constraint(schema(), binary(), list(binary())) -> schema().
unique_constraint(Schema, Name, Columns) ->
add_constraint(Schema, {unique_constraint, Name, Columns}).
-file("src/cquill/schema.gleam", 156).
?DOC(" Add an index\n").
-spec index(schema(), binary(), list(binary()), boolean()) -> schema().
index(Schema, Name, Columns, Unique) ->
add_constraint(Schema, {index, Name, Columns, Unique}).
-file("src/cquill/schema.gleam", 166).
?DOC(" Add a table-level check constraint\n").
-spec table_check(schema(), binary(), binary()) -> schema().
table_check(Schema, Name, Expression) ->
add_constraint(Schema, {table_check, Name, Expression}).
-file("src/cquill/schema.gleam", 171).
?DOC(" Add a comment to the schema\n").
-spec with_comment(schema(), binary()) -> schema().
with_comment(Schema, Text) ->
{schema,
erlang:element(2, Schema),
erlang:element(3, Schema),
erlang:element(4, Schema),
erlang:element(5, Schema),
erlang:element(6, Schema),
{some, Text}}.
-file("src/cquill/schema.gleam", 180).
?DOC(" Get the source table name\n").
-spec get_source(schema()) -> binary().
get_source(Schema) ->
erlang:element(2, Schema).
-file("src/cquill/schema.gleam", 185).
?DOC(" Get the fully qualified table name (schema.table or just table)\n").
-spec get_qualified_name(schema()) -> binary().
get_qualified_name(Schema) ->
case erlang:element(3, Schema) of
{some, S} ->
<<<<S/binary, "."/utf8>>/binary,
(erlang:element(2, Schema))/binary>>;
none ->
erlang:element(2, Schema)
end.
-file("src/cquill/schema.gleam", 193).
?DOC(" Get all fields in order\n").
-spec get_fields(schema()) -> list(cquill@schema@field:field()).
get_fields(Schema) ->
erlang:element(4, Schema).
-file("src/cquill/schema.gleam", 198).
?DOC(" Get field count\n").
-spec field_count(schema()) -> integer().
field_count(Schema) ->
erlang:length(erlang:element(4, Schema)).
-file("src/cquill/schema.gleam", 203).
?DOC(" Get the primary key columns\n").
-spec get_primary_key(schema()) -> list(binary()).
get_primary_key(Schema) ->
erlang:element(5, Schema).
-file("src/cquill/schema.gleam", 208).
?DOC(" Check if schema has a primary key defined\n").
-spec has_primary_key(schema()) -> boolean().
has_primary_key(Schema) ->
not gleam@list:is_empty(erlang:element(5, Schema)).
-file("src/cquill/schema.gleam", 213).
?DOC(" Get a field by name\n").
-spec get_field(schema(), binary()) -> gleam@option:option(cquill@schema@field:field()).
get_field(Schema, Name) ->
_pipe = gleam@list:find(
erlang:element(4, Schema),
fun(F) -> cquill@schema@field:get_name(F) =:= Name end
),
gleam@option:from_result(_pipe).
-file("src/cquill/schema.gleam", 219).
?DOC(" Check if schema has a field with the given name\n").
-spec has_field(schema(), binary()) -> boolean().
has_field(Schema, Name) ->
gleam@option:is_some(get_field(Schema, Name)).
-file("src/cquill/schema.gleam", 224).
?DOC(" Get all field names in order\n").
-spec field_names(schema()) -> list(binary()).
field_names(Schema) ->
gleam@list:map(
erlang:element(4, Schema),
fun cquill@schema@field:get_name/1
).
-file("src/cquill/schema.gleam", 229).
?DOC(" Get fields that are part of the primary key\n").
-spec primary_key_fields(schema()) -> list(cquill@schema@field:field()).
primary_key_fields(Schema) ->
gleam@list:filter(
erlang:element(4, Schema),
fun(F) ->
gleam@list:contains(
erlang:element(5, Schema),
cquill@schema@field:get_name(F)
)
end
).
-file("src/cquill/schema.gleam", 236).
?DOC(" Get fields that are NOT part of the primary key\n").
-spec non_primary_key_fields(schema()) -> list(cquill@schema@field:field()).
non_primary_key_fields(Schema) ->
gleam@list:filter(
erlang:element(4, Schema),
fun(F) ->
not gleam@list:contains(
erlang:element(5, Schema),
cquill@schema@field:get_name(F)
)
end
).
-file("src/cquill/schema.gleam", 243).
?DOC(" Get all nullable fields\n").
-spec nullable_fields(schema()) -> list(cquill@schema@field:field()).
nullable_fields(Schema) ->
gleam@list:filter(
erlang:element(4, Schema),
fun cquill@schema@field:is_nullable/1
).
-file("src/cquill/schema.gleam", 248).
?DOC(" Get all required (non-nullable, no default) fields\n").
-spec required_fields(schema()) -> list(cquill@schema@field:field()).
required_fields(Schema) ->
gleam@list:filter(
erlang:element(4, Schema),
fun(F) ->
not cquill@schema@field:is_nullable(F) andalso gleam@option:is_none(
erlang:element(4, F)
)
end
).
-file("src/cquill/schema.gleam", 255).
?DOC(" Get all auto-increment fields\n").
-spec auto_increment_fields(schema()) -> list(cquill@schema@field:field()).
auto_increment_fields(Schema) ->
gleam@list:filter(
erlang:element(4, Schema),
fun cquill@schema@field:is_auto_increment/1
).
-file("src/cquill/schema.gleam", 260).
?DOC(" Get fields with foreign key constraints\n").
-spec foreign_key_fields(schema()) -> list(cquill@schema@field:field()).
foreign_key_fields(Schema) ->
gleam@list:filter(
erlang:element(4, Schema),
fun(F) ->
cquill@schema@field:has_constraint(
F,
fun cquill@schema@field:is_foreign_key_constraint/1
)
end
).
-file("src/cquill/schema.gleam", 272).
?DOC(
" Create a new schema with only the specified fields\n"
" Useful for creating insert/update schemas without auto-generated fields\n"
).
-spec select_fields(schema(), list(binary())) -> schema().
select_fields(Schema, Names) ->
Selected_fields = gleam@list:filter(
erlang:element(4, Schema),
fun(F) ->
gleam@list:contains(Names, cquill@schema@field:get_name(F))
end
),
{schema,
erlang:element(2, Schema),
erlang:element(3, Schema),
Selected_fields,
erlang:element(5, Schema),
erlang:element(6, Schema),
erlang:element(7, Schema)}.
-file("src/cquill/schema.gleam", 280).
?DOC(
" Create a new schema excluding the specified fields\n"
" Useful for creating public schemas without sensitive fields\n"
).
-spec exclude_fields(schema(), list(binary())) -> schema().
exclude_fields(Schema, Names) ->
Remaining_fields = gleam@list:filter(
erlang:element(4, Schema),
fun(F) ->
not gleam@list:contains(Names, cquill@schema@field:get_name(F))
end
),
{schema,
erlang:element(2, Schema),
erlang:element(3, Schema),
Remaining_fields,
erlang:element(5, Schema),
erlang:element(6, Schema),
erlang:element(7, Schema)}.
-file("src/cquill/schema.gleam", 290).
?DOC(
" Create a new schema with only non-auto-generated fields\n"
" Useful for insert schemas\n"
).
-spec insertable_fields(schema()) -> schema().
insertable_fields(Schema) ->
Fields = gleam@list:filter(
erlang:element(4, Schema),
fun(F) -> not cquill@schema@field:is_auto_increment(F) end
),
{schema,
erlang:element(2, Schema),
erlang:element(3, Schema),
Fields,
erlang:element(5, Schema),
erlang:element(6, Schema),
erlang:element(7, Schema)}.
-file("src/cquill/schema.gleam", 296).
?DOC(" Rename a field in the schema\n").
-spec rename_field(schema(), binary(), binary()) -> schema().
rename_field(Schema, Old_name, New_name) ->
Updated_fields = gleam@list:map(
erlang:element(4, Schema),
fun(F) -> case cquill@schema@field:get_name(F) =:= Old_name of
true ->
{field,
New_name,
erlang:element(3, F),
erlang:element(4, F),
erlang:element(5, F),
erlang:element(6, F)};
false ->
F
end end
),
{schema,
erlang:element(2, Schema),
erlang:element(3, Schema),
Updated_fields,
erlang:element(5, Schema),
erlang:element(6, Schema),
erlang:element(7, Schema)}.
-file("src/cquill/schema.gleam", 385).
-spec check_duplicates(list(binary()), list(schema_error())) -> list(schema_error()).
check_duplicates(Names, Errors) ->
case Names of
[] ->
Errors;
[Name | Rest] ->
case gleam@list:contains(Rest, Name) of
true ->
check_duplicates(Rest, [{duplicate_field, Name} | Errors]);
false ->
check_duplicates(Rest, Errors)
end
end.
-file("src/cquill/schema.gleam", 400).
-spec validate_constraint_columns(
binary(),
list(binary()),
schema(),
list(schema_error())
) -> list(schema_error()).
validate_constraint_columns(Constraint_name, Columns, Schema, Errors) ->
gleam@list:fold(
Columns,
Errors,
fun(Errs, Col) -> case has_field(Schema, Col) of
true ->
Errs;
false ->
[{invalid_constraint_column, Constraint_name, Col} | Errs]
end end
).
-file("src/cquill/schema.gleam", 334).
?DOC(" Validate a schema and return any errors\n").
-spec validate(schema()) -> list(schema_error()).
validate(Schema) ->
Errors = [],
Errors@1 = case gleam@string:is_empty(erlang:element(2, Schema)) of
true ->
[{invalid_table_name, <<""/utf8>>} | Errors];
false ->
Errors
end,
Errors@2 = case gleam@list:is_empty(erlang:element(4, Schema)) of
true ->
[empty_schema | Errors@1];
false ->
Errors@1
end,
Field_name_list = field_names(Schema),
Errors@3 = check_duplicates(Field_name_list, Errors@2),
Errors@4 = gleam@list:fold(
erlang:element(5, Schema),
Errors@3,
fun(Errs, Pk_col) -> case has_field(Schema, Pk_col) of
true ->
Errs;
false ->
[{invalid_primary_key, Pk_col} | Errs]
end end
),
Errors@5 = gleam@list:fold(
erlang:element(6, Schema),
Errors@4,
fun(Errs@1, Constraint) -> case Constraint of
{unique_constraint, Name, Columns} ->
validate_constraint_columns(Name, Columns, Schema, Errs@1);
{index, Name@1, Columns@1, _} ->
validate_constraint_columns(
Name@1,
Columns@1,
Schema,
Errs@1
);
{table_check, _, _} ->
Errs@1;
{composite_foreign_key, Name@2, Columns@2, _, _} ->
validate_constraint_columns(
Name@2,
Columns@2,
Schema,
Errs@1
)
end end
),
lists:reverse(Errors@5).
-file("src/cquill/schema.gleam", 380).
?DOC(" Check if schema is valid\n").
-spec is_valid(schema()) -> boolean().
is_valid(Schema) ->
gleam@list:is_empty(validate(Schema)).
-file("src/cquill/schema.gleam", 428).
?DOC(" Find differences between two schemas\n").
-spec diff(schema(), schema()) -> list(schema_diff()).
diff(Old_schema, New_schema) ->
Diffs = [],
Old_names = field_names(Old_schema),
Added_diffs = gleam@list:filter_map(
erlang:element(4, New_schema),
fun(F) ->
case gleam@list:contains(Old_names, cquill@schema@field:get_name(F)) of
true ->
{error, nil};
false ->
{ok, {field_added, F}}
end
end
),
New_names = field_names(New_schema),
Removed_diffs = gleam@list:filter_map(
erlang:element(4, Old_schema),
fun(F@1) ->
Name = cquill@schema@field:get_name(F@1),
case gleam@list:contains(New_names, Name) of
true ->
{error, nil};
false ->
{ok, {field_removed, Name}}
end
end
),
Type_change_diffs = gleam@list:filter_map(
erlang:element(4, Old_schema),
fun(Old_field) ->
Name@1 = cquill@schema@field:get_name(Old_field),
case get_field(New_schema, Name@1) of
none ->
{error, nil};
{some, New_field} ->
Old_type = cquill@schema@field:get_type(Old_field),
New_type = cquill@schema@field:get_type(New_field),
case Old_type =:= New_type of
true ->
{error, nil};
false ->
{ok,
{field_type_changed, Name@1, Old_type, New_type}}
end
end
end
),
Pk_diffs = case erlang:element(5, Old_schema) =:= erlang:element(
5,
New_schema
) of
true ->
[];
false ->
[{primary_key_changed,
erlang:element(5, Old_schema),
erlang:element(5, New_schema)}]
end,
lists:append(
[Diffs, Added_diffs, Removed_diffs, Type_change_diffs, Pk_diffs]
).