-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathREADME
568 lines (461 loc) · 25.3 KB
/
README
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
NAME
DBD::OraLite - PostgreSQL emulation mode for SQLite
SUMMARY
use DBI;
my $dbh = DBI->connect('dbi:OraLite:dbname=file');
# The following PostgreSQL-flavoured SQL is invalid
# in SQLite directly, but works using OraLite
my $sql = q[
SELECT
news_id, title, cat_id, cat_name, sc_id sc_name,
to_char(news_created,'FMDD.FMMM.YYYY') AS ndate
FROM
news
NATURAL JOIN x_news_cat
NATURAL JOIN cat
NATURAL JOIN subcat
WHERE
news_active = TRUE
AND news_created > NOW() - INTERVAL '7 days'
];
my $res = $dbh->selectall_arrayref($sql,{Columns=>{}});
# From v. 0.05 with full sequence function support
my $get_nid = "SELECT NEXTVAL('news_news_id_seq')";
my $news_id = $dbh->selectrow_array($get_nid);
DESCRIPTION
The module automatically and transparently transforms a broad range of
SQL statements typical of PostgreSQL into a form suitable for use in
SQLite. This involves both (a) parsing and filtering of the SQL; and (b)
the addition of several PostgreSQL-compatible functions to SQLite.
Mainly because of datatype issues, support for many PostgreSQL features
simply cannot be provided without elaborate planning and detailed
metadata. Since this module is intended to be usable with any SQLite3
database, it follows that the emulation is limited in several respects.
An overview of what works and what doesn't is given in the following
section on PostgreSQL Compatibility.
DBD::OraLite has support of a sort for stored procedures. This is
described in the Extras section below. So are the few database functions
defined by this module which are not in PostgreSQL. Finally, the Extras
section contains a brief mention of the DBD::OraLite::MirrorOraToSQLite
companion module.
If you do not want SQL filtering to be turned on by default for the
entire session, you can connect setting the connection attribute
*FilterSQL* to a false value:
my $dbh = DBI->connect("dbi:OraLite:dbname=$fn",
undef, undef, {FilterSQL=>0});
To turn filtering off (or on) for a single statement, you can specify
*FilterSQL* option as a statement attribute, e.g.:
$dbh->do($sql, {FilterSQL=>0}, @bind);
my $sth = $dbh->prepare($sql, {FilterSQL=>0});
$res = $dbh->selectall_arrayref($sql, {FilterSQL=>0}, @bind);
It is possible to specify user-defined pre- and postfiltering routines,
both globally (by specifying them as attributes of the database handle)
and locally (by specifying them as statement attributes):
$dbh = DBI->connect("dbi:OraLite:$file",undef,undef,
{prefilter=>\&prefilter});
$res = $dbh->selectall_arrayref($sql,
{postfilter=>\&postfilter},
@bind_values);
The pre-/postfiltering subroutine receives the SQL as parameter and is
expected to return the changed SQL.
STATUS OF THE MODULE
This module was initially developed using SQLite 3.0 and PostgreSQL 7.3,
but it should be fully compatible with newer versions of both SQLite
(3.1 and 3.2 have been tested) and PostgreSQL (8.1 has been tested).
Support for SELECT statements and the WHERE-conditions of DELETE and
UPDATE statements is rather good, though still incomplete. The module
especially focuses on NATURAL JOIN differences and commonly used,
built-in PostgreSQL functions.
Support for inserted/updated values in INSERT and UPDATE statements
could use some improvement but is useable for simple things.
There is no support for differences in DDL.
The SQL transformations used are not based on a formal grammar but on
applying simple regular expressions. An obvious consequence of this is
that they may depend excessively on the author's SQL style. YMMV. (I
would however like you to contact me if you come across some SQL
statements which you feel should work but that don't).
The development of this module has been driven by personal needs, and so
is likely to be even more one-sided than the above description suggests.
POSTGRESQL COMPATIBILITY
In this section, the PostgreSQL functions and operators supported by the
module are enumerated.
Regex operators
* The regex operators "~", "~*", "!~" and "!~*" are transformed into
calls to the user-defined function matches(). The regex flavour
supported is Perl, not plain vanilla POSIX, so some
incompatibilities may arise.
* Note that for ease of parsing, whitespace before and after the
operator is required for the filtering to succeed. So "col ~ 'pat'"
works, but "col~'pat'" doesn't.
* "SIMILAR TO" is not supported.
* ILIKE is quietly changed to LIKE. LIKE in SQLite is case-insensitive
for 7-bit characters. In future, ILIKE will probably be handled more
elegantly, and LIKE will be redefined so as to be more like
PostgreSQL.
Math Functions
* Added: abs, cbrt, ceil, degrees, exp, floor, ln, log (1- and
2-argument forms), mod, pi, pow, radians, sign, sqrt, trunc (1- and
2-argument forms), acos, asin, atan, atan2, cos, cot, sin, tan.
* random() exists in SQLite but was redefined to conform better with
PostgreSQL in terms of value range. setseed() was also added, but is
not entirely compatible with PostgreSQL in the sense that setting
the random seed does not engender the same sequence of pseudo-random
numbers as it would in PostgreSQL.
* SQLite already has a handful of mathematical functions which have
been left alone, notably round() (1- and 2-argument forms).
String Functions
The only string functions which are present natively in SQLite are
substr(), lower() and upper(). These have been left alone. Added
functions are the following:
* ascii, bit_length, btrim, char_length, character_length, chr,
convert (1- and 2-arg), decode, encode, initcap, length, lpad,
ltrim, md5, octet_length, position, pg_client_encoding (always
'SQL_ASCII'), quote_ident, quote_literal, repeat, replace, rpad,
rtrim, split_part, strpos, substring(string,offset,length),
substring(string from pattern), to_ascii (assumes latin-1 input),
to_hex, translate, trim.
Except for convert(), where another input encoding can be specified
explicitly, these functions all assume that the strings are in an 8-bit
character set, preferably iso-8859-1.
The little-used idiom "substring(string from pattern for escape)" (where
'pattern' is not a POSIX regular expression but a SQL pattern) is not
supported. Otherwise support for string functions is pretty complete.
Data Type Formatting Functions
The implementation of these functions is impeded by the sparse type
system employed by SQLite. Workarounds are possible, however, so this
area will probably be better covered in future.
* to_char(timestamp, format) is mostly supported. There is support for
most formatting strings (all except 'Y,YYY', 'IYYY', 'IYY', 'IY',
'I', 'J', 'TZ', and 'tz'). The FM prefix is supported for 'MM', 'DD'
and 'HH*', but not otherwise. Other prefixes are not supported.
* to_char(interval, format) and to_char(number, format) are not
currently supported. Nor are to_date(), to_timestamp() and
to_number() (yet).
Date/Time Functions
Again, SQLite's intrinsically bad support for dates and intervals makes
this area somewhat hard to cover properly. Function support is as
follows; also note the caveats below:
* Supported: now, current_date, current_time, current_datetime,
date_part (with timestamps, not intervals), date_trunc, extract
(with timestamps, not intervals), localtime, localtimestamp,
timeofday.
* Not supported: age, isfinite, overlaps.
Versions of SQLite 3.1 and later support some of these functions, e.g.
current_date. In these versions the built-in will be overridden.
The module makes no distinction between time/timestamp with and without
time zone. It is assumed that times and timestamps are either all GMT or
all localtime; time zone information is silently discarded. This may
change later.
Support for calculations with dates and intervals is still very limited.
Basically, what is supported are expressions of the form "expr +/-
interval 'descr'" where expr reduces to a timestamp or date value.
If a transaction is started with begin_work(), the time as represented
by now() and friends is "frozen" in the same way as in PostgreSQL until
commit() or rollback() are called. A transaction started by simply
running the SQL statement "BEGIN" does not, however, trigger this
behaviour. Nor is the time automatically "unfrozen" when an error occurs
during a transaction; you need to catch exceptions and call rollback()
manually.
Sequence Manipulation Functions
* There is now full support for all explicit invocations of the
sequence functions nextval(), setval(), currval() and lastval().
Sequences are emulated using the table pglite_seq. (This works even
with multiple connections to the same database file, some of which
are using transactions, since SQLite transactions lock the whole
database file, luckily eliminating any risk of two connections
getting the same value from a nextval() call).
Please be aware that sequences are autogenerated if they do not
exist. Be careful to specify the appropriate sequence names or you
will get unexpected results.
If a sequence being autogenerated ends with '_seq' and has a name
which seems to match an existing table + an integer column from that
table (tablename_colname_seq), it is given an initial value based on
the maximum value in the column in question.
There is as yet no support for CREATE SEQUENCE statements. Use the
autogeneration feature to create sequences.
Implicit calls to NEXTVAL() by omitting the serial column from the
column list in an INSERT are caught in most cases. The main
conditions that must be fulfilled for this to work are: (1) that the
column in question is an integer column which is the sole primary
key on the table; and (2) that the statement is a normal INSERT with
a column list and a VALUES clause (and not, e.g., a statement of the
form INSERT INTO x SELECT * FROM y).
There is as yet no interaction with the SQLite builtin
autoincrement/last_insert_rowid() functionality in connection with
the sequence function support.
Aggregate Functions
* max(), min(), count() and sum() are already supported by SQLite and
have been left alone. Note that the construct "count(distinct
colname)" is not supported unless the SQLite version being used
supports it (3.2.6 and later).
* avg() has been added.
* stddev() and variance() are not supported.
A Note on Casting
Casting using the construct "::datatype" is not supported in general.
However, "::int", "::date" and "::bool" should work as expected. All
other casts are silently discarded.
A Note on Booleans
This module assumes that booleans will be stored as numeric values in
the SQLite database. SQLite interprets 0 as false and any non-zero
numeric value as true. Accordingly, expressions such as "= TRUE" and "=
't'" are simply removed in SELECT and DELETE statements. Likewise, "expr
= FALSE" is turned into "NOT expr" before being passed on to SQLite.
In INSERT and DELETE statements, TRUE and FALSE (as well as 't'::bool
and 'f'::bool - but not 't' and 'f' by themselves) are turned into 1 and
0.
Current_user etc.
The functions current_user(), session_user() and user() - with or
without parentheses - all mean the same thing. They return the username
of the effective uid.
Other Functions
The main groups of other functions (not supported by this module at all)
are:
* Database/user information functions: Aside from
current_user/session_user/user, which were mentioned above, no
functions in this group are supported. This includes
current_database(), current_schema(), all functions with names
starting with 'pg_' and 'has_', obj_description and col_description.
See
http://www.postgresql.org/docs/current/static/functions-misc.html
* Array functions are not implemented - see
http://www.postgresql.org/docs/current/static/functions-array.html
* Binary string (BYTEA) functions are not implemented - see
http://www.postgresql.org/docs/current/static/functions-binarystring
.html
* Geometric functions are not implemented - see
http://www.postgresql.org/docs/current/static/functions-geometry.htm
l
* Network Address Functions are not implemented - see
http://www.postgresql.org/docs/current/static/functions-net.html
EXTRAS
Stored Procedures
If the active database file contains a table called pglite_functions,
the module assumes that it will have the following structure:
CREATE TABLE pglite_functions (
name TEXT, -- name of the function
argnum INT, -- number of arguments (-1 means any number)
type TEXT, -- can be 'sql' or 'perl'
sql TEXT, -- the body of the function
PRIMARY KEY (name, argnum)
);
In the case of a SQL-type function, it can contain syntax supported
through the module (and not directly by SQLite). The numeric arguments
($1-$9) customary in PostgreSQL are supported, so that in many cases
simple functions will be directly transferrable from pg_proc in a
PostgreSQL database.
An instance of a SQL snippet which would work as a function body both in
PostgreSQL and OraLite (e.g. with the function name 'full_price_descr'):
SELECT TRIM(group_name||': '||price_description)
FROM price_group NATURAL JOIN price
WHERE price_id = $1
As for perl-type functions, the function body is simply the text of a
subroutine. Here is a simple example of a function body for the function
'commify', which takes two arguments: the number to be formatted and the
desired number of decimal places:
sub {
my ($num,$dp) = @_;
my $format = "%.${dp}f";
$num = scalar reverse(sprintf $format, $num);
my $rest = $1 if $num =~ s/^(\d+)\.//;
$num =~ s/(...)/$1,/g;
$num = "$rest.$num" if $rest;
return scalar reverse($num);
}
Non-Pg Functions
matches(), imatches():
These functions are used behind the scenes to implement support for
the '~' regex-matching operator and its variants. They take two
arguments, a string and a regular expression. matches() is case
sensitive, imatches() isn't.
matches_safe(), imatches_safe():
These work in the same way as matches() and imatches() except that
metacharacters are escaped in the regex argument. They are therefore
in many cases more suitable for user input and other untrusted
sources.
lower_latin1():
Depending on platform, lower() and upper() may not transform the
case of non-ascii characters despite a proper locale being defined
in the environment. This functions assumes that a Latin-1 locale is
active and returns a lower-case version of the input given this
assumption.
localeorder():
DBD::SQLite does not provide access to defining SQLite collation
functions. This is a workaround for a specific case where this
limitation can be an issue. Given a Latin-1 encoded string, it
returns a string of hex digits which can be ascii-sorted in the
ordinary way. The resulting row order will be in accordance with the
currently active locele - but only if the locale is Latin-1 based.
The sort is case-insensitive.
locale():
An information function simply returning the name of the current
locale. The module sets the locale based on the environment
variables $ENV{LC_COLLATE}, $ENV{LC_ALL}, $ENV{LANG}, and
$ENV{LC_CTYPE}, in that order. Currently it is not possible to use
different locales for character type and collation, as far as the
module is concerned.
DBD::OraLite::MirrorOraToSQLite
The companion module, DBD::OraLite::MirrorOraToSQLite, may be of use in
conjunction with this module. It can be used for easily mirroring
specific tables from a PostgreSQL database, moving views and (some)
functions as well if desired.
CAVEATS
Some functions defined by the module are not suitable for use with UTF-8
data and/or in an UTF-8 locale. (This, however, would be rather easy to
change if you're willing to sacrifice proper support for 8-bit locales
such as iso-8859-1).
Please do not make the mistake of using this module for an important
production system - too much can go wrong. But as a development tool it
can be useful, and as a toy it can be fun...
TODO
There is a lot left undone. The next step is probably to handle
non-SELECT statements better.
SEE ALSO
DBI, DBD::SQLite, DBD::Pg, DBD::OraLite::MirrorOraToSQLite;
THANKS TO
Johan Vromans, for encouraging me to improve the sequence support.
AUTHOR
Baldur Kristinsson ([email protected]), 2006.
Copyright (c) 2006 Baldur Kristinsson. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
===================================
NAME
DBD::OraLite::MirrorOraToSQLite - Mirror tables from PostgreSQL to SQLite
SUMMARY
use DBD::OraLite::MirrorPgToSQLite qw(pg_to_sqlite);
pg_to_sqlite(
sqlite_file => '/var/pg_mirror/news.sqlite',
pg_dbh => $dbh,
schema => 'news',
tables => [ qw(news cat img /^x_news/)],
views => [ 'v_newslist' ],
indexes => 1,
verbose => 1,
snapshot => 1,
);
USAGE
The purpose of this module is to facilitate mirroring of tables from a
PostgreSQL dataabse to a SQLite file. The module has only be tested with
PostgreSQL 7.3 and SQLite 3.0-3.2. SQLite 2.x will probably not work; as
for PostgreSQL, any version after 7.2 is supposed to work. If it
doesn't, please let me know.
As seen above, options to the pg_to_sqlite() function (which is exported
on request) are passed in as a hash. These options are described below.
The default values can be changed by overriding the
DBD::OraLite::MirrorOraToSQLite::defaults() subroutine.
Required options
Obviously, the mirroring function needs either a PosgtgreSQL database
connection or enough information to be able to connect to the database
by itself. It also needs the name of a target SQLite file, and a list of
tables to copy between the two databases.
pg_dbh, pg_user, pg_pass, pg_dsn
If a database handle is specified in *pg_dbh*, it takes precedence.
Otherwise we try to connect using *pg_dsn*, *pg_user*, and *pg_pass*
(which are assigned defaults based on the environment variables
PGDATABASE, PGUSER and PGPASSWORD, if any of these is present).
tables
The value of the required *tables* option should be an arrayref of
strings or a string containing a comma-separated list of tablenames
and tablename patterns. A tablename pattern is a string or distinct
string portion delimited by forward slashes. To clarify: Suppose
that a database contains the tables news, img, img_group, cat,
users, comments, news_read_log, x_news_cat, x_news_img, and
x_img_group; and that we want to mirror news, img, cat, x_news_img
and x_news_cat, leaving the other tables alone. To achieve this, you
would set the *tables* option to any of the following (there are of
course also other possibilities):
(1) [qw(news img cat x_news_img x_news_cat)]
(2) 'news, img, cat, x_news_img, x_news_cat'
(3) [qw(news /img$/ /cat$/)]
(4) 'news,/img$/,/cat/'
The purpose of this seemingly unneccesary flexibility in how the
table list is specified is to make the functionality of the module
more easily accessible from the command line.
Please note that the patterns between the slash delimiters are not
Perl regular expressions but rather POSIX regular expressions, used
to query the PostgreSQL system tables directly.
sqlite_file
This should specify the full path to a SQLite file. While the
mirroring takes place, the incoming data is not written directly to
this file, but to a file with the same name except for a '.tmp'
extension. When the operation has finished, the previous file with
the name specified (if any) is renamed with a '.bak' extension, and
the .tmp file is renamed to the requested filename. Unless you use
the *append* option, the information previously in the file will be
totally replaced.
Other options
schema
This signifies the schema from which the tables on the PostgreSQL
side are to be fetched. Default: 'public'. Only one schema can be
specified at a time.
where
A WHERE-condition appended to the SELECT-statement used to get data
from the PostgreSQL tables.
views
A list of views, specified in the same manner as the list of tables
for the *tables* option. An attempt is made to define corresponding
views on the SQLite side (though this functionality is far from
reliable).
indexes
A boolean option indicating whether to create indexes for the same
columns in SQLite as in PostgreSQL. Default: false. (Normally only
the primary key is created).
functions
A boolean indicating whether to attempt to create functions on the
SQLite side corresponding to any SQL language (NOT PL/pgSQL or other
procedural language) functions in the PostgreSQL database. This is
for use with DBD::OraLite only, since these functions are put into
the pglite_functions table. Default: false.
page_limit
Normally the information from the PostgreSQL tables is read into
memory in one go and transferred directly to the SQLite file. This
is, however, obviously not desireable for very large tables. If the
PostgreSQL system tables report that the page count for the table is
above the limit specified by *page_limit*, the table is instead
transferred row-by-row. Default value: 5000; since each page
normally is 8K, this represents about 40 MB on disk and perhaps
70-100 MB of memory usage by the Perl process. For page_limit to
work, the table must have a primary key.
NB! Do not set this limit lower than necessary: it is orders of
magnitude slower than the default "slurp into memory" mode.
append
If this boolean option is true, then instead of creating a new
SQLite file, the current contents of the *sqlite_file* are added to.
If a table which is being mirrored existed previously in the file,
it is dropped and recreated, but any tables not being copied from
PostgreSQL in the current run are left alone. (This is primarily
useful for mirroring some tables in toto, and others only in part,
into the same file). Default: false. Incompatible with the
*snapshot* option.
snapshot
If this is true, then the copying from PostgreSQL takes place in
serialized mode (transaction isolation level serializable), which
should ensure consistency of relations between tables linked by
foreign key constraints. Currently, foreign keys are not created on
the SQLite side, however. Default: false. Incompatible with the
*append* option.
cachedir
The current method for getting information about table structure in
PostgreSQL is somewhat slow, especially for databases with very many
tables. To offset this, table definitions are cached in a temporary
directory so that subsequent mirrorings of the same table will go
faster. The downside is, of course, that if the table structure
changes, the cache needs to be cleared manually. The cache directory
can be specified using this option; the default is
/tmp/sqlite_mirror_cache (with separate subdirectories for each
user).
verbose
If this is true, a few messages will be output to stderr during the
mirroring process.
TODO
* Support for foreign keys is missing.
* The method used to read tables bigger than *page_limit* needs to be
improved.
* It would be nice to have a quick way of telling whether the cached
table definition of a specific table is still valid.
* Tests.
AUTHOR
Baldur Kristinsson ([email protected]), 2004-2006.
Copyright (c) 2006 Baldur Kristinsson. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.