gda 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.autotest +12 -0
- data/.gemtest +0 -0
- data/CHANGELOG.rdoc +6 -0
- data/Manifest.txt +27 -0
- data/README.rdoc +61 -0
- data/Rakefile +34 -0
- data/ext/gda/extconf.rb +19 -0
- data/ext/gda/gda.c +99 -0
- data/ext/gda/gda.h +18 -0
- data/ext/gda/gda_nodes.c +516 -0
- data/ext/gda/gda_nodes.h +9 -0
- data/ext/gda/gda_provider.c +61 -0
- data/ext/gda/gda_provider.h +7 -0
- data/ext/gda/gda_statement.c +58 -0
- data/ext/gda/gda_statement.h +6 -0
- data/lib/gda.rb +39 -0
- data/lib/gda/visitors/dot.rb +188 -0
- data/lib/gda/visitors/each.rb +18 -0
- data/lib/gda/visitors/max_depth.rb +29 -0
- data/lib/gda/visitors/visitor.rb +129 -0
- data/test/helper.rb +17 -0
- data/test/sqllog.sqlite3 +0 -0
- data/test/test_dot_visitor.rb +43 -0
- data/test/test_gda.rb +55 -0
- data/test/test_max_depth.rb +19 -0
- data/test/test_node_attributes.rb +159 -0
- data/test/test_nodes.rb +26 -0
- data/test/test_statement.rb +79 -0
- metadata +155 -0
data/ext/gda/gda_nodes.h
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#include <gda.h>
|
2
|
+
|
3
|
+
VALUE cProvider;
|
4
|
+
|
5
|
+
static VALUE name(VALUE self)
|
6
|
+
{
|
7
|
+
GdaServerProvider * pr;
|
8
|
+
Data_Get_Struct(self, GdaServerProvider, pr);
|
9
|
+
|
10
|
+
return rb_tainted_str_new2(gda_server_provider_get_name(pr));
|
11
|
+
}
|
12
|
+
|
13
|
+
static VALUE find(VALUE klass, VALUE string)
|
14
|
+
{
|
15
|
+
GdaServerProvider * pr;
|
16
|
+
GError * error = NULL;
|
17
|
+
|
18
|
+
pr = gda_config_get_provider(StringValuePtr(string), &error);
|
19
|
+
|
20
|
+
if (pr)
|
21
|
+
return Data_Wrap_Struct(klass, NULL, NULL, pr);
|
22
|
+
else {
|
23
|
+
/* FIXME: should actually raise an error here. */
|
24
|
+
g_error_free(error);
|
25
|
+
return Qnil;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
static VALUE parser(VALUE self)
|
30
|
+
{
|
31
|
+
GdaSqlParser * parser;
|
32
|
+
GdaServerProvider * pr;
|
33
|
+
|
34
|
+
Data_Get_Struct(self, GdaServerProvider, pr);
|
35
|
+
|
36
|
+
parser = gda_server_provider_create_parser(pr, NULL);
|
37
|
+
|
38
|
+
if (!parser)
|
39
|
+
rb_raise(rb_eRuntimeError, "zomglol");
|
40
|
+
|
41
|
+
return Data_Wrap_Struct(cParser, NULL, g_object_unref, parser);
|
42
|
+
}
|
43
|
+
|
44
|
+
static VALUE quote_str(VALUE self, VALUE str)
|
45
|
+
{
|
46
|
+
GdaServerProvider * pr;
|
47
|
+
|
48
|
+
Data_Get_Struct(self, GdaServerProvider, pr);
|
49
|
+
return rb_str_new2(gda_sql_identifier_quote(StringValuePtr(str), NULL, pr, TRUE, TRUE));
|
50
|
+
}
|
51
|
+
|
52
|
+
void Init_gda_provider()
|
53
|
+
{
|
54
|
+
cProvider = rb_define_class_under(mSQL, "Provider", rb_cObject);
|
55
|
+
rb_define_singleton_method(cProvider, "find", find, 1);
|
56
|
+
rb_define_method(cProvider, "name", name, 0);
|
57
|
+
rb_define_method(cProvider, "parser", parser, 0);
|
58
|
+
rb_define_method(cProvider, "quote", quote_str, 1);
|
59
|
+
}
|
60
|
+
|
61
|
+
/* vim: set noet sws=4 sw=4: */
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#include <gda.h>
|
2
|
+
|
3
|
+
VALUE cStatement;
|
4
|
+
VALUE cStructure;
|
5
|
+
|
6
|
+
static VALUE serialize(VALUE self)
|
7
|
+
{
|
8
|
+
GdaStatement * stmt;
|
9
|
+
gchar * string;
|
10
|
+
|
11
|
+
Data_Get_Struct(self, GdaStatement, stmt);
|
12
|
+
|
13
|
+
string = gda_statement_serialize(stmt);
|
14
|
+
return rb_str_new2(string);
|
15
|
+
}
|
16
|
+
|
17
|
+
static VALUE ast(VALUE self)
|
18
|
+
{
|
19
|
+
GdaSqlStatement * sqlst;
|
20
|
+
|
21
|
+
Data_Get_Struct(self, GdaSqlStatement, sqlst);
|
22
|
+
|
23
|
+
return WrapAnyPart(self, GDA_SQL_ANY_PART(sqlst->contents));
|
24
|
+
}
|
25
|
+
|
26
|
+
static VALUE sql(VALUE self)
|
27
|
+
{
|
28
|
+
GdaSqlStatement * sqlst;
|
29
|
+
|
30
|
+
Data_Get_Struct(self, GdaSqlStatement, sqlst);
|
31
|
+
|
32
|
+
return rb_str_new2(sqlst->sql);
|
33
|
+
}
|
34
|
+
|
35
|
+
static VALUE structure(VALUE self)
|
36
|
+
{
|
37
|
+
GdaStatement * stmt;
|
38
|
+
GdaSqlStatement * sqlst;
|
39
|
+
|
40
|
+
Data_Get_Struct(self, GdaStatement, stmt);
|
41
|
+
|
42
|
+
g_object_get(G_OBJECT(stmt), "structure", &sqlst, NULL);
|
43
|
+
|
44
|
+
return Data_Wrap_Struct(cStructure, NULL, gda_sql_statement_free, sqlst);
|
45
|
+
}
|
46
|
+
|
47
|
+
void Init_gda_statement()
|
48
|
+
{
|
49
|
+
cStatement = rb_define_class_under(mSQL, "Statement", rb_cObject);
|
50
|
+
cStructure = rb_define_class_under(mSQL, "Structure", rb_cObject);
|
51
|
+
|
52
|
+
rb_define_method(cStatement, "serialize", serialize, 0);
|
53
|
+
rb_define_method(cStatement, "structure", structure, 0);
|
54
|
+
rb_define_method(cStructure, "ast", ast, 0);
|
55
|
+
rb_define_method(cStructure, "sql", sql, 0);
|
56
|
+
}
|
57
|
+
|
58
|
+
/* vim: set noet sws=4 sw=4: */
|
data/lib/gda.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'gda.so'
|
2
|
+
require 'gda/visitors/each'
|
3
|
+
require 'gda/visitors/dot'
|
4
|
+
require 'gda/visitors/max_depth'
|
5
|
+
|
6
|
+
module GDA
|
7
|
+
VERSION = '1.0.0'
|
8
|
+
|
9
|
+
module SQL
|
10
|
+
class Statement
|
11
|
+
def ast
|
12
|
+
structure.ast
|
13
|
+
end
|
14
|
+
|
15
|
+
def sql
|
16
|
+
structure.sql
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Nodes
|
22
|
+
class Node
|
23
|
+
include Enumerable
|
24
|
+
|
25
|
+
def each &block
|
26
|
+
Visitors::Each.new(block).accept self
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_dot
|
30
|
+
viz = Visitors::Dot.new
|
31
|
+
viz.accept self
|
32
|
+
end
|
33
|
+
|
34
|
+
def max_depth
|
35
|
+
Visitors::MaxDepth.new.accept(self)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'gda/visitors/visitor'
|
2
|
+
require 'erb'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module GDA
|
6
|
+
module Visitors
|
7
|
+
class Dot < GDA::Visitors::Visitor
|
8
|
+
attr_reader :stack
|
9
|
+
|
10
|
+
def initialize io = StringIO.new
|
11
|
+
@stack = []
|
12
|
+
@buffer = io
|
13
|
+
end
|
14
|
+
|
15
|
+
HEADER = "digraph G { graph [rankdir = \"TB\"];"
|
16
|
+
FOOTER = "}"
|
17
|
+
|
18
|
+
def accept node
|
19
|
+
puts HEADER
|
20
|
+
super
|
21
|
+
puts FOOTER
|
22
|
+
@buffer.string
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def puts str
|
28
|
+
@buffer.puts str
|
29
|
+
end
|
30
|
+
|
31
|
+
def printf *args
|
32
|
+
@buffer.printf(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
NODE = ERB.new <<-eoerb
|
36
|
+
node<%= node.object_id %> [shape="plaintext" label=<
|
37
|
+
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
|
38
|
+
<TR><TD COLSPAN="2"><%= node.class %></TD></TR>
|
39
|
+
<% attrs.each do |attr| %>
|
40
|
+
<% next if node.send(attr).nil? %>
|
41
|
+
<TR><TD><%= ERB::Util.h attr %></TD><TD><%= ERB::Util.h node.send(attr) %></TD></TR>
|
42
|
+
<% end %>
|
43
|
+
</TABLE>>];
|
44
|
+
eoerb
|
45
|
+
|
46
|
+
LIST = ERB.new <<-eoerb
|
47
|
+
node<%= node.object_id %> [shape="invhouse", color=gray, fontcolor=gray, label=list];
|
48
|
+
eoerb
|
49
|
+
|
50
|
+
def add_node node, attrs = []
|
51
|
+
puts NODE.result binding
|
52
|
+
link_node node
|
53
|
+
end
|
54
|
+
|
55
|
+
FMT = "node%d -> node%d [ label = \"%s\" ];\n"
|
56
|
+
|
57
|
+
def link_node node
|
58
|
+
return if stack.empty?
|
59
|
+
printf FMT, stack.last.first.object_id, node.object_id, stack.last.last
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_list node
|
63
|
+
puts LIST.result binding
|
64
|
+
link_node node
|
65
|
+
end
|
66
|
+
|
67
|
+
def visit_edge node, edge
|
68
|
+
stack.push [node, edge]
|
69
|
+
visit node.send edge
|
70
|
+
stack.pop
|
71
|
+
end
|
72
|
+
|
73
|
+
def visit_Array node
|
74
|
+
return if node.empty?
|
75
|
+
|
76
|
+
add_list node
|
77
|
+
node.each_with_index { |n, i|
|
78
|
+
stack.push [node, i]
|
79
|
+
visit n
|
80
|
+
stack.pop
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def visit_GDA_Nodes_Insert node
|
85
|
+
add_node node, [:on_conflict]
|
86
|
+
visit_edge node, :table
|
87
|
+
visit_edge node, :fields_list
|
88
|
+
visit_edge node, :values_list
|
89
|
+
end
|
90
|
+
|
91
|
+
def visit_GDA_Nodes_Update node
|
92
|
+
add_node node, [:on_conflict]
|
93
|
+
visit_edge node, :table
|
94
|
+
visit_edge node, :fields_list
|
95
|
+
visit_edge node, :expr_list
|
96
|
+
visit_edge node, :cond
|
97
|
+
end
|
98
|
+
|
99
|
+
def visit_GDA_Nodes_Delete node
|
100
|
+
add_node node
|
101
|
+
visit_edge node, :table
|
102
|
+
visit_edge node, :cond
|
103
|
+
end
|
104
|
+
|
105
|
+
def visit_GDA_Nodes_Select node
|
106
|
+
add_node node
|
107
|
+
visit_edge node, :distinct_expr
|
108
|
+
visit_edge node, :expr_list
|
109
|
+
visit_edge node, :from
|
110
|
+
visit_edge node, :where_cond
|
111
|
+
visit_edge node, :group_by
|
112
|
+
visit_edge node, :having_cond
|
113
|
+
visit_edge node, :order_by
|
114
|
+
visit_edge node, :limit_count
|
115
|
+
visit_edge node, :limit_offset
|
116
|
+
end
|
117
|
+
|
118
|
+
def visit_GDA_Nodes_Table node
|
119
|
+
add_node node, [:table_name]
|
120
|
+
end
|
121
|
+
|
122
|
+
def visit_GDA_Nodes_SelectField node
|
123
|
+
add_node node, [:field_name, :table_name, :as]
|
124
|
+
visit_edge node, :expr
|
125
|
+
end
|
126
|
+
|
127
|
+
def visit_GDA_Nodes_Expr node
|
128
|
+
add_node node, [:value, :cast_as]
|
129
|
+
visit_edge node, :func
|
130
|
+
visit_edge node, :cond
|
131
|
+
visit_edge node, :select
|
132
|
+
visit_edge node, :case_s
|
133
|
+
visit_edge node, :param_spec
|
134
|
+
end
|
135
|
+
|
136
|
+
def visit_GDA_Nodes_Field node
|
137
|
+
add_node node, [:field_name]
|
138
|
+
end
|
139
|
+
|
140
|
+
def visit_GDA_Nodes_Operation node
|
141
|
+
add_node node, [:operator]
|
142
|
+
visit_edge node, :operands
|
143
|
+
end
|
144
|
+
|
145
|
+
def visit_GDA_Nodes_From node
|
146
|
+
add_node node
|
147
|
+
visit_edge node, :targets
|
148
|
+
visit_edge node, :joins
|
149
|
+
end
|
150
|
+
|
151
|
+
def visit_GDA_Nodes_Target node
|
152
|
+
add_node node, [:table_name, :as]
|
153
|
+
visit_edge node, :expr
|
154
|
+
end
|
155
|
+
|
156
|
+
def visit_GDA_Nodes_Function node
|
157
|
+
add_node node, [:function_name]
|
158
|
+
visit_edge node, :args_list
|
159
|
+
end
|
160
|
+
|
161
|
+
def visit_GDA_Nodes_Order node
|
162
|
+
add_node node, [:asc, :collation_name]
|
163
|
+
visit_edge node, :expr
|
164
|
+
end
|
165
|
+
|
166
|
+
def visit_GDA_Nodes_Unknown node
|
167
|
+
add_node node
|
168
|
+
visit_edge node, :expressions
|
169
|
+
end
|
170
|
+
|
171
|
+
def visit_GDA_Nodes_Join node
|
172
|
+
add_node node, [:join_type, :position]
|
173
|
+
visit_edge node, :expr
|
174
|
+
visit_edge node, :use
|
175
|
+
end
|
176
|
+
|
177
|
+
def visit_GDA_Nodes_Savepoint node
|
178
|
+
add_node node, [:__type__, :isolation_level, :trans_mode, :trans_name]
|
179
|
+
end
|
180
|
+
|
181
|
+
alias visit_GDA_Nodes_RollbackSavepoint visit_GDA_Nodes_Savepoint
|
182
|
+
alias visit_GDA_Nodes_DeleteSavepoint visit_GDA_Nodes_Savepoint
|
183
|
+
alias visit_GDA_Nodes_Rollback visit_GDA_Nodes_Savepoint
|
184
|
+
alias visit_GDA_Nodes_Commit visit_GDA_Nodes_Savepoint
|
185
|
+
alias visit_GDA_Nodes_Begin visit_GDA_Nodes_Savepoint
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'gda/visitors/visitor'
|
2
|
+
|
3
|
+
module GDA
|
4
|
+
module Visitors
|
5
|
+
class Each < Visitor
|
6
|
+
def initialize block
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def visit node
|
11
|
+
super
|
12
|
+
unless node.nil? || Array === node
|
13
|
+
@block.call node
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'gda/visitors/visitor'
|
2
|
+
|
3
|
+
module GDA
|
4
|
+
module Visitors
|
5
|
+
class MaxDepth < Visitor
|
6
|
+
def initialize
|
7
|
+
@max = 0
|
8
|
+
@current = 0
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def accept node
|
13
|
+
super
|
14
|
+
@max
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def visit node
|
20
|
+
return super if node.nil?
|
21
|
+
|
22
|
+
@current += 1
|
23
|
+
super
|
24
|
+
@max = [@max, @current].max
|
25
|
+
@current -= 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module GDA
|
2
|
+
module Visitors
|
3
|
+
class Visitor
|
4
|
+
def accept node
|
5
|
+
visit node
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def visit node
|
11
|
+
return unless node
|
12
|
+
|
13
|
+
method = METHOD_CACHE.fetch(node.class) { |k|
|
14
|
+
"visit_" + k.name.split('::').join('_')
|
15
|
+
}
|
16
|
+
|
17
|
+
send method, node
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_Array node
|
21
|
+
node.each { |n| visit n }
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit_GDA_Nodes_Select node
|
25
|
+
visit node.distinct_expr
|
26
|
+
visit node.expr_list
|
27
|
+
visit node.from
|
28
|
+
visit node.where_cond
|
29
|
+
visit node.group_by
|
30
|
+
visit node.having_cond
|
31
|
+
visit node.order_by
|
32
|
+
visit node.limit_count
|
33
|
+
visit node.limit_offset
|
34
|
+
end
|
35
|
+
|
36
|
+
def visit_GDA_Nodes_Insert node
|
37
|
+
visit node.table
|
38
|
+
visit node.fields_list
|
39
|
+
visit node.values_list
|
40
|
+
visit node.select
|
41
|
+
end
|
42
|
+
|
43
|
+
def visit_GDA_Nodes_Update node
|
44
|
+
visit node.table
|
45
|
+
visit node.fields_list
|
46
|
+
visit node.expr_list
|
47
|
+
visit node.cond
|
48
|
+
end
|
49
|
+
|
50
|
+
def visit_GDA_Nodes_Join node
|
51
|
+
visit node.expr
|
52
|
+
visit node.use
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_GDA_Nodes_Delete node
|
56
|
+
visit node.table
|
57
|
+
visit node.cond
|
58
|
+
end
|
59
|
+
|
60
|
+
def visit_GDA_Nodes_SelectField node
|
61
|
+
visit node.expr
|
62
|
+
end
|
63
|
+
|
64
|
+
def visit_GDA_Nodes_Expr node
|
65
|
+
visit node.func
|
66
|
+
visit node.cond
|
67
|
+
visit node.select
|
68
|
+
visit node.case_s
|
69
|
+
visit node.param_spec
|
70
|
+
end
|
71
|
+
|
72
|
+
def visit_GDA_Nodes_From node
|
73
|
+
visit node.targets
|
74
|
+
visit node.joins
|
75
|
+
end
|
76
|
+
|
77
|
+
def visit_GDA_Nodes_Target node
|
78
|
+
visit node.expr
|
79
|
+
end
|
80
|
+
|
81
|
+
def visit_GDA_Nodes_Operation node
|
82
|
+
visit node.operands
|
83
|
+
end
|
84
|
+
|
85
|
+
def visit_GDA_Nodes_Function node
|
86
|
+
visit node.args_list
|
87
|
+
end
|
88
|
+
|
89
|
+
def visit_GDA_Nodes_Order node
|
90
|
+
visit node.expr
|
91
|
+
end
|
92
|
+
|
93
|
+
def visit_GDA_Nodes_Unknown node
|
94
|
+
visit node.expressions
|
95
|
+
end
|
96
|
+
|
97
|
+
## Terminal nodes
|
98
|
+
def visit_GDA_Nodes_Table node
|
99
|
+
end
|
100
|
+
|
101
|
+
def visit_GDA_Nodes_Field node
|
102
|
+
end
|
103
|
+
|
104
|
+
def visit_GDA_Nodes_Savepoint node
|
105
|
+
end
|
106
|
+
|
107
|
+
def visit_GDA_Nodes_RollbackSavepoint node
|
108
|
+
end
|
109
|
+
|
110
|
+
def visit_GDA_Nodes_Begin node
|
111
|
+
end
|
112
|
+
|
113
|
+
def visit_GDA_Nodes_DeleteSavepoint node
|
114
|
+
end
|
115
|
+
|
116
|
+
def visit_GDA_Nodes_Rollback node
|
117
|
+
end
|
118
|
+
|
119
|
+
def visit_GDA_Nodes_Commit node
|
120
|
+
end
|
121
|
+
|
122
|
+
METHOD_CACHE = {}
|
123
|
+
private_instance_methods.grep(/^visit_(.*)$/) do |method|
|
124
|
+
k = $1.split('_').inject(Object) { |klass,c| klass.const_get c }
|
125
|
+
METHOD_CACHE[k] = method
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|