prestogres 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +11 -0
- data/README.md +100 -39
- data/VERSION +1 -1
- data/bin/prestogres +5 -5
- data/config/pgpool.conf +1 -1
- data/ext/depend +11 -12
- data/pgpool2/child.c +1 -1
- data/pgpool2/config.h.in +3 -0
- data/pgpool2/configure +39 -0
- data/pgpool2/configure.in +1 -0
- data/pgpool2/pool_query_context.c +101 -19
- data/pgsql/prestogres.py +50 -28
- data/pgsql/setup.sql +18 -0
- metadata +3 -8
data/ChangeLog
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
|
2
|
+
2014-03-05 version 0.4.0:
|
3
|
+
|
4
|
+
* Preserve OID of tables when clients SELECT from system catalogs
|
5
|
+
* Increased maximum rewritten query length to 32768 bytes
|
6
|
+
* Fixed protocol version v2 handling
|
7
|
+
* Fixed 'too long error message' errors
|
8
|
+
* Fixed build scripts
|
9
|
+
* Add "LIMIT 1000" to "SELECT * FROM table" queries automatically
|
10
|
+
(ad-hoc fix for Tableau)
|
11
|
+
|
12
|
+
|
2
13
|
2014-02-07 version 0.3.0:
|
3
14
|
|
4
15
|
* Fixed system catalog queries to support multiple schemas
|
data/README.md
CHANGED
@@ -14,54 +14,92 @@ You can use any PostgreSQL clients (see also *Limitation* section):
|
|
14
14
|
|
15
15
|
Prestogres also offers password-based authentication and SSL.
|
16
16
|
|
17
|
+
## Documents
|
18
|
+
|
19
|
+
* [How it works?](#how-it-works)
|
20
|
+
* [Limitation](#limitation)
|
21
|
+
* [Installation](#installation)
|
22
|
+
* [Running servers](#running-servers)
|
23
|
+
* [Mac OS X](#mac-os-x)
|
24
|
+
* [Configuration](#configuration)
|
25
|
+
* [pgpool.conf file](#pgpoolconf-file)
|
26
|
+
* [pool_hba.conf file](#pool_hbaconf-file)
|
27
|
+
* [prestogres_md5 method](#prestogres_md5-method)
|
28
|
+
* [prestogres_external method](#prestogres_external-method)
|
29
|
+
* [prestogres command](#prestogres-command)
|
30
|
+
* [Development](#development)
|
31
|
+
|
32
|
+
---
|
33
|
+
|
17
34
|
## How it works?
|
18
35
|
|
19
36
|
Prestogres uses modified **[pgpool-II](http://www.pgpool.net/)** to rewrite queries before sending them to PostgreSQL.
|
20
|
-
pgpool-II is originally an open-source middleware to provide connection pool and other features to PostgreSQL.
|
37
|
+
pgpool-II is originally an open-source middleware to provide connection pool and other features to PostgreSQL. For regular SQL query, patched pgpool-II wraps it in **run_presto_astemp_table(..., 'SELECT ... FROM ...')** function. This function sends the query to Presto:
|
21
38
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
+-----------+ +------------+ +--------+
|
28
|
-
\ \
|
29
|
-
rewrite queries run custom functions
|
30
|
-
| |
|
31
|
-
+-----------------------------------+
|
32
|
-
Prestogres
|
33
|
-
```
|
39
|
+
![Regular SQL](https://gist.github.com/frsyuki/9012980/raw/2782f51cd3c708254ac64c4c30b7bff0e7894640/figure1.png)
|
40
|
+
|
41
|
+
If the query selects records from system catalog (e.g. `\\d` command by psql), patched pgpool-II wraps the query in `run_system_catalog_as_temp_table` function. It gets table list from Presto, and actually runs **CREATE TABLE** to create records in system catalog on PostgreSQL. Then the original query runs as usual on PostgreSQL:
|
42
|
+
|
43
|
+
![System catalog access](https://gist.github.com/frsyuki/9012980/raw/f869bd3dd59ab22f439165972c29d122ea560694/figure2.png)
|
34
44
|
|
35
|
-
|
36
|
-
|
37
|
-
|
45
|
+
In fact there're some other tricks. See [pgsql/prestogres.py](pgsql/prestogres.py) for the real behavior.
|
46
|
+
|
47
|
+
---
|
38
48
|
|
39
49
|
## Limitation
|
40
50
|
|
41
51
|
* Extended query is not supported ([PostgreSQL Frontend/Backend Protocol](http://www.postgresql.org/docs/9.3/static/protocol.html))
|
42
|
-
* ODBC driver needs to set
|
43
|
-
|
44
|
-
|
45
|
-
*
|
52
|
+
* ODBC driver needs to set:
|
53
|
+
* **UseServerSidePrepare=0** (Server side prepare: no) property
|
54
|
+
* **UseDeclareFetch=0** (Use Declare/Fetch: no) property
|
55
|
+
* **Unicode** mode
|
56
|
+
* JDBC driver needs to set:
|
57
|
+
* **protocolVersion=2** property
|
58
|
+
* Cursor (DECLARE/FETCH) is not supported
|
59
|
+
|
60
|
+
---
|
46
61
|
|
47
62
|
## Installation
|
48
63
|
|
64
|
+
### 1. Install PostgreSQL >= 9.3
|
65
|
+
|
66
|
+
You need to install PostgreSQL separately. Following commands install PostgreSQL 9.3 from postgresql.org:
|
67
|
+
|
68
|
+
**Ubuntu/Debian:**
|
69
|
+
|
49
70
|
```
|
50
|
-
|
71
|
+
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
72
|
+
sudo apt-get update
|
73
|
+
sudo apt-get install postgresql-9.3 postgresql-server-dev-9.3 postgresql-plpython-9.3
|
74
|
+
sudo apt-get install gcc make libssl-dev libpcre3-dev
|
75
|
+
sudo apt-get install ruby ruby-dev
|
51
76
|
```
|
52
77
|
|
53
|
-
|
78
|
+
**RedHat/CentOS:**
|
54
79
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
80
|
+
```
|
81
|
+
sudo yum install http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-redhat93-9.3-1.noarch.rpm
|
82
|
+
sudo yum install postgresql93-server postgresql93-contrib postgresql93-devel
|
83
|
+
sudo yum install gcc make openssl-devel pcre-devel
|
84
|
+
sudo yum install ruby ruby-devel
|
85
|
+
```
|
86
|
+
|
87
|
+
**Mac OS X:**
|
60
88
|
|
61
|
-
|
89
|
+
```
|
90
|
+
ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
|
91
|
+
brew install postgresql
|
92
|
+
```
|
93
|
+
|
94
|
+
### 2. Install Prestogres
|
95
|
+
|
96
|
+
```
|
97
|
+
$ sudo gem install prestogres --no-ri --no-rdoc
|
98
|
+
```
|
62
99
|
|
100
|
+
---
|
63
101
|
|
64
|
-
##
|
102
|
+
## Running servers
|
65
103
|
|
66
104
|
You need to run 2 server programs: pgpool-II and PostgreSQL.
|
67
105
|
You can use `prestogres` command to setup & run them as following:
|
@@ -86,26 +124,42 @@ $ psql -h localhost -p 9900 -U pg postgres
|
|
86
124
|
|
87
125
|
If configuration is correct, you can run `SELECT * FROM sys.node;` query. Otherwise, see log files in **./pgdata/log/** directory.
|
88
126
|
|
89
|
-
####
|
127
|
+
#### Setting shmem max parameter
|
90
128
|
|
91
|
-
|
129
|
+
Probably above command fails first time! Error message is:
|
92
130
|
|
93
131
|
```
|
94
|
-
|
95
|
-
|
132
|
+
FATAL: could not create shared memory segment: Cannot allocate memory
|
133
|
+
DETAIL: Failed system call was shmget(key=6432001, size=3809280, 03600).
|
134
|
+
HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded
|
135
|
+
available memory or swap space, or exceeded your kernel's SHMALL parameter. You can either
|
136
|
+
reduce the request size or reconfigure the kernel with larger SHMALL. To reduce the request
|
137
|
+
size (currently 3809280 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing
|
138
|
+
shared_buffers or max_connections.
|
96
139
|
```
|
97
140
|
|
98
|
-
|
141
|
+
You need to set 2 kernel parameters to run PostgreSQL.
|
142
|
+
|
143
|
+
**Linux:**
|
99
144
|
|
100
145
|
```
|
101
|
-
|
102
|
-
|
103
|
-
|
146
|
+
sudo sudo -c "echo kernel.shmmax = 17179869184 >> /etc/sysctl.conf"
|
147
|
+
sudo sudo -c "echo kernel.shmall = 4194304 >> /etc/sysctl.conf"
|
148
|
+
sudo sysctl -p /etc/sysctl.conf
|
104
149
|
```
|
105
150
|
|
151
|
+
**Mac OS X:**
|
152
|
+
|
153
|
+
```
|
154
|
+
$ sudo sysctl -w kern.sysv.shmmax=1073741824
|
155
|
+
$ sudo sysctl -w kern.sysv.shmall=1073741824
|
156
|
+
```
|
157
|
+
|
158
|
+
---
|
159
|
+
|
106
160
|
## Configuration
|
107
161
|
|
108
|
-
###
|
162
|
+
### pgpool.conf file
|
109
163
|
|
110
164
|
Please read [pgpool-II documentation](http://www.pgpool.net/docs/latest/pgpool-en.html) for most of parameters.
|
111
165
|
Following parameters are unique to Prestogres:
|
@@ -184,7 +238,6 @@ See *pgool.conf file* section for available parameters.
|
|
184
238
|
|
185
239
|
If you want to reject this connection, the program exists with non-0 status code.
|
186
240
|
|
187
|
-
|
188
241
|
### Creating database on PostgreSQL
|
189
242
|
|
190
243
|
Prestogres setups a database named *postgres* on PostgreSQL by default. But you may want to create other databases
|
@@ -221,6 +274,8 @@ commands:
|
|
221
274
|
show_init_sql display statements to initialize a new PostgreSQL database
|
222
275
|
```
|
223
276
|
|
277
|
+
---
|
278
|
+
|
224
279
|
## Development
|
225
280
|
|
226
281
|
To install git HEAD, use following commands to build:
|
@@ -242,3 +297,9 @@ $ bundle exec rake
|
|
242
297
|
$ gem install --no-ri --no-rdoc pkg/prestogres-*.gem
|
243
298
|
# if this command failed, you may need to install toolchain (gcc, etc.) to build pgpool-II
|
244
299
|
```
|
300
|
+
|
301
|
+
___
|
302
|
+
|
303
|
+
Prestogres is licensed under Apache License, Version 2.0.
|
304
|
+
Copyright (C) 2014 Sadayuki Furuhashi
|
305
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/bin/prestogres
CHANGED
@@ -68,11 +68,11 @@ usage nil unless config[:command]
|
|
68
68
|
args = ARGV[arg_offset..-1]
|
69
69
|
|
70
70
|
setup_params = {
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
71
|
+
port: 9900,
|
72
|
+
backend_port: 6432,
|
73
|
+
pgpool_pid_file: nil,
|
74
|
+
pgpool_status_dir: nil,
|
75
|
+
unix_socket_directory: nil,
|
76
76
|
}
|
77
77
|
|
78
78
|
ENV['PATH'] = "#{ENV['PATH']}:#{config[:bin_path]}"
|
data/config/pgpool.conf
CHANGED
@@ -269,7 +269,7 @@ white_function_list = ''
|
|
269
269
|
# Comma separated list of function names
|
270
270
|
# that don't write to database
|
271
271
|
# Regexp are accepted
|
272
|
-
black_function_list = 'currval,lastval,nextval,setval,current_setting'
|
272
|
+
black_function_list = 'currval,lastval,nextval,setval,current_setting,version,pg_client_encoding'
|
273
273
|
# Comma separated list of function names
|
274
274
|
# that write to database
|
275
275
|
# Regexp are accepted
|
data/ext/depend
CHANGED
@@ -6,29 +6,28 @@ narchdir = $(if $(rubyarchdir),$(rubyarchdir),$(RUBYARCHDIR))
|
|
6
6
|
# hack for RubyGems >= 1.8.26
|
7
7
|
final_archdir = $(if $(findstring .gem.,$(narchdir)),$(dir $(narchdir)),$(narchdir))
|
8
8
|
|
9
|
-
|
9
|
+
NATIVE_PREFIX = $(abspath $(final_archdir)/prestogres)
|
10
10
|
|
11
|
-
|
11
|
+
CFLAGS += -DPRESTOGRES_PREFIX="\"$(NATIVE_PREFIX)\""
|
12
12
|
|
13
|
-
|
13
|
+
all: native
|
14
|
+
clean: clean-native
|
15
|
+
install: install-native
|
14
16
|
|
15
|
-
|
17
|
+
PGPOOL2_PATH = $(abspath $(srcdir)/../pgpool2)
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
install: install-pgpool2
|
20
|
-
|
21
|
-
pgpool2:
|
19
|
+
native:
|
22
20
|
mkdir -p build
|
23
21
|
cd build && "$(PGPOOL2_PATH)/configure" \
|
24
22
|
CFLAGS="-I$(PGPOOL2_PATH)" \
|
25
23
|
--with-openssl \
|
26
|
-
--prefix="$(
|
24
|
+
--prefix="$(NATIVE_PREFIX)"
|
27
25
|
cd build && make sysconfdir=/opt/prestogres/etc
|
28
26
|
|
29
|
-
clean-
|
27
|
+
clean-native:
|
30
28
|
@rm -rf build
|
31
29
|
|
32
|
-
install-
|
30
|
+
install-native:
|
33
31
|
cd build && make install
|
34
32
|
|
33
|
+
.PHONY: native clean-native install-native
|
data/pgpool2/child.c
CHANGED
@@ -99,7 +99,7 @@ static void rebuild_startup_packet(StartupPacket *sp)
|
|
99
99
|
{
|
100
100
|
if (sp->major == PROTO_MAJOR_V2) {
|
101
101
|
StartupPacket_v2 *sp2 = (StartupPacket_v2 *)(sp->startup_packet);
|
102
|
-
strncpy(sp2->database,
|
102
|
+
strncpy(sp2->database, sp->database, SM_DATABASE);
|
103
103
|
strncpy(sp2->user, sp->user, SM_USER);
|
104
104
|
|
105
105
|
} else if (sp->major == PROTO_MAJOR_V3) {
|
data/pgpool2/config.h.in
CHANGED
data/pgpool2/configure
CHANGED
@@ -11450,6 +11450,45 @@ _ACEOF
|
|
11450
11450
|
|
11451
11451
|
fi
|
11452
11452
|
|
11453
|
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lpcre" >&5
|
11454
|
+
$as_echo_n "checking for main in -lpcre... " >&6; }
|
11455
|
+
if test "${ac_cv_lib_pcre_main+set}" = set; then :
|
11456
|
+
$as_echo_n "(cached) " >&6
|
11457
|
+
else
|
11458
|
+
ac_check_lib_save_LIBS=$LIBS
|
11459
|
+
LIBS="-lpcre $LIBS"
|
11460
|
+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
11461
|
+
/* end confdefs.h. */
|
11462
|
+
|
11463
|
+
|
11464
|
+
int
|
11465
|
+
main ()
|
11466
|
+
{
|
11467
|
+
return main ();
|
11468
|
+
;
|
11469
|
+
return 0;
|
11470
|
+
}
|
11471
|
+
_ACEOF
|
11472
|
+
if ac_fn_c_try_link "$LINENO"; then :
|
11473
|
+
ac_cv_lib_pcre_main=yes
|
11474
|
+
else
|
11475
|
+
ac_cv_lib_pcre_main=no
|
11476
|
+
fi
|
11477
|
+
rm -f core conftest.err conftest.$ac_objext \
|
11478
|
+
conftest$ac_exeext conftest.$ac_ext
|
11479
|
+
LIBS=$ac_check_lib_save_LIBS
|
11480
|
+
fi
|
11481
|
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcre_main" >&5
|
11482
|
+
$as_echo "$ac_cv_lib_pcre_main" >&6; }
|
11483
|
+
if test "x$ac_cv_lib_pcre_main" = x""yes; then :
|
11484
|
+
cat >>confdefs.h <<_ACEOF
|
11485
|
+
#define HAVE_LIBPCRE 1
|
11486
|
+
_ACEOF
|
11487
|
+
|
11488
|
+
LIBS="-lpcre $LIBS"
|
11489
|
+
|
11490
|
+
fi
|
11491
|
+
|
11453
11492
|
|
11454
11493
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
|
11455
11494
|
$as_echo_n "checking for ANSI C header files... " >&6; }
|
data/pgpool2/configure.in
CHANGED
@@ -32,6 +32,8 @@
|
|
32
32
|
#include <netinet/in.h>
|
33
33
|
#include <stdlib.h>
|
34
34
|
|
35
|
+
#include <pcre.h>
|
36
|
+
|
35
37
|
/*
|
36
38
|
* Where to send query
|
37
39
|
*/
|
@@ -293,7 +295,11 @@ int pool_virtual_master_db_node_id(void)
|
|
293
295
|
#define PRESTO_RESULT_TABLE_NAME "presto_result"
|
294
296
|
#endif
|
295
297
|
|
296
|
-
|
298
|
+
#ifndef PRESTO_REWRITE_QUERY_SIZE_LIMIT
|
299
|
+
#define PRESTO_REWRITE_QUERY_SIZE_LIMIT 32768
|
300
|
+
#endif
|
301
|
+
|
302
|
+
char rewrite_query_string_buffer[PRESTO_REWRITE_QUERY_SIZE_LIMIT];
|
297
303
|
|
298
304
|
static char *strcpy_capped(char *buffer, int length, const char *string)
|
299
305
|
{
|
@@ -362,7 +368,7 @@ static void do_replace_query(POOL_QUERY_CONTEXT* query_context, const char *quer
|
|
362
368
|
query_context->original_length = strlen(dupq) + 1;
|
363
369
|
}
|
364
370
|
|
365
|
-
static void
|
371
|
+
static void rewrite_error_query_static(POOL_QUERY_CONTEXT* query_context, const char *message, const char* errcode)
|
366
372
|
{
|
367
373
|
char *buffer, *bufend;
|
368
374
|
|
@@ -381,14 +387,39 @@ static void rewrite_error_query(POOL_QUERY_CONTEXT* query_context, const char *m
|
|
381
387
|
buffer = strcpy_capped(buffer, bufend - buffer, "; end $$ language plpgsql");
|
382
388
|
|
383
389
|
if (buffer == NULL) {
|
384
|
-
|
385
|
-
|
386
|
-
|
390
|
+
buffer = rewrite_query_string_buffer;
|
391
|
+
bufend = buffer + sizeof(rewrite_query_string_buffer);
|
392
|
+
|
393
|
+
buffer = strcpy_capped(buffer, bufend - buffer, "do $$ begin raise ecxeption 'too long error message'");
|
394
|
+
buffer = strcpy_capped(buffer, bufend - buffer, " using errcode = E'");
|
395
|
+
buffer = strcpy_capped_escaped(buffer, bufend - buffer, errcode, "'\\$");
|
396
|
+
buffer = strcpy_capped(buffer, bufend - buffer, "'; end $$ language plpgsql");
|
397
|
+
|
398
|
+
if (buffer == NULL) {
|
399
|
+
do_replace_query(query_context,
|
400
|
+
"do $$ begin raise ecxeption 'too long error message'; end $$ language plpgsql");
|
401
|
+
return;
|
402
|
+
}
|
387
403
|
}
|
388
404
|
|
389
405
|
do_replace_query(query_context, rewrite_query_string_buffer);
|
390
406
|
}
|
391
407
|
|
408
|
+
static void rewrite_error_query(POOL_QUERY_CONTEXT* query_context, char *message, const char* errcode)
|
409
|
+
{
|
410
|
+
/* 20 is for escape characters */
|
411
|
+
const size_t static_length = strlen("do $$ begin raise exception '%', E'' using errcode = E'XXXXX'; end $$ language plpgsql") + 20;
|
412
|
+
|
413
|
+
if (sizeof(rewrite_query_string_buffer) < strlen(message) + static_length) {
|
414
|
+
message[sizeof(rewrite_query_string_buffer) - static_length - 3] = '.';
|
415
|
+
message[sizeof(rewrite_query_string_buffer) - static_length - 2] = '.';
|
416
|
+
message[sizeof(rewrite_query_string_buffer) - static_length - 1] = '.';
|
417
|
+
message[sizeof(rewrite_query_string_buffer) - static_length - 0] = '\0';
|
418
|
+
}
|
419
|
+
|
420
|
+
rewrite_error_query_static(query_context, message, errcode);
|
421
|
+
}
|
422
|
+
|
392
423
|
static void run_clear_query(POOL_SESSION_CONTEXT* session_context, POOL_QUERY_CONTEXT* query_context)
|
393
424
|
{
|
394
425
|
// TODO
|
@@ -413,7 +444,7 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
413
444
|
buffer = strcpy_capped(buffer, bufend - buffer, "'");
|
414
445
|
|
415
446
|
if (buffer == NULL) {
|
416
|
-
|
447
|
+
rewrite_error_query_static(query_context, "schema name too long", NULL);
|
417
448
|
return;
|
418
449
|
}
|
419
450
|
|
@@ -425,7 +456,7 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
425
456
|
if (errmsg != NULL) {
|
426
457
|
rewrite_error_query(query_context, errmsg, errcode);
|
427
458
|
} else if (status != POOL_CONTINUE) {
|
428
|
-
|
459
|
+
rewrite_error_query_static(query_context, "Unknown execution error", NULL);
|
429
460
|
}
|
430
461
|
|
431
462
|
/* build run_system_catalog_as_temp_table query */
|
@@ -447,7 +478,7 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
447
478
|
buffer = strcpy_capped(buffer, bufend - buffer, "')");
|
448
479
|
|
449
480
|
if (buffer == NULL) {
|
450
|
-
|
481
|
+
rewrite_error_query_static(query_context, "metadata too long", NULL);
|
451
482
|
return;
|
452
483
|
}
|
453
484
|
|
@@ -459,7 +490,7 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
459
490
|
if (errmsg != NULL) {
|
460
491
|
rewrite_error_query(query_context, errmsg, errcode);
|
461
492
|
} else if (status != POOL_CONTINUE) {
|
462
|
-
|
493
|
+
rewrite_error_query_static(query_context, "Unknown execution error", NULL);
|
463
494
|
}
|
464
495
|
|
465
496
|
/* rewrite query */
|
@@ -470,13 +501,47 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
470
501
|
buffer = strcpy_capped(buffer, bufend - buffer, PRESTO_RESULT_TABLE_NAME);
|
471
502
|
|
472
503
|
if (buffer == NULL) {
|
473
|
-
|
504
|
+
rewrite_error_query_static(query_context, "query too long", NULL);
|
474
505
|
return;
|
475
506
|
}
|
476
507
|
|
477
508
|
do_replace_query(query_context, rewrite_query_string_buffer);
|
478
509
|
}
|
479
510
|
|
511
|
+
/*
|
512
|
+
* /\A\s*select\s*\*\s*from\s+(("[^\\"]*([\\"][^\\"]*)*")|[a-zA-Z_][a-zA-Z0-9_]*)(\.(("[^\\"]*([\\"][^\\"]*)*")|[a-zA-Z_][a-zA-Z0-9_]*))?\s*(;|\z)/.to_s
|
513
|
+
*/
|
514
|
+
#define AUTO_LIMIT_QUERY_PATTERN "\\A\\s*select\\s*\\*\\s*from\\s+((\"[^\\\\\"]*([\\\\\"][^\\\\\"]*)*\")|[a-zA-Z_][a-zA-Z0-9_]*)(\\.((\"[^\\\\\"]*([\\\\\"][^\\\\\"]*)*\")|[a-zA-Z_][a-zA-Z0-9_]*))?\\s*(;|\\z)"
|
515
|
+
|
516
|
+
static bool match_auto_limit_pattern(const char* query)
|
517
|
+
{
|
518
|
+
const char* errptr;
|
519
|
+
int erroffset;
|
520
|
+
pcre* pattern;
|
521
|
+
int ret;
|
522
|
+
int ovec[10];
|
523
|
+
|
524
|
+
pattern = pcre_compile(AUTO_LIMIT_QUERY_PATTERN,
|
525
|
+
PCRE_CASELESS | PCRE_NO_AUTO_CAPTURE | PCRE_UTF8, &errptr, &erroffset, NULL);
|
526
|
+
if (pattern == NULL) {
|
527
|
+
// TODO pcre pattern should be precompiled. see also pcre_study.
|
528
|
+
pool_error("match_auto_limit_pattern: invalid regexp %s at %d", errptr, erroffset);
|
529
|
+
pcre_free(pattern);
|
530
|
+
return false;
|
531
|
+
}
|
532
|
+
|
533
|
+
ret = pcre_exec(pattern, NULL, query, strlen(query), 0, 0, &ovec, sizeof(ovec));
|
534
|
+
if (ret < 0) {
|
535
|
+
// error. pattern didn't match in most of cases
|
536
|
+
pcre_free(pattern);
|
537
|
+
return false;
|
538
|
+
}
|
539
|
+
|
540
|
+
pcre_free(pattern);
|
541
|
+
|
542
|
+
return true;
|
543
|
+
}
|
544
|
+
|
480
545
|
static void run_and_rewrite_presto_query(POOL_SESSION_CONTEXT* session_context, POOL_QUERY_CONTEXT* query_context)
|
481
546
|
{
|
482
547
|
char *buffer, *bufend;
|
@@ -519,11 +584,17 @@ static void run_and_rewrite_presto_query(POOL_SESSION_CONTEXT* session_context,
|
|
519
584
|
if (c != NULL) {
|
520
585
|
*c = save;
|
521
586
|
}
|
587
|
+
|
588
|
+
if (match_auto_limit_pattern(query)) {
|
589
|
+
// TODO send warning message to client
|
590
|
+
pool_debug("run_and_rewrite_presto_query: adding 'limit 1000' to a SELECT * query");
|
591
|
+
buffer = strcpy_capped(buffer, bufend - buffer, " limit 1000");
|
592
|
+
}
|
522
593
|
}
|
523
594
|
buffer = strcpy_capped(buffer, bufend - buffer, "')");
|
524
595
|
|
525
596
|
if (buffer == NULL) {
|
526
|
-
|
597
|
+
rewrite_error_query_static(query_context, "query too long", NULL);
|
527
598
|
return;
|
528
599
|
}
|
529
600
|
|
@@ -536,7 +607,7 @@ static void run_and_rewrite_presto_query(POOL_SESSION_CONTEXT* session_context,
|
|
536
607
|
rewrite_error_query(query_context, errmsg, errcode);
|
537
608
|
return;
|
538
609
|
} else if (status != POOL_CONTINUE) {
|
539
|
-
|
610
|
+
rewrite_error_query_static(query_context, "Unknown execution error", NULL);
|
540
611
|
return;
|
541
612
|
}
|
542
613
|
|
@@ -548,7 +619,7 @@ static void run_and_rewrite_presto_query(POOL_SESSION_CONTEXT* session_context,
|
|
548
619
|
buffer = strcpy_capped(buffer, bufend - buffer, PRESTO_RESULT_TABLE_NAME);
|
549
620
|
|
550
621
|
if (buffer == NULL) {
|
551
|
-
|
622
|
+
rewrite_error_query_static(query_context, "query too long", NULL);
|
552
623
|
return;
|
553
624
|
}
|
554
625
|
|
@@ -564,7 +635,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
564
635
|
POOL_CONNECTION_POOL *backend;
|
565
636
|
int i;
|
566
637
|
|
567
|
-
const char*
|
638
|
+
const char* static_error_message;
|
568
639
|
enum {
|
569
640
|
REWRITE_CLEAR,
|
570
641
|
REWRITE_SYSTEM_CATALOG,
|
@@ -676,7 +747,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
676
747
|
*/
|
677
748
|
if (TSTATE(backend, PRIMARY_NODE_ID) == 'I' ||
|
678
749
|
(!pool_is_writing_transaction() &&
|
679
|
-
!pool_is_failed_transaction() &&
|
750
|
+
!(MAJOR(backend) == PROTO_MAJOR_V3 && pool_is_failed_transaction()) &&
|
680
751
|
pool_get_transaction_isolation() != POOL_SERIALIZABLE))
|
681
752
|
{
|
682
753
|
BackendInfo *bkinfo = pool_get_node_info(session_context->load_balance_node_id);
|
@@ -695,7 +766,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
695
766
|
pool_debug("prestogres: send_to_where: replication delay");
|
696
767
|
pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID);
|
697
768
|
rewrite_mode = REWRITE_ERROR;
|
698
|
-
|
769
|
+
static_error_message = "unexpected replication delay";
|
699
770
|
}
|
700
771
|
|
701
772
|
/*
|
@@ -776,16 +847,27 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
776
847
|
pool_debug("prestogres: send_to_where: invalid session state");
|
777
848
|
pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID);
|
778
849
|
rewrite_mode = REWRITE_ERROR;
|
779
|
-
|
850
|
+
static_error_message = "invalid session state";
|
780
851
|
}
|
781
852
|
}
|
853
|
+
else if (query_context->is_parse_error)
|
854
|
+
{
|
855
|
+
/*
|
856
|
+
* If failed to parse the query, run it on Presto because
|
857
|
+
* it may include Presto's SQL syntax extensions.
|
858
|
+
*/
|
859
|
+
pool_debug("prestogres: send_to_where: parse-error");
|
860
|
+
pool_set_node_to_be_sent(query_context,
|
861
|
+
session_context->load_balance_node_id);
|
862
|
+
rewrite_mode = REWRITE_PRESTO;
|
863
|
+
}
|
782
864
|
else
|
783
865
|
{
|
784
866
|
/* Send to the primary only */
|
785
867
|
pool_debug("prestogres: send_to_where: non-select");
|
786
868
|
pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID);
|
787
869
|
rewrite_mode = REWRITE_ERROR;
|
788
|
-
|
870
|
+
static_error_message = "only SELECT is supported";
|
789
871
|
}
|
790
872
|
}
|
791
873
|
}
|
@@ -891,7 +973,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
891
973
|
break;
|
892
974
|
|
893
975
|
case REWRITE_ERROR:
|
894
|
-
|
976
|
+
rewrite_error_query_static(query_context, static_error_message, NULL);
|
895
977
|
break;
|
896
978
|
}
|
897
979
|
}
|
data/pgsql/prestogres.py
CHANGED
@@ -48,25 +48,22 @@ def _build_create_temp_table_sql(table_name, column_names, column_types):
|
|
48
48
|
return create_sql
|
49
49
|
|
50
50
|
# build CREATE TABLE statement
|
51
|
-
def
|
52
|
-
|
51
|
+
def _build_alter_table_holder_sql(schema_name, table_name, column_names, column_types, not_nulls):
|
52
|
+
alter_sql = "alter table %s.%s \n " % (plpy.quote_ident(schema_name), plpy.quote_ident(table_name))
|
53
53
|
|
54
54
|
first = True
|
55
55
|
for column_name, column_type, not_null in zip(column_names, column_types, not_nulls):
|
56
56
|
if first:
|
57
57
|
first = False
|
58
58
|
else:
|
59
|
-
|
59
|
+
alter_sql += ",\n "
|
60
60
|
|
61
|
-
|
62
|
-
create_sql += " "
|
63
|
-
create_sql += column_type
|
61
|
+
alter_sql += "add %s %s" % (plpy.quote_ident(column_name), column_type)
|
64
62
|
|
65
63
|
if not_null:
|
66
|
-
|
64
|
+
alter_sql += " not null"
|
67
65
|
|
68
|
-
|
69
|
-
return create_sql
|
66
|
+
return alter_sql
|
70
67
|
|
71
68
|
# build INSERT INTO statement and string format to build VALUES (..), ...
|
72
69
|
def _build_insert_into_sql(table_name, column_names):
|
@@ -231,6 +228,8 @@ def run_system_catalog_as_temp_table(server, user, catalog, schema, result_table
|
|
231
228
|
statements = []
|
232
229
|
schema_names = []
|
233
230
|
|
231
|
+
table_holder_id = 0
|
232
|
+
|
234
233
|
for schema_name, tables in schemas.items():
|
235
234
|
if schema_name == "sys" or schema_name == "information_schema":
|
236
235
|
# skip system schemas
|
@@ -248,8 +247,17 @@ def run_system_catalog_as_temp_table(server, user, catalog, schema, result_table
|
|
248
247
|
column_types.append(_pg_table_type(column.type))
|
249
248
|
not_nulls.append(not column.nullable)
|
250
249
|
|
251
|
-
|
252
|
-
statements.append(
|
250
|
+
# rename table holder into the schema
|
251
|
+
statements.append("alter table prestogres_catalog.table_holder_%d set schema %s" % \
|
252
|
+
(table_holder_id, plpy.quote_ident(schema_name)))
|
253
|
+
statements.append("alter table %s.table_holder_%d rename to %s" % \
|
254
|
+
(plpy.quote_ident(schema_name), table_holder_id, plpy.quote_ident(table_name)))
|
255
|
+
|
256
|
+
# change columns
|
257
|
+
alter_sql = _build_alter_table_holder_sql(schema_name, table_name, column_names, column_types, not_nulls)
|
258
|
+
statements.append(alter_sql)
|
259
|
+
|
260
|
+
table_holder_id += 1
|
253
261
|
|
254
262
|
# cache expires after 60 seconds
|
255
263
|
SchemaCacheEntry.set_cache(server, user, catalog, schema, schema_names, statements, time.time() + 60)
|
@@ -267,45 +275,50 @@ def run_system_catalog_as_temp_table(server, user, catalog, schema, result_table
|
|
267
275
|
subxact = plpy.subtransaction()
|
268
276
|
subxact.enter()
|
269
277
|
try:
|
270
|
-
#
|
278
|
+
# drop all schemas excepting prestogres_catalog, pg_catalog, information_schema, public
|
279
|
+
# and schema holders
|
271
280
|
sql = "select n.nspname as schema_name from pg_catalog.pg_namespace n" \
|
272
281
|
" where n.nspname not in ('prestogres_catalog', 'pg_catalog', 'information_schema', 'public')" \
|
282
|
+
" and n.nspname not like 'prestogres_catalog_schema_holder_%'" \
|
273
283
|
" and n.nspname !~ '^pg_toast'"
|
274
284
|
for row in plpy.cursor(sql):
|
275
285
|
plpy.execute("drop schema %s cascade" % plpy.quote_ident(row["schema_name"]))
|
276
286
|
|
277
|
-
#
|
278
|
-
|
279
|
-
sql = "select n.nspname as schema_name, c.relname as table_name from pg_catalog.pg_class c" \
|
280
|
-
" left join pg_catalog.pg_namespace n on n.oid = c.relnamespace" \
|
281
|
-
" where c.relkind in ('r')" \
|
282
|
-
" and n.nspname in ('prestogres_catalog')" \
|
283
|
-
" and n.nspname !~ '^pg_toast'"
|
284
|
-
for row in plpy.cursor(sql):
|
285
|
-
plpy.execute("drop table %s.%s" % (plpy.quote_ident(row["schema_name"]), plpy.quote_ident(row["table_name"])))
|
286
|
-
|
287
|
-
# create schemas
|
287
|
+
# alter schema holders
|
288
|
+
schema_holder_id = 0
|
288
289
|
for schema_name in schema_names:
|
289
290
|
try:
|
290
|
-
plpy.execute("
|
291
|
+
plpy.execute("alter schema prestogres_catalog_schema_holder_%s rename to %s" % \
|
292
|
+
(schema_holder_id, plpy.quote_ident(schema_name)))
|
293
|
+
schema_holder_id += 1
|
291
294
|
except:
|
292
|
-
# ignore error
|
295
|
+
# ignore error?
|
293
296
|
pass
|
294
297
|
|
295
|
-
#
|
298
|
+
# alter table holders in prestogres_catalog schema
|
296
299
|
for statement in statements:
|
297
300
|
plpy.execute(statement)
|
298
301
|
|
299
|
-
#
|
302
|
+
# drop prestogres_catalog schema
|
303
|
+
plpy.execute("drop schema prestogres_catalog cascade")
|
304
|
+
|
305
|
+
# drop schema holders
|
306
|
+
sql = "select n.nspname as schema_name from pg_catalog.pg_namespace n" \
|
307
|
+
" where n.nspname like 'prestogres_catalog_schema_holder_%'"
|
308
|
+
for row in plpy.cursor(sql):
|
309
|
+
plpy.execute("drop schema %s" % plpy.quote_ident(row["schema_name"]))
|
310
|
+
|
311
|
+
# run the actual query and save result
|
300
312
|
metadata = plpy.execute(query)
|
301
313
|
column_names = metadata.colnames()
|
302
314
|
column_type_oids = metadata.coltypes()
|
303
315
|
result = map(lambda row: map(row.get, column_names), metadata)
|
304
316
|
|
305
|
-
#
|
317
|
+
# save result schema
|
306
318
|
oid_to_type_name = _load_oid_to_type_name_mapping(column_type_oids)
|
307
319
|
column_types = map(oid_to_type_name.get, column_type_oids)
|
308
320
|
|
321
|
+
# store query cache
|
309
322
|
query_cache[query] = QueryResult(column_names, column_types, result)
|
310
323
|
|
311
324
|
finally:
|
@@ -325,3 +338,12 @@ def run_system_catalog_as_temp_table(server, user, catalog, schema, result_table
|
|
325
338
|
e.__class__.__module__ = "__main__"
|
326
339
|
raise
|
327
340
|
|
341
|
+
def create_schema_holders(count):
|
342
|
+
for i in range(count):
|
343
|
+
plpy.execute("drop schema if exists prestogres_catalog_schema_holder_%d cascade" % i)
|
344
|
+
plpy.execute("create schema prestogres_catalog_schema_holder_%d" % i)
|
345
|
+
|
346
|
+
def create_table_holders(count):
|
347
|
+
for i in range(count):
|
348
|
+
plpy.execute("create table prestogres_catalog.table_holder_%d ()" % i)
|
349
|
+
|
data/pgsql/setup.sql
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
|
2
2
|
create language "plpythonu";
|
3
3
|
|
4
|
+
drop schema if exists prestogres_catalog cascade;
|
5
|
+
|
4
6
|
create schema prestogres_catalog;
|
5
7
|
|
6
8
|
create or replace function prestogres_catalog.run_presto_as_temp_table(
|
@@ -27,3 +29,19 @@ import prestogres
|
|
27
29
|
prestogres.run_system_catalog_as_temp_table(server, user, catalog, schema, table_name, query)
|
28
30
|
$$ language plpythonu;
|
29
31
|
|
32
|
+
create or replace function prestogres_catalog.create_schema_holders("count" int)
|
33
|
+
returns void as $$
|
34
|
+
import prestogres
|
35
|
+
prestogres.create_schema_holders(count);
|
36
|
+
$$ language plpythonu;
|
37
|
+
|
38
|
+
create or replace function prestogres_catalog.create_table_holders("count" int)
|
39
|
+
returns void as $$
|
40
|
+
import prestogres
|
41
|
+
prestogres.create_table_holders(count);
|
42
|
+
$$ language plpythonu;
|
43
|
+
|
44
|
+
select prestogres_catalog.create_schema_holders(512);
|
45
|
+
|
46
|
+
select prestogres_catalog.create_table_holders(2048);
|
47
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prestogres
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-03-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -479,18 +479,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
479
479
|
- - ! '>='
|
480
480
|
- !ruby/object:Gem::Version
|
481
481
|
version: '0'
|
482
|
-
segments:
|
483
|
-
- 0
|
484
|
-
hash: -1048444669451031740
|
485
482
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
486
483
|
none: false
|
487
484
|
requirements:
|
488
485
|
- - ! '>='
|
489
486
|
- !ruby/object:Gem::Version
|
490
487
|
version: '0'
|
491
|
-
segments:
|
492
|
-
- 0
|
493
|
-
hash: -1048444669451031740
|
494
488
|
requirements: []
|
495
489
|
rubyforge_project:
|
496
490
|
rubygems_version: 1.8.23
|
@@ -498,3 +492,4 @@ signing_key:
|
|
498
492
|
specification_version: 3
|
499
493
|
summary: Presto PostgreSQL protocol gateway
|
500
494
|
test_files: []
|
495
|
+
has_rdoc: false
|