/
geoip--0.2.2.sql
185 lines (141 loc) · 5.84 KB
/
geoip--0.2.2.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
* Author: Tomas Vondra
* Created at: Sat Mar 31 22:51:21 +0200 2012
*
*/
CREATE TABLE geoip_country (
begin_ip INET NOT NULL,
end_ip INET NOT NULL,
country CHAR(2) NOT NULL,
name VARCHAR(100) NOT NULL,
CONSTRAINT valid_range CHECK (begin_ip <= end_ip)
);
CREATE TABLE geoip_city_location (
loc_id INTEGER PRIMARY KEY,
country CHAR(2) NOT NULL,
region CHAR(2),
city VARCHAR(100),
postal_code VARCHAR(10),
latitude DOUBLE PRECISION,
longitude DOUBLE PRECISION,
metro_code INT,
area_code INT
);
CREATE TABLE geoip_city_block (
begin_ip INET NOT NULL,
end_ip INET NOT NULL,
loc_id INTEGER NOT NULL REFERENCES geoip_city_location(loc_id)
);
CREATE TABLE geoip_asn (
begin_ip INET NOT NULL,
end_ip INET NOT NULL,
name TEXT NOT NULL
);
-- indexes (might be improved to handle index-only scans)
CREATE INDEX geoip_country_ip_idx ON geoip_country (begin_ip DESC);
CREATE INDEX geoip_city_block_ip_idx ON geoip_city_block (begin_ip DESC);
CREATE INDEX geoip_asn_ip_idx ON geoip_asn (begin_ip DESC);
/** functions used to search data by IP **/
-- search country, returns just the country code (2 characters)
CREATE OR REPLACE FUNCTION geoip_country_code(p_ip INET) RETURNS CHAR(2) AS $$
SELECT country
FROM geoip_country
WHERE $1 >= begin_ip AND $1 <= end_ip ORDER BY begin_ip DESC LIMIT 1;
$$ LANGUAGE sql;
-- search city, returns just the location ID (PK of the geoip_city_location)
CREATE OR REPLACE FUNCTION geoip_city_location(p_ip INET) RETURNS INT AS $$
SELECT loc_id
FROM geoip_city_block
WHERE $1 >= begin_ip AND $1 <= end_ip ORDER BY begin_ip DESC LIMIT 1;
$$ LANGUAGE sql;
-- search city, returns all the city details (zipcode, GPS etc.)
CREATE OR REPLACE FUNCTION geoip_city(p_ip INET, OUT loc_id INT, OUT country CHAR(2), OUT region CHAR(2),
OUT city VARCHAR(100), OUT postal_code VARCHAR(10),
OUT latitude DOUBLE PRECISION, OUT longitude DOUBLE PRECISION,
OUT metro_code INT, OUT area_code INT) AS $$
SELECT l.loc_id, country, region, city, postal_code, latitude, longitude, metro_code, area_code
FROM geoip_city_block b JOIN geoip_city_location l ON (b.loc_id = l.loc_id)
WHERE $1 >= begin_ip AND $1 <= end_ip ORDER BY begin_ip DESC LIMIT 1;
$$ LANGUAGE sql;
-- search country, returns all the details
CREATE OR REPLACE FUNCTION geoip_country(p_ip INET, OUT begin_ip INET, OUT end_ip INET,
OUT country CHAR(2), OUT name VARCHAR(100)) AS $$
SELECT begin_ip, end_ip, country, name
FROM geoip_country WHERE $1 >= begin_ip AND $1 <= end_ip ORDER BY begin_ip DESC LIMIT 1;
$$ LANGUAGE sql;
-- search ASN, returns the IP range and ASN name
CREATE OR REPLACE FUNCTION geoip_asn(p_ip INET, OUT begin_ip INET, OUT end_ip INET,
OUT name VARCHAR(100)) AS $$
SELECT begin_ip, end_ip, name
FROM geoip_asn WHERE $1 >= begin_ip AND $1 <= end_ip ORDER BY begin_ip DESC LIMIT 1;
$$ LANGUAGE sql;
/** functions used to search data by IP **/
-- check consistency of the country table
CREATE OR REPLACE FUNCTION geoip_country_check() RETURNS BOOLEAN AS $$
DECLARE
v_previous RECORD;
v_country RECORD;
v_first BOOLEAN := TRUE;
v_valid BOOLEAN := TRUE;
BEGIN
FOR v_country IN SELECT * FROM geoip_country ORDER BY begin_ip ASC LOOP
IF (NOT v_first) THEN
v_first := FALSE;
IF (v_previous.end_ip + 1 != v_country.begin_ip) THEN
RAISE WARNING 'there''s a hole between %-% and %-%',v_previous.begin_ip,
v_previous.end_ip,v_country.begin_ip,v_country.end_ip;
v_valid := FALSE;
END IF;
END IF;
v_previous := v_country;
END LOOP;
RETURN v_valid;
END;
$$ LANGUAGE plpgsql;
-- check consistency of the city table
CREATE OR REPLACE FUNCTION geoip_city_check() RETURNS BOOLEAN AS $$
DECLARE
v_previous RECORD;
v_block RECORD;
v_first BOOLEAN := TRUE;
v_valid BOOLEAN := TRUE;
BEGIN
FOR v_block IN SELECT begin_ip, end_ip FROM geoip_city_block ORDER BY begin_ip ASC LOOP
IF (NOT v_first) THEN
v_first := FALSE;
IF (v_previous.end_ip + 1 != v_block.begin_ip) THEN
RAISE WARNING 'there''s a hole between %-% and %-%',v_previous.begin_ip,
v_previous.end_ip,v_block.begin_ip,v_block.end_ip;
v_valid := FALSE;
END IF;
END IF;
v_previous := v_block;
END LOOP;
RETURN v_valid;
END;
$$ LANGUAGE plpgsql;
-- check consistency of the ASN table
CREATE OR REPLACE FUNCTION geoip_asn_check() RETURNS BOOLEAN AS $$
DECLARE
v_previous RECORD;
v_block RECORD;
v_first BOOLEAN := TRUE;
v_valid BOOLEAN := TRUE;
BEGIN
FOR v_block IN SELECT begin_ip, end_ip FROM geoip_asn ORDER BY begin_ip ASC LOOP
IF (NOT v_first) THEN
v_first := FALSE;
IF (v_previous.end_ip + 1 != v_block.begin_ip) THEN
RAISE WARNING 'there''s a hole between %-% and %-%',v_previous.begin_ip,
v_previous.end_ip,v_block.begin_ip,v_block.end_ip;
v_valid := FALSE;
END IF;
END IF;
v_previous := v_block;
END LOOP;
RETURN v_valid;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION geoip_bigint_to_inet(p_ip BIGINT) RETURNS inet AS $$
SELECT (($1 >> 24 & 255) || '.' || ($1 >> 16 & 255) || '.' || ($1 >> 8 & 255) || '.' || ($1 & 255))::inet
$$ LANGUAGE sql strict immutable;