seaquel 0.1
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/FEATURES +5 -0
- data/LICENSE +23 -0
- data/README +25 -0
- data/lib/seaquel.rb +13 -0
- data/lib/seaquel/ast.rb +19 -0
- data/lib/seaquel/ast/alias.rb +15 -0
- data/lib/seaquel/ast/assign.rb +17 -0
- data/lib/seaquel/ast/bin_op.rb +21 -0
- data/lib/seaquel/ast/binding.rb +20 -0
- data/lib/seaquel/ast/column.rb +50 -0
- data/lib/seaquel/ast/column_list.rb +11 -0
- data/lib/seaquel/ast/expression.rb +35 -0
- data/lib/seaquel/ast/funcall.rb +17 -0
- data/lib/seaquel/ast/immediate.rb +16 -0
- data/lib/seaquel/ast/join_op.rb +29 -0
- data/lib/seaquel/ast/list.rb +25 -0
- data/lib/seaquel/ast/literal.rb +16 -0
- data/lib/seaquel/ast/node.rb +109 -0
- data/lib/seaquel/ast/order.rb +16 -0
- data/lib/seaquel/ast/table.rb +36 -0
- data/lib/seaquel/ast/table_alias.rb +24 -0
- data/lib/seaquel/bit.rb +34 -0
- data/lib/seaquel/expression_converter.rb +181 -0
- data/lib/seaquel/generator.rb +29 -0
- data/lib/seaquel/module.rb +39 -0
- data/lib/seaquel/quoter.rb +23 -0
- data/lib/seaquel/statement.rb +245 -0
- data/lib/seaquel/statement/join.rb +36 -0
- data/lib/seaquel/statement_gatherer.rb +119 -0
- data/qed/applique/ae.rb +1 -0
- data/qed/applique/sql.rb +2 -0
- data/qed/generation.md +103 -0
- data/spec/functional/delete_spec.rb +19 -0
- data/spec/functional/insert_spec.rb +31 -0
- data/spec/functional/select_spec.rb +99 -0
- data/spec/functional/update_spec.rb +30 -0
- data/spec/lib/ast/column_spec.rb +53 -0
- data/spec/lib/ast/immediate_spec.rb +42 -0
- data/spec/spec_helper.rb +14 -0
- metadata +94 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f51aa6cb26dbabeed5e62e1544877377b44090bc
|
4
|
+
data.tar.gz: 982a28e72454c041dd21129416c7def66b95ff32
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d50b0346f4288a0974d2b0364b3f6596cde261fbcb126662a3aaf9f92fcd5c0255d688c5707afcf2b0d6d4639861427172aac652c20ed5347667175904c97290
|
7
|
+
data.tar.gz: cb41ddc3e7e14f77ab0f0774660fc66a414c5c6c4a4f02e7d42ad205a5ac1a5c6e32d08f48a88ea2c2a8189d6eaa1e9e12d338365a11ed6870791962ac1cef2f
|
data/FEATURES
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2015 Kaspar Schiess
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person
|
5
|
+
obtaining a copy of this software and associated documentation
|
6
|
+
files (the "Software"), to deal in the Software without
|
7
|
+
restriction, including without limitation the rights to use,
|
8
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the
|
10
|
+
Software is furnished to do so, subject to the following
|
11
|
+
conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
18
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
22
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
23
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
A simple SQL generation DSL.
|
2
|
+
|
3
|
+
SYNOPSIS
|
4
|
+
|
5
|
+
include Seaquel
|
6
|
+
select.from(table('foobar')).where(column('name').equal('baz')).to_sql
|
7
|
+
# => %Q(SELECT * FROM "foobar" WHERE "name"='baz')
|
8
|
+
|
9
|
+
DESCRIPTION
|
10
|
+
|
11
|
+
Allows generating SQL from a Ruby DSL. Geared towards the rich possibilities of
|
12
|
+
PostgreSQL, this library can generate standard SQL as well.
|
13
|
+
|
14
|
+
Please also have a look at flounder, Seaquels friendly companion - located one
|
15
|
+
layer higher up our SQL stack, it automates much of the work you'll be doing
|
16
|
+
when you use Seaquel.
|
17
|
+
|
18
|
+
STATUS
|
19
|
+
|
20
|
+
Early alpha
|
21
|
+
|
22
|
+
WHY
|
23
|
+
|
24
|
+
Because nothing really worked at the time we wrote this. Really. We looked.
|
25
|
+
|
data/lib/seaquel.rb
ADDED
data/lib/seaquel/ast.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module Seaquel::AST
|
3
|
+
end
|
4
|
+
|
5
|
+
require 'seaquel/ast/node'
|
6
|
+
require 'seaquel/ast/bin_op'
|
7
|
+
require 'seaquel/ast/join_op'
|
8
|
+
require 'seaquel/ast/assign'
|
9
|
+
require 'seaquel/ast/alias'
|
10
|
+
require 'seaquel/ast/list'
|
11
|
+
require 'seaquel/ast/column_list'
|
12
|
+
require 'seaquel/ast/column'
|
13
|
+
require 'seaquel/ast/table'
|
14
|
+
require 'seaquel/ast/literal'
|
15
|
+
require 'seaquel/ast/immediate'
|
16
|
+
require 'seaquel/ast/binding'
|
17
|
+
require 'seaquel/ast/funcall'
|
18
|
+
require 'seaquel/ast/table_alias'
|
19
|
+
require 'seaquel/ast/order'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Seaquel::AST
|
2
|
+
# Assignment of right to left.
|
3
|
+
# This class can be visited.
|
4
|
+
#
|
5
|
+
class Assign
|
6
|
+
attr_reader :left, :right
|
7
|
+
|
8
|
+
def initialize left, right
|
9
|
+
@left, @right = left, right
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit visitor
|
13
|
+
visitor.visit_assign(left, right)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
module Seaquel::AST
|
3
|
+
# A binary statement as in left=right.
|
4
|
+
# This class can be visited.
|
5
|
+
#
|
6
|
+
class BinOp
|
7
|
+
attr_reader :op, :left, :right
|
8
|
+
|
9
|
+
def initialize op, left, right
|
10
|
+
@op, @left, @right = op, left, right
|
11
|
+
end
|
12
|
+
|
13
|
+
def visit visitor
|
14
|
+
visitor.visit_binop(op, left, right)
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
"(" + ['bin_op', op.inspect, left.inspect, right.inspect].join(' ') + ")"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
require_relative 'expression'
|
3
|
+
|
4
|
+
module Seaquel::AST
|
5
|
+
class Binding < Expression
|
6
|
+
attr_reader :pos
|
7
|
+
|
8
|
+
def initialize pos
|
9
|
+
@pos = pos
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit visitor
|
13
|
+
visitor.visit_binding pos.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
"(bind #{pos})"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
require_relative 'expression'
|
3
|
+
|
4
|
+
module Seaquel::AST
|
5
|
+
class Column < Expression
|
6
|
+
attr_reader :name
|
7
|
+
attr_reader :table
|
8
|
+
|
9
|
+
def initialize name, table=nil
|
10
|
+
@name = name
|
11
|
+
@table = table
|
12
|
+
end
|
13
|
+
|
14
|
+
# Set the column to value.
|
15
|
+
#
|
16
|
+
def to value
|
17
|
+
Assign.new(self, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Visits the column as part of sql generation.
|
21
|
+
#
|
22
|
+
def visit visitor
|
23
|
+
visitor.visit_column self
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns an SQL column reference, including the table name.
|
27
|
+
#
|
28
|
+
def as_full_reference quoter
|
29
|
+
parts = []
|
30
|
+
|
31
|
+
if table
|
32
|
+
parts << table.as_column_prefix(quoter)
|
33
|
+
end
|
34
|
+
|
35
|
+
parts << as_column_reference(quoter)
|
36
|
+
|
37
|
+
parts.join('.')
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns an SQL column reference, excluding table name.
|
41
|
+
#
|
42
|
+
def as_column_reference quoter
|
43
|
+
quoter.column(name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def inspect
|
47
|
+
"(column #{name})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
module Seaquel::AST
|
3
|
+
# Base class for all expression-type AST nodes.
|
4
|
+
#
|
5
|
+
class Expression
|
6
|
+
def self.binop op
|
7
|
+
define_method(op) do |exp|
|
8
|
+
BinOp.new(op, self, exp)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
binop :eq
|
13
|
+
binop :noteq
|
14
|
+
|
15
|
+
binop :lt
|
16
|
+
binop :lteq
|
17
|
+
|
18
|
+
binop :gt
|
19
|
+
binop :gteq
|
20
|
+
|
21
|
+
binop :is
|
22
|
+
binop :isnot
|
23
|
+
|
24
|
+
def as name
|
25
|
+
Alias.new(name, self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def asc
|
29
|
+
Order.new(:asc, self)
|
30
|
+
end
|
31
|
+
def desc
|
32
|
+
Order.new(:desc, self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require_relative 'expression'
|
3
|
+
|
4
|
+
module Seaquel::AST
|
5
|
+
class Funcall < Expression
|
6
|
+
attr_reader :name, :args
|
7
|
+
|
8
|
+
def initialize name, args
|
9
|
+
@name = name
|
10
|
+
@args = args
|
11
|
+
end
|
12
|
+
|
13
|
+
def visit visitor
|
14
|
+
visitor.visit_funcall(name, args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Seaquel::AST
|
2
|
+
# A binary operation that can be used to join things together, like 'AND' or
|
3
|
+
# 'OR'.
|
4
|
+
#
|
5
|
+
class JoinOp
|
6
|
+
attr_reader :op
|
7
|
+
attr_reader :elements
|
8
|
+
|
9
|
+
def initialize op, elements=[]
|
10
|
+
@op, @elements = op, elements
|
11
|
+
end
|
12
|
+
|
13
|
+
def concat element
|
14
|
+
elements << element
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
elements.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit visitor
|
22
|
+
visitor.visit_joinop(op, elements)
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
[:join_op, op, *elements].inspect
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Seaquel::AST
|
5
|
+
class List
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :list
|
9
|
+
|
10
|
+
def initialize initial=[]
|
11
|
+
@list = initial
|
12
|
+
end
|
13
|
+
|
14
|
+
# Behaves array-like in many respects
|
15
|
+
def_delegators :@list, :empty?, :concat, :size, :<<, :map, :replace
|
16
|
+
|
17
|
+
def visit visitor
|
18
|
+
visitor.visit_list(list)
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
'[' + list.map { |e| e.inspect }.join(', ') + ']'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Seaquel::AST
|
2
|
+
# A general purpose node containing a node type and a list of arguments.
|
3
|
+
# The node stores a link to its parent or nil if no such parent exists.
|
4
|
+
# This class can be visited.
|
5
|
+
#
|
6
|
+
class Node
|
7
|
+
attr_reader :type
|
8
|
+
attr_reader :parent
|
9
|
+
attr_reader :args
|
10
|
+
|
11
|
+
def initialize *args
|
12
|
+
if args.first.kind_of?(Symbol)
|
13
|
+
@type, *@args = args
|
14
|
+
else
|
15
|
+
@parent, @type, *@args = args
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Replaces previous projection list with this one.
|
20
|
+
#
|
21
|
+
# @param list [Array<Column>]
|
22
|
+
# @return [Node] node for chaining
|
23
|
+
#
|
24
|
+
def project *fields
|
25
|
+
node(:project, fields)
|
26
|
+
end
|
27
|
+
|
28
|
+
def from *tables
|
29
|
+
node(:from, *tables)
|
30
|
+
end
|
31
|
+
|
32
|
+
def where *exps
|
33
|
+
node(:where, JoinOp.new(:and, exps))
|
34
|
+
end
|
35
|
+
|
36
|
+
def set *assign_list
|
37
|
+
node(:set, assign_list)
|
38
|
+
end
|
39
|
+
|
40
|
+
def join *tables
|
41
|
+
node(:join, tables)
|
42
|
+
end
|
43
|
+
def on *exps
|
44
|
+
node(:on, exps)
|
45
|
+
end
|
46
|
+
|
47
|
+
def limit n
|
48
|
+
node(:limit, n)
|
49
|
+
end
|
50
|
+
def offset n
|
51
|
+
node(:offset, n)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Replaces previous ORDER BY specification with this one.
|
55
|
+
#
|
56
|
+
# @param list [Array<Column>]
|
57
|
+
# @return [Node] node for chaining
|
58
|
+
#
|
59
|
+
def order_by *list
|
60
|
+
node(:order_by, list)
|
61
|
+
end
|
62
|
+
|
63
|
+
# INSERT INTO ... (FIELDS) VALUES (...)
|
64
|
+
def fields *list
|
65
|
+
node(:fields, list)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Adds a values list to an INSERT statement. You have to pass all the columns
|
69
|
+
# at once; repeating this method call will create an INSERT statement that
|
70
|
+
# inserts multiple rows at once (a Postgres extension).
|
71
|
+
#
|
72
|
+
# Example:
|
73
|
+
# include Seaquel
|
74
|
+
# insert.into(table('foo')).values(immediate(1), immediate(2))
|
75
|
+
# # => INSERT INTO "foo" VALUES (1, 2)
|
76
|
+
#
|
77
|
+
# @param list [Array<AST::Expression>] values to insert
|
78
|
+
# @return [AST::Node] query you are building
|
79
|
+
#
|
80
|
+
def values *list
|
81
|
+
node(:values, list)
|
82
|
+
end
|
83
|
+
|
84
|
+
def into table
|
85
|
+
node(:into, table)
|
86
|
+
end
|
87
|
+
|
88
|
+
def node *args
|
89
|
+
self.class.new(self, *args)
|
90
|
+
end
|
91
|
+
|
92
|
+
def visit visitor
|
93
|
+
method_name = "visit_#{type}"
|
94
|
+
if visitor.respond_to?(:visit_node)
|
95
|
+
visitor.visit_node(self)
|
96
|
+
else
|
97
|
+
visitor.send(method_name, parent, *args)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_sql
|
102
|
+
::Seaquel::Generator.new(self).compact_sql
|
103
|
+
end
|
104
|
+
|
105
|
+
def inspect
|
106
|
+
[type, args, parent].inspect
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|