gda 1.0.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/.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
|