@@ -79,24 +79,6 @@ if Code.ensure_loaded?(Sqlitex.Server) do
79
79
"ROLLBACK TO SAVEPOINT " <> savepoint
80
80
end
81
81
82
- def release_savepoint ( name ) do
83
- "RELEASE " <> name
84
- end
85
-
86
- # Initiate a transaction with a savepoint. If any error occurs when we call
87
- # the func parameter, rollback our changes. Returns the result of the call
88
- # to func.
89
- def with_savepoint ( pid , func ) do
90
- sp = "sp_" <> random_id
91
- :ok = exec ( pid , savepoint ( sp ) )
92
- result = safe_call ( pid , func , sp )
93
- if is_tuple ( result ) and elem ( result , 0 ) == :error do
94
- :ok = exec ( pid , rollback_to_savepoint ( sp ) )
95
- end
96
- :ok = exec ( pid , release_savepoint ( sp ) )
97
- result
98
- end
99
-
100
82
## Query
101
83
102
84
alias Ecto.Query
@@ -109,16 +91,19 @@ if Code.ensure_loaded?(Sqlitex.Server) do
109
91
end
110
92
def all ( query ) do
111
93
sources = create_names ( query , :select )
94
+ distinct_exprs = distinct_exprs ( query , sources )
112
95
113
- select = select ( query . select , query . distinct , sources )
114
96
from = from ( sources )
97
+ select = select ( query , distinct_exprs , sources )
115
98
join = join ( query , sources )
116
99
where = where ( query , sources )
117
- group_by = group_by ( query . group_bys , query . havings , sources )
118
- order_by = order_by ( query . order_bys , sources )
119
- limit = limit ( query . limit , query . offset , sources )
100
+ group_by = group_by ( query , sources )
101
+ having = having ( query , sources )
102
+ order_by = order_by ( query , distinct_exprs , sources )
103
+ limit = limit ( query , sources )
104
+ offset = offset ( query , sources )
120
105
121
- assemble [ select , from , join , where , group_by , order_by , limit ]
106
+ assemble ( [ select , from , join , where , group_by , having , order_by , limit , offset ] )
122
107
end
123
108
124
109
def update_all ( % Ecto.Query { joins: [ _ | _ ] } ) do
@@ -349,17 +334,23 @@ if Code.ensure_loaded?(Sqlitex.Server) do
349
334
350
335
defp handle_call ( fun , _arity ) , do: { :fun , Atom . to_string ( fun ) }
351
336
352
- defp select ( % SelectExpr { fields: fields } , distinct , sources ) do
353
- fields = Enum . map_join ( fields , ", " , fn ( f ) ->
354
- assemble ( expr ( f , sources , "query" ) )
355
- end )
356
- [ "SELECT ", distinct ( distinct ) , fields ]
337
+ defp select ( % Query { select: % SelectExpr { fields: fields } , distinct: distinct } = query ,
338
+ distinct_exprs , sources ) do
339
+ "SELECT " <>
340
+ distinct ( distinct , distinct_exprs ) <>
341
+ Enum . map_join ( fields , ", ", & expr ( & 1 , sources , query ) )
357
342
end
358
343
359
- defp distinct ( nil ) , do: [ ]
360
- defp distinct ( % QueryExpr { expr: true } ) , do: "DISTINCT"
361
- defp distinct ( % QueryExpr { expr: false } ) , do: [ ]
362
- defp distinct ( % QueryExpr { expr: exprs } ) when is_list ( exprs ) do
344
+ defp distinct_exprs ( % Query { distinct: % QueryExpr { expr: exprs } } = query , sources )
345
+ when is_list ( exprs ) do
346
+ Enum . map_join ( exprs , ", " , & expr ( & 1 , sources , query ) )
347
+ end
348
+ defp distinct_exprs ( _ , _ ) , do: ""
349
+
350
+ defp distinct ( nil , _sources ) , do: ""
351
+ defp distinct ( % QueryExpr { expr: true } , _exprs ) , do: "DISTINCT "
352
+ defp distinct ( % QueryExpr { expr: false } , _exprs ) , do: ""
353
+ defp distinct ( _query , exprs ) do
363
354
raise ArgumentError , "DISTINCT with multiple columns is not supported by SQLite"
364
355
end
365
356
@@ -376,7 +367,7 @@ if Code.ensure_loaded?(Sqlitex.Server) do
376
367
where = expr ( expr , sources , query )
377
368
"USING #{ table } AS #{ name } WHERE " <> where
378
369
% JoinExpr { qual: qual } ->
379
- error! ( query , "PostgreSQL supports only inner joins on delete_all, got: `#{ qual } `" )
370
+ error! ( query , "SQLite supports only inner joins on delete_all, got: `#{ qual } `" )
380
371
end )
381
372
end
382
373
@@ -459,49 +450,56 @@ if Code.ensure_loaded?(Sqlitex.Server) do
459
450
[ "WHERE" | Enum . intersperse ( filters , "AND" ) ]
460
451
end
461
452
462
- defp having ( [ ] , _sources ) , do: [ ]
463
- defp having ( havings , sources ) do
464
- exprs = map_intersperse havings , "AND" , fn % QueryExpr { expr: expr } ->
465
- [ "(" , expr ( expr , sources , "query" ) , ")" ]
466
- end
467
- [ "HAVING" | exprs ]
453
+ defp having ( % Query { havings: havings } = query , sources ) do
454
+ boolean ( "HAVING" , havings , sources , query )
468
455
end
469
456
470
- defp group_by ( group_bys , havings , sources ) do
471
- Enum . map_join ( group_bys , ", " , fn % QueryExpr { expr: expr } ->
472
- Enum . map_join ( expr , ", " , & assemble ( expr ( & 1 , sources , "query" ) ) )
473
- end )
474
- |> group_by_clause ( havings , sources )
475
- end
457
+ defp group_by ( % Query { group_bys: group_bys } = query , sources ) do
458
+ exprs =
459
+ Enum . map_join ( group_bys , ", " , fn
460
+ % QueryExpr { expr: expr } ->
461
+ Enum . map_join ( expr , ", " , & expr ( & 1 , sources , query ) )
462
+ end )
476
463
477
- defp group_by_clause ( "" , _ , _ ) , do: [ ]
478
- defp group_by_clause ( exprs , havings , sources ) do
479
- [ "GROUP BY" , exprs , having ( havings , sources ) ]
464
+ case exprs do
465
+ "" -> [ ]
466
+ _ -> "GROUP BY " <> exprs
467
+ end
480
468
end
481
469
482
- defp order_by ( order_bys , sources ) do
483
- Enum . map_join ( order_bys , ", " , fn % QueryExpr { expr: expr } ->
484
- Enum . map_join ( expr , ", " , & ordering_term ( & 1 , sources ) )
485
- end )
486
- |> order_by_clause
487
- end
470
+ defp order_by ( % Query { order_bys: order_bys } = query , distinct_exprs , sources ) do
471
+ exprs =
472
+ Enum . map_join ( order_bys , ", " , fn
473
+ % QueryExpr { expr: expr } ->
474
+ Enum . map_join ( expr , ", " , & order_by_expr ( & 1 , sources , query ) )
475
+ end )
488
476
489
- defp order_by_clause ( "" ) , do: [ ]
490
- defp order_by_clause ( exprs ) , do: [ "ORDER BY" , exprs ]
477
+ case { distinct_exprs , exprs } do
478
+ { _ , "" } ->
479
+ [ ]
480
+ { "" , _ } ->
481
+ "ORDER BY " <> exprs
482
+ { _ , _ } ->
483
+ "ORDER BY " <> distinct_exprs <> ", " <> exprs
484
+ end
485
+ end
491
486
492
- defp ordering_term ( { :asc , expr } , sources ) , do: assemble ( expr ( expr , sources , "query" ) )
493
- defp ordering_term ( { :desc , expr } , sources ) do
494
- assemble ( expr ( expr , sources , "query" ) ) <> " DESC"
487
+ defp order_by_expr ( { dir , expr } , sources , query ) do
488
+ str = expr ( expr , sources , query )
489
+ case dir do
490
+ :asc -> str
491
+ :desc -> str <> " DESC"
492
+ end
495
493
end
496
494
497
- defp limit ( nil , _offset , _sources ) , do: [ ]
498
- defp limit ( % QueryExpr { expr: expr } , offset , sources ) do
499
- [ "LIMIT" , expr ( expr , sources , " query" ) , offset ( offset , sources ) ]
495
+ defp limit ( % Query { limit: nil } , _sources ) , do: [ ]
496
+ defp limit ( % Query { limit: % QueryExpr { expr: expr } } = query , sources ) do
497
+ "LIMIT " <> expr ( expr , sources , query )
500
498
end
501
499
502
- defp offset ( nil , _sources ) , do: [ ]
503
- defp offset ( % QueryExpr { expr: expr } , sources ) do
504
- [ "OFFSET" , expr ( expr , sources , " query" ) ]
500
+ defp offset ( % Query { offset: nil } , _sources ) , do: [ ]
501
+ defp offset ( % Query { offset: % QueryExpr { expr: expr } } = query , sources ) do
502
+ "OFFSET " <> expr ( expr , sources , query )
505
503
end
506
504
507
505
defp boolean ( _name , [ ] , _sources , _query ) , do: [ ]
@@ -519,17 +517,19 @@ if Code.ensure_loaded?(Sqlitex.Server) do
519
517
520
518
defp expr ( { { :. , _ , [ { :& , _ , [ idx ] } , field ] } , _ , [ ] } , sources , _query ) when is_atom ( field ) do
521
519
{ _ , name , _ } = elem ( sources , idx )
522
- "#{ name } .#{ quote_id ( field ) } "
520
+ "#{ name } .#{ quote_name ( field ) } "
523
521
end
524
522
525
- defp expr ( { :& , _ , [ idx ] } , sources , _query ) do
523
+ defp expr ( { :& , _ , [ idx ] } , sources , query ) do
526
524
{ table , name , model } = elem ( sources , idx )
527
525
unless model do
528
- raise ArgumentError , "SQLite requires a model when using selector #{ inspect name } but " <>
529
- "only the table #{ inspect table } was given. Please specify a model " <>
530
- "or specify exactly which fields from #{ inspect name } you desire"
526
+ error! ( query , "SQLite requires a model when using selector " <>
527
+ "#{ inspect name } but only the table #{ inspect table } was given. " <>
528
+ "Please specify a model or specify exactly which fields from " <>
529
+ "#{ inspect name } you desire" )
531
530
end
532
- map_intersperse ( model . __schema__ ( :fields ) , "," , & "#{ name } .#{ quote_id ( & 1 ) } " )
531
+ fields = model . __schema__ ( :fields )
532
+ Enum . map_join ( fields , ", " , & "#{ name } .#{ quote_name ( & 1 ) } " )
533
533
end
534
534
535
535
defp expr ( { :in , _ , [ left , right ] } , sources , query ) when is_list ( right ) do
@@ -554,8 +554,8 @@ if Code.ensure_loaded?(Sqlitex.Server) do
554
554
"NOT (" <> expr ( expr , sources , query ) <> ")"
555
555
end
556
556
557
- defp expr ( { :fragment , _ , [ kw ] } , _sources , _query ) when is_list ( kw ) or tuple_size ( kw ) == 3 do
558
- raise ArgumentError , "SQLite adapter does not support keyword or interpolated fragments"
557
+ defp expr ( { :fragment , _ , [ kw ] } , _sources , query ) when is_list ( kw ) or tuple_size ( kw ) == 3 do
558
+ error! ( query , "SQLite adapter does not support keyword or interpolated fragments" )
559
559
end
560
560
561
561
defp expr ( { :fragment , _ , parts } , sources , query ) do
@@ -565,20 +565,16 @@ if Code.ensure_loaded?(Sqlitex.Server) do
565
565
end )
566
566
end
567
567
568
- # start of SQLite function to display date
569
- # NOTE the open parenthesis must be closed
570
- @ date_format "strftime('%Y-%m-%d'"
568
+ @ datetime_format "strftime('%Y-%m-%d %H:%M:%f000'" # NOTE: Open paren must be closed
571
569
572
- defp expr ( { :date_add , _ , [ date , count , interval ] } , sources , query ) do
573
- "CAST (#{ @ date_format } ,#{ expr ( date , sources , query ) } ,#{ interval ( count , interval , sources ) } ) AS TEXT_DATE )"
570
+ defp expr ( { :datetime_add , _ , [ datetime , count , interval ] } , sources , query ) do
571
+ "CAST (#{ @ datetime_format } ,#{ expr ( datetime , sources , query ) } ,#{ interval ( count , interval , sources ) } ) AS TEXT_DATETIME )"
574
572
end
575
573
576
- # start of SQLite function to display datetime
577
- # NOTE the open parenthesis must be closed
578
- @ datetime_format "strftime('%Y-%m-%d %H:%M:%f000'"
574
+ @ date_format "strftime('%Y-%m-%d'" # NOTE: Open paren must be closed
579
575
580
- defp expr ( { :datetime_add , _ , [ datetime , count , interval ] } , sources , query ) do
581
- "CAST (#{ @ datetime_format } ,#{ expr ( datetime , sources , query ) } ,#{ interval ( count , interval , sources ) } ) AS TEXT_DATETIME )"
576
+ defp expr ( { :date_add , _ , [ date , count , interval ] } , sources , query ) do
577
+ "CAST (#{ @ date_format } ,#{ expr ( date , sources , query ) } ,#{ interval ( count , interval , sources ) } ) AS TEXT_DATE )"
582
578
end
583
579
584
580
defp expr ( { fun , _ , args } , sources , query ) when is_atom ( fun ) and is_list ( args ) do
@@ -604,7 +600,8 @@ if Code.ensure_loaded?(Sqlitex.Server) do
604
600
Decimal . to_string ( decimal , :normal )
605
601
end
606
602
607
- defp expr ( % Ecto.Query.Tagged { value: binary , type: :binary } , _sources , _query ) when is_binary ( binary ) do
603
+ defp expr ( % Ecto.Query.Tagged { value: binary , type: :binary } , _sources , _query )
604
+ when is_binary ( binary ) do
608
605
"X'#{ Base . encode16 ( binary , case: :upper ) } '"
609
606
end
610
607
@@ -888,6 +885,24 @@ if Code.ensure_loaded?(Sqlitex.Server) do
888
885
# Use Ecto's JSON library (currently Poison) for embedded JSON datatypes.
889
886
def json_library , do: Application . get_env ( :ecto , :json_library )
890
887
888
+ # Initiate a transaction with a savepoint. If any error occurs when we call
889
+ # the func parameter, rollback our changes. Returns the result of the call
890
+ # to func.
891
+ defp with_savepoint ( pid , func ) do
892
+ sp = "sp_" <> random_id
893
+ :ok = exec ( pid , savepoint ( sp ) )
894
+ result = safe_call ( pid , func , sp )
895
+ if is_tuple ( result ) and elem ( result , 0 ) == :error do
896
+ :ok = exec ( pid , rollback_to_savepoint ( sp ) )
897
+ end
898
+ :ok = exec ( pid , release_savepoint ( sp ) )
899
+ result
900
+ end
901
+
902
+ defp release_savepoint ( name ) do
903
+ "RELEASE " <> name
904
+ end
905
+
891
906
# Call func.() and return the result. If any exceptions are encountered,
892
907
# safely rollback and release the transaction.
893
908
defp safe_call ( pid , func , sp ) do
@@ -977,7 +992,7 @@ if Code.ensure_loaded?(Sqlitex.Server) do
977
992
end
978
993
979
994
defp error! ( nil , message ) do
980
- raise ArgumentError , message
995
+ raise ArgumentError , message: message
981
996
end
982
997
defp error! ( query , message ) do
983
998
raise Ecto.QueryError , query: query , message: message
0 commit comments