babik 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +16 -0
- data/README.md +718 -0
- data/Rakefile +18 -0
- data/lib/babik.rb +122 -0
- data/lib/babik/database.rb +16 -0
- data/lib/babik/queryset.rb +154 -0
- data/lib/babik/queryset/components/aggregation.rb +172 -0
- data/lib/babik/queryset/components/limit.rb +22 -0
- data/lib/babik/queryset/components/order.rb +161 -0
- data/lib/babik/queryset/components/projection.rb +118 -0
- data/lib/babik/queryset/components/select_related.rb +78 -0
- data/lib/babik/queryset/components/sql_renderer.rb +99 -0
- data/lib/babik/queryset/components/where.rb +43 -0
- data/lib/babik/queryset/lib/association/foreign_association_chain.rb +97 -0
- data/lib/babik/queryset/lib/association/select_related_association_chain.rb +32 -0
- data/lib/babik/queryset/lib/condition.rb +103 -0
- data/lib/babik/queryset/lib/field.rb +34 -0
- data/lib/babik/queryset/lib/join/association_joiner.rb +39 -0
- data/lib/babik/queryset/lib/join/join.rb +86 -0
- data/lib/babik/queryset/lib/selection/config.rb +19 -0
- data/lib/babik/queryset/lib/selection/foreign_selection.rb +39 -0
- data/lib/babik/queryset/lib/selection/local_selection.rb +40 -0
- data/lib/babik/queryset/lib/selection/operation/base.rb +126 -0
- data/lib/babik/queryset/lib/selection/operation/date.rb +178 -0
- data/lib/babik/queryset/lib/selection/operation/operations.rb +201 -0
- data/lib/babik/queryset/lib/selection/operation/regex.rb +58 -0
- data/lib/babik/queryset/lib/selection/path/foreign_path.rb +50 -0
- data/lib/babik/queryset/lib/selection/path/local_path.rb +44 -0
- data/lib/babik/queryset/lib/selection/path/path.rb +23 -0
- data/lib/babik/queryset/lib/selection/select_related_selection.rb +38 -0
- data/lib/babik/queryset/lib/selection/selection.rb +19 -0
- data/lib/babik/queryset/lib/update/assignment.rb +108 -0
- data/lib/babik/queryset/mixins/aggregatable.rb +17 -0
- data/lib/babik/queryset/mixins/bounded.rb +38 -0
- data/lib/babik/queryset/mixins/clonable.rb +52 -0
- data/lib/babik/queryset/mixins/countable.rb +44 -0
- data/lib/babik/queryset/mixins/deletable.rb +13 -0
- data/lib/babik/queryset/mixins/distinguishable.rb +27 -0
- data/lib/babik/queryset/mixins/filterable.rb +51 -0
- data/lib/babik/queryset/mixins/limitable.rb +88 -0
- data/lib/babik/queryset/mixins/lockable.rb +31 -0
- data/lib/babik/queryset/mixins/none.rb +16 -0
- data/lib/babik/queryset/mixins/projectable.rb +34 -0
- data/lib/babik/queryset/mixins/related_selector.rb +28 -0
- data/lib/babik/queryset/mixins/set_operations.rb +32 -0
- data/lib/babik/queryset/mixins/sortable.rb +49 -0
- data/lib/babik/queryset/mixins/sql_renderizable.rb +17 -0
- data/lib/babik/queryset/mixins/updatable.rb +14 -0
- data/lib/babik/queryset/templates/default/delete/main.sql.erb +14 -0
- data/lib/babik/queryset/templates/default/select/components/aggregation.sql.erb +5 -0
- data/lib/babik/queryset/templates/default/select/components/from.sql.erb +16 -0
- data/lib/babik/queryset/templates/default/select/components/from_set.sql.erb +3 -0
- data/lib/babik/queryset/templates/default/select/components/from_table.sql.erb +2 -0
- data/lib/babik/queryset/templates/default/select/components/limit.sql.erb +10 -0
- data/lib/babik/queryset/templates/default/select/components/order_by.sql.erb +9 -0
- data/lib/babik/queryset/templates/default/select/components/projection.sql.erb +7 -0
- data/lib/babik/queryset/templates/default/select/components/select_related.sql.erb +26 -0
- data/lib/babik/queryset/templates/default/select/components/where.sql.erb +39 -0
- data/lib/babik/queryset/templates/default/select/main.sql.erb +42 -0
- data/lib/babik/queryset/templates/default/update/main.sql.erb +15 -0
- data/lib/babik/queryset/templates/mssql/select/components/limit.sql.erb +8 -0
- data/lib/babik/queryset/templates/mssql/select/components/order_by.sql.erb +21 -0
- data/lib/babik/queryset/templates/mysql2/delete/main.sql.erb +15 -0
- data/lib/babik/queryset/templates/mysql2/update/main.sql.erb +18 -0
- data/lib/babik/queryset/templates/sqlite3/select/components/from_set.sql.erb +5 -0
- data/test/config/db/schema.rb +83 -0
- data/test/config/models/bad_post.rb +5 -0
- data/test/config/models/bad_tag.rb +5 -0
- data/test/config/models/category.rb +4 -0
- data/test/config/models/geozone.rb +6 -0
- data/test/config/models/group.rb +5 -0
- data/test/config/models/group_user.rb +5 -0
- data/test/config/models/post.rb +24 -0
- data/test/config/models/post_tag.rb +5 -0
- data/test/config/models/tag.rb +5 -0
- data/test/config/models/user.rb +6 -0
- data/test/delete/delete_test.rb +60 -0
- data/test/delete/foreign_conditions_delete_test.rb +57 -0
- data/test/delete/local_conditions_delete_test.rb +20 -0
- data/test/enable_coverage.rb +17 -0
- data/test/lib/selection/operation/log/test-queries.log +1 -0
- data/test/lib/selection/operation/test_date.rb +131 -0
- data/test/lib/selection/operation/test_regex.rb +55 -0
- data/test/other/clone_test.rb +129 -0
- data/test/other/escape_test.rb +21 -0
- data/test/other/inverse_of_required_test.rb +33 -0
- data/test/select/aggregate_test.rb +151 -0
- data/test/select/bounds_test.rb +46 -0
- data/test/select/count_test.rb +147 -0
- data/test/select/distinct_test.rb +38 -0
- data/test/select/exclude_test.rb +72 -0
- data/test/select/filter_from_object_test.rb +125 -0
- data/test/select/filter_test.rb +207 -0
- data/test/select/for_update_test.rb +19 -0
- data/test/select/foreign_selection_test.rb +60 -0
- data/test/select/get_test.rb +40 -0
- data/test/select/limit_test.rb +109 -0
- data/test/select/local_selection_test.rb +24 -0
- data/test/select/lookup_test.rb +208 -0
- data/test/select/none_test.rb +40 -0
- data/test/select/order_test.rb +165 -0
- data/test/select/project_test.rb +107 -0
- data/test/select/select_related_test.rb +124 -0
- data/test/select/subquery_test.rb +50 -0
- data/test/set_operations/basic_usage_test.rb +121 -0
- data/test/test_helper.rb +55 -0
- data/test/update/update_test.rb +93 -0
- metadata +278 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Babik
|
4
|
+
module QuerySet
|
5
|
+
# Functionality related to the UPDATE operation
|
6
|
+
module Updatable
|
7
|
+
# Runs the update
|
8
|
+
# @param update_command [Hash{field: value}] Runs the update query.
|
9
|
+
def update(update_command)
|
10
|
+
self.model.connection.execute(sql.update(update_command))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%#
|
2
|
+
SQL default delete.
|
3
|
+
Most DBMS engines optimize IN if there is a simple subquery inside pulling up the conditions as joins.
|
4
|
+
%>
|
5
|
+
|
6
|
+
<% model = queryset.model %>
|
7
|
+
|
8
|
+
DELETE
|
9
|
+
FROM <%= model.table_name %>
|
10
|
+
WHERE id IN (
|
11
|
+
SELECT id FROM (
|
12
|
+
<%= queryset.sql.select %>
|
13
|
+
) AS subquery
|
14
|
+
)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
<% if queryset.class == Babik::QuerySet::Base %>
|
3
|
+
|
4
|
+
FROM
|
5
|
+
<%= render.('select/components/from_table.sql.erb', {queryset: queryset}) %>
|
6
|
+
|
7
|
+
<% elsif queryset.is_a?(Babik::QuerySet::SetOperation) %>
|
8
|
+
|
9
|
+
FROM
|
10
|
+
<%= render.('select/components/from_set.sql.erb', {queryset: queryset}) %>
|
11
|
+
|
12
|
+
<% else %>
|
13
|
+
|
14
|
+
Operation NOT recognized
|
15
|
+
|
16
|
+
<% end %>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<% select_related = queryset._select_related %>
|
2
|
+
<% associations = select_related.associations %>
|
3
|
+
|
4
|
+
<%# Include each association target model in the select_related call in the SQL SELECT %>
|
5
|
+
<% associations.each_with_index do |association, association_index| %>
|
6
|
+
|
7
|
+
<% last_one_association = association_index == associations.length - 1 %>
|
8
|
+
|
9
|
+
<% target_alias = association.target_alias %>
|
10
|
+
<% target_model = association.target_model %>
|
11
|
+
|
12
|
+
<%# Include each column of the target model in the SQL SELECT %>
|
13
|
+
<% target_model.column_names.each_with_index do |target_field, target_field_index| %>
|
14
|
+
|
15
|
+
<% last_one_target_field = target_field_index == target_model.column_names.length - 1 %>
|
16
|
+
|
17
|
+
<%# Each column is included with an alias. That alias is the association path replacing :: with __ %>
|
18
|
+
<%= target_alias %>.<%= target_field %>
|
19
|
+
AS
|
20
|
+
<%= association.id %>__<%= target_field %>
|
21
|
+
|
22
|
+
<% unless last_one_target_field %>,<% end %>
|
23
|
+
<% end %>
|
24
|
+
|
25
|
+
<% unless last_one_association %>,<% end %>
|
26
|
+
<% end %>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<% where = queryset._where %>
|
2
|
+
|
3
|
+
<% if where.inclusion_filters? || where.exclusion_filters? %>
|
4
|
+
|
5
|
+
WHERE (
|
6
|
+
<% if where.inclusion_filters? %>
|
7
|
+
(
|
8
|
+
<% inclusion_filters = where.inclusion_filters %>
|
9
|
+
<% inclusion_filters.each_with_index do |condition, condition_index| %>
|
10
|
+
<% last_one = condition_index == inclusion_filters.length - 1 %>
|
11
|
+
(
|
12
|
+
<%= condition.sql %>
|
13
|
+
)
|
14
|
+
<% unless last_one %>AND<% end %>
|
15
|
+
<% end %>
|
16
|
+
)
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<% if where.inclusion_filters? && where.exclusion_filters? %>
|
20
|
+
AND
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<% if where.exclusion_filters? %>
|
24
|
+
( NOT
|
25
|
+
(
|
26
|
+
<% exclusion_filters = where.exclusion_filters %>
|
27
|
+
<% exclusion_filters.each_with_index do |condition, condition_index| %>
|
28
|
+
<% last_one = condition_index == exclusion_filters.length - 1 %>
|
29
|
+
(
|
30
|
+
<%= condition.sql %>
|
31
|
+
)
|
32
|
+
<% unless last_one %>AND<% end %>
|
33
|
+
<% end %>
|
34
|
+
)
|
35
|
+
)
|
36
|
+
<% end %>
|
37
|
+
)
|
38
|
+
|
39
|
+
<% end %>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<% model = queryset.model %>
|
2
|
+
|
3
|
+
SELECT
|
4
|
+
|
5
|
+
<% if queryset.lock? %>
|
6
|
+
<%= queryset._lock_type %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<% if queryset.distinct? %>
|
10
|
+
DISTINCT
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<% if queryset.aggregation? %>
|
14
|
+
<%= render.('select/components/aggregation.sql.erb', {queryset: queryset}) %>
|
15
|
+
<% else %>
|
16
|
+
<% if queryset.projection? %>
|
17
|
+
<%# Projection of the results %>
|
18
|
+
<%= render.('select/components/projection.sql.erb', {queryset: queryset}) %>
|
19
|
+
<% else %>
|
20
|
+
<%= model.table_name %>.*
|
21
|
+
<% if queryset.select_related? %>
|
22
|
+
,
|
23
|
+
<%= render.('select/components/select_related.sql.erb', {queryset: queryset}) %>
|
24
|
+
<% end %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
<%# From %>
|
30
|
+
<%= render.('select/components/from.sql.erb', {queryset: queryset}) %>
|
31
|
+
|
32
|
+
<%# Left joins %>
|
33
|
+
<%= queryset.sql.left_joins %>
|
34
|
+
|
35
|
+
<%# Where conditions %>
|
36
|
+
<%= render.('select/components/where.sql.erb', {queryset: queryset}) %>
|
37
|
+
|
38
|
+
<%# Order the results %>
|
39
|
+
<%= render.('select/components/order_by.sql.erb', {queryset: queryset}) %>
|
40
|
+
|
41
|
+
<%# Limit the results (only MySQL, MariaDB and PostgreSQL) %>
|
42
|
+
<%= render.('select/components/limit.sql.erb', {queryset: queryset}) %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%#
|
2
|
+
SQL default update.
|
3
|
+
Most DBMS engines optimize IN if there is a simple subquery inside pulling up the conditions as joins
|
4
|
+
%>
|
5
|
+
|
6
|
+
<% model = queryset.model %>
|
7
|
+
|
8
|
+
UPDATE <%= model.table_name %>
|
9
|
+
SET
|
10
|
+
<% update_command.each do |field, value| %>
|
11
|
+
<%= Babik::QuerySet::Update::Assignment.sql_field(model, field) %> = <%= Babik::QuerySet::Update::Assignment.sql_value(value) %>
|
12
|
+
<% end %>
|
13
|
+
WHERE id IN (
|
14
|
+
<%= queryset.sql.select %>
|
15
|
+
)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% order = queryset._order %>
|
2
|
+
|
3
|
+
|
4
|
+
<%# Order the results %>
|
5
|
+
<% if order %>
|
6
|
+
|
7
|
+
ORDER BY
|
8
|
+
<%= order.sql %>
|
9
|
+
|
10
|
+
<% else %>
|
11
|
+
|
12
|
+
<% limit = queryset._limit %>
|
13
|
+
|
14
|
+
<%# MSSQL server requires always an ORDER BY if a OFFSET - FETCH NEXT clause is present %>
|
15
|
+
<% if limit %>
|
16
|
+
|
17
|
+
ORDER BY <%= queryset.model.table_name %>.id ASC
|
18
|
+
|
19
|
+
<% end %>
|
20
|
+
|
21
|
+
<% end %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%#
|
2
|
+
SQL default delete.
|
3
|
+
Most DBMS engines optimize IN if there is a simple subquery inside pulling up the conditions as joins.
|
4
|
+
%>
|
5
|
+
|
6
|
+
<% model = queryset.model %>
|
7
|
+
|
8
|
+
|
9
|
+
DELETE <%= model.table_name %>.*
|
10
|
+
FROM <%= model.table_name %> <%= model.table_name %>
|
11
|
+
|
12
|
+
<%= queryset.sql.left_joins %>
|
13
|
+
|
14
|
+
<%# Where conditions %>
|
15
|
+
<%= render.('select/components/where.sql.erb', {queryset: queryset}) %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%#
|
2
|
+
SQL default update.
|
3
|
+
Most DBMS engines optimize IN if there is a simple subquery inside pulling up the conditions as joins
|
4
|
+
%>
|
5
|
+
|
6
|
+
<% model = queryset.model %>
|
7
|
+
|
8
|
+
UPDATE <%= model.table_name %> AS <%= model.table_name %>
|
9
|
+
|
10
|
+
<%= queryset.sql.left_joins %>
|
11
|
+
|
12
|
+
SET
|
13
|
+
<% update_command.each do |field, value| %>
|
14
|
+
<%= model.table_name %>.<%= Babik::QuerySet::Update::Assignment.sql_field(model, field) %> = <%= Babik::QuerySet::Update::Assignment.sql_value(value) %>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<%# Where conditions %>
|
18
|
+
<%= render.('select/components/where.sql.erb', {queryset: queryset}) %>
|
@@ -0,0 +1,83 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
self.verbose = false
|
3
|
+
|
4
|
+
create_table :geo_zones, force: true do |t|
|
5
|
+
t.string :name
|
6
|
+
t.text :description
|
7
|
+
t.integer :parent_zone_id
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :group, force: true do |t|
|
12
|
+
t.string :name
|
13
|
+
t.string :description
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :group_users, force: true do |t|
|
18
|
+
t.integer :group_id
|
19
|
+
t.integer :user_id
|
20
|
+
t.timestamps
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :users, force: true do |t|
|
24
|
+
t.integer :zone_id
|
25
|
+
t.string :first_name
|
26
|
+
t.string :last_name
|
27
|
+
t.text :biography
|
28
|
+
t.integer :age
|
29
|
+
t.string :email
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table :posts, force: true do |t|
|
34
|
+
t.string :title
|
35
|
+
t.text :content
|
36
|
+
t.integer :stars
|
37
|
+
t.integer :author_id
|
38
|
+
t.integer :category_id
|
39
|
+
t.timestamps
|
40
|
+
end
|
41
|
+
|
42
|
+
create_table :post_tags, force: true do |t|
|
43
|
+
t.integer :post_id
|
44
|
+
t.integer :tag_id
|
45
|
+
t.timestamps
|
46
|
+
end
|
47
|
+
|
48
|
+
create_table :tags, force: true do |t|
|
49
|
+
t.string :name
|
50
|
+
t.timestamps
|
51
|
+
end
|
52
|
+
|
53
|
+
create_table :categories, force: true do |t|
|
54
|
+
t.string :name
|
55
|
+
t.timestamps
|
56
|
+
end
|
57
|
+
|
58
|
+
# Post class only used to show what happens if a model
|
59
|
+
# has no inverse_of in one of its associations
|
60
|
+
create_table :bad_posts, force: true do |t|
|
61
|
+
t.string :title
|
62
|
+
t.text :content
|
63
|
+
t.integer :stars
|
64
|
+
t.integer :category_id
|
65
|
+
t.timestamps
|
66
|
+
end
|
67
|
+
|
68
|
+
# Tag class that will make use of a has_and_belongs_to_many relationship
|
69
|
+
# Used only for test the detection of has_and_belongs_to_many and the raise
|
70
|
+
# of an exception happens
|
71
|
+
create_table :bad_tags, force: true do |t|
|
72
|
+
t.string :name
|
73
|
+
t.integer :bad_post_id
|
74
|
+
t.timestamps
|
75
|
+
end
|
76
|
+
|
77
|
+
add_index :users, :email, unique: true
|
78
|
+
add_index :group_users, [:group_id, :user_id], unique: true
|
79
|
+
add_index :categories, :name, unique: true
|
80
|
+
add_index :post_tags, [:post_id, :tag_id], unique: true
|
81
|
+
add_index :posts, [:title, :author_id], unique: true
|
82
|
+
add_index :tags, :name, unique: true
|
83
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
class Post < ActiveRecord::Base
|
3
|
+
belongs_to :author, foreign_key: 'author_id', class_name: 'User', inverse_of: :posts
|
4
|
+
belongs_to :category, inverse_of: :posts
|
5
|
+
has_many :post_tags
|
6
|
+
has_many :tags, through: :post_tags, inverse_of: :posts
|
7
|
+
|
8
|
+
def add_tag(tag)
|
9
|
+
begin
|
10
|
+
PostTag.create!(post: self, tag: tag)
|
11
|
+
rescue ActiveRecord::RecordNotUnique
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_tag_by_name(tag_name)
|
16
|
+
begin
|
17
|
+
new_tag = Tag.create!(name: tag_name)
|
18
|
+
rescue ActiveRecord::RecordNotUnique
|
19
|
+
new_tag = Tag.find_by(name: tag_name)
|
20
|
+
end
|
21
|
+
add_tag(new_tag)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|