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.
@@ -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
@@ -0,0 +1,5 @@
1
+
2
+ # SELECT
3
+
4
+ SELECT * FROM table WHERE 1=2
5
+ SELECT 1, 2, 3
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
+
@@ -0,0 +1,13 @@
1
+
2
+ module Seaquel
3
+ end
4
+
5
+ require 'seaquel/quoter'
6
+ require 'seaquel/ast'
7
+
8
+ require 'seaquel/statement'
9
+ require 'seaquel/statement_gatherer'
10
+ require 'seaquel/generator'
11
+
12
+ require 'seaquel/module'
13
+
@@ -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,15 @@
1
+
2
+ module Seaquel::AST
3
+ class Alias
4
+ attr_reader :name, :expression
5
+
6
+ def initialize name, expression
7
+ @name = name
8
+ @expression = expression
9
+ end
10
+
11
+ def visit visitor
12
+ visitor.visit_alias(name, expression)
13
+ end
14
+ end
15
+ end
@@ -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,11 @@
1
+
2
+ module Seaquel::AST
3
+ # Special case of a column list that needs to be formatted without table
4
+ # references.
5
+ #
6
+ class ColumnList < List
7
+ def visit visitor
8
+ visitor.visit_column_list(list)
9
+ end
10
+ end
11
+ 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,16 @@
1
+
2
+ require_relative 'expression'
3
+
4
+ module Seaquel::AST
5
+ class Immediate < Expression
6
+ attr_reader :val
7
+
8
+ def initialize ruby_val
9
+ @val = ruby_val
10
+ end
11
+
12
+ def visit visitor
13
+ visitor.visit_immediate val.to_s
14
+ end
15
+ end
16
+ 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,16 @@
1
+
2
+ require_relative 'expression'
3
+
4
+ module Seaquel::AST
5
+ class Literal < Expression
6
+ attr_reader :text
7
+
8
+ def initialize text
9
+ @text = text
10
+ end
11
+
12
+ def visit visitor
13
+ visitor.visit_literal(text)
14
+ end
15
+ end
16
+ 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