mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-01 14:35:29 +00:00
[3080] Initial PostgreSQL patch
- patch contributed by David Carlier (thanks!) - updated to current master - fixed compliation issues - written developer's guide (pgsql setup steps) - many smaller changes - still WIP
This commit is contained in:
72
configure.ac
72
configure.ac
@@ -969,6 +969,58 @@ AC_CHECK_HEADER(sys/filio.h)
|
|||||||
# ... and at the shell level, so Makefile.am can take action depending on this.
|
# ... and at the shell level, so Makefile.am can take action depending on this.
|
||||||
AM_CONDITIONAL(HAVE_MYSQL, test "$MYSQL_CONFIG" != "")
|
AM_CONDITIONAL(HAVE_MYSQL, test "$MYSQL_CONFIG" != "")
|
||||||
|
|
||||||
|
pg_config="no"
|
||||||
|
AC_ARG_WITH([dhcp-pgsql],
|
||||||
|
AC_HELP_STRING([--with-dhcp-pgsql=PATH],
|
||||||
|
[path to the PostgreSQL 'pg_config' script]),
|
||||||
|
[pg_config="$withval"])
|
||||||
|
|
||||||
|
if test "${pg_config}" = "yes" ; then
|
||||||
|
PG_CONFIG="/usr/bin/pg_config"
|
||||||
|
elif test "${pg_config}" != "no" ; then
|
||||||
|
PG_CONFIG="${withval}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$PG_CONFIG" != "" ; then
|
||||||
|
if test -d "$PG_CONFIG" -o ! -x "$PG_CONFIG" ; then
|
||||||
|
AC_MSG_ERROR([--with-dhcp-pgsql should point to a pg_config program])
|
||||||
|
fi
|
||||||
|
|
||||||
|
PGSQL_CPPFLAGS=`$PG_CONFIG --cppflags`
|
||||||
|
PGSQL_INCLUDEDIR=`$PG_CONFIG --includedir`
|
||||||
|
PGSQL_CPPFLAGS="$PGSQL_CPPFLAGS -I$PGSQL_INCLUDEDIR"
|
||||||
|
PGSQL_LIBS=`$PG_CONFIG --ldflags`
|
||||||
|
PGSQL_LIBS="$PGSQL_LIBS -lpq"
|
||||||
|
PGSQL_VERSION=`$PG_CONFIG --version`
|
||||||
|
|
||||||
|
AC_SUBST(PGSQL_CPPFLAGS)
|
||||||
|
AC_SUBST(PGSQL_LIBS)
|
||||||
|
|
||||||
|
# Check that a simple program using PostgreSQL functions can compile and link.
|
||||||
|
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||||
|
LIBS_SAVED="$LIBS"
|
||||||
|
|
||||||
|
CPPFLAGS="$PGSQL_CPPFLAGS $CPPFLAGS"
|
||||||
|
LIBS="$PGSQL_LIBS $LIBS"
|
||||||
|
|
||||||
|
AC_LINK_IFELSE(
|
||||||
|
[AC_LANG_PROGRAM([#include <libpq-fe.h>],
|
||||||
|
[PGconn * c = PQconnectdb("dbname = 'postgres'");
|
||||||
|
PQfinish(c);])],
|
||||||
|
[AC_MSG_RESULT([checking for PostgreSQL headers and library... yes])],
|
||||||
|
[AC_MSG_RESULT([checking for PostgreSQL headers and library... no])
|
||||||
|
AC_MSG_ERROR([Needs PostgreSQL library])]
|
||||||
|
)
|
||||||
|
|
||||||
|
CPPFLAGS=$CPPFLAGS_SAVED
|
||||||
|
LIBS=$LIBS_SAVED
|
||||||
|
|
||||||
|
# Note that PostgreSQL is present in the config.h file
|
||||||
|
AC_DEFINE([HAVE_PGSQL], [1], [PostgreSQL is present])
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ... and at the shell level, so Makefile.am can take action depending on this.
|
||||||
|
AM_CONDITIONAL(HAVE_PGSQL, test "$PG_CONFIG" != "")
|
||||||
|
|
||||||
# Check for log4cplus
|
# Check for log4cplus
|
||||||
log4cplus_path="yes"
|
log4cplus_path="yes"
|
||||||
@@ -1743,6 +1795,26 @@ MySQL:
|
|||||||
MYSQL_CPPFLAGS: ${MYSQL_CPPFLAGS}
|
MYSQL_CPPFLAGS: ${MYSQL_CPPFLAGS}
|
||||||
MYSQL_LIBS: ${MYSQL_LIBS}
|
MYSQL_LIBS: ${MYSQL_LIBS}
|
||||||
END
|
END
|
||||||
|
else
|
||||||
|
cat >> config.report << END
|
||||||
|
|
||||||
|
MySQL: no
|
||||||
|
END
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$PGSQL_CPPFLAGS" != "" ; then
|
||||||
|
cat >> config.report << END
|
||||||
|
|
||||||
|
PostgreSQL:
|
||||||
|
PGSQL_VERSION: ${PGSQL_VERSION}
|
||||||
|
PGSQL_CPPFLAGS: ${PGSQL_CPPFLAGS}
|
||||||
|
PGSQL_LIBS: ${PGSQL_LIBS}
|
||||||
|
END
|
||||||
|
else
|
||||||
|
cat >> config.report << END
|
||||||
|
|
||||||
|
PostgreSQL: no
|
||||||
|
END
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$enable_gtest" != "no"; then
|
if test "$enable_gtest" != "no"; then
|
||||||
|
@@ -7,6 +7,9 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
|
|||||||
if HAVE_MYSQL
|
if HAVE_MYSQL
|
||||||
AM_CPPFLAGS += $(MYSQL_CPPFLAGS)
|
AM_CPPFLAGS += $(MYSQL_CPPFLAGS)
|
||||||
endif
|
endif
|
||||||
|
if HAVE_PGSQL
|
||||||
|
AM_CPPFLAGS += $(PGSQL_CPPFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
AM_CXXFLAGS = $(B10_CXXFLAGS)
|
AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||||
|
|
||||||
@@ -54,6 +57,9 @@ libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
|
|||||||
if HAVE_MYSQL
|
if HAVE_MYSQL
|
||||||
libb10_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
|
libb10_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
|
||||||
endif
|
endif
|
||||||
|
if HAVE_PGSQL
|
||||||
|
libb10_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
|
||||||
|
endif
|
||||||
libb10_dhcpsrv_la_SOURCES += option_space_container.h
|
libb10_dhcpsrv_la_SOURCES += option_space_container.h
|
||||||
libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
|
libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
|
||||||
libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
|
libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
|
||||||
@@ -77,6 +83,9 @@ libb10_dhcpsrv_la_LDFLAGS = -no-undefined -version-info 3:0:0
|
|||||||
if HAVE_MYSQL
|
if HAVE_MYSQL
|
||||||
libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
|
libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
|
||||||
endif
|
endif
|
||||||
|
if HAVE_PGSQL
|
||||||
|
libb10_dhcpsrv_la_LDFLAGS += $(PGSQL_LIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
if USE_CLANGPP
|
if USE_CLANGPP
|
||||||
# Disable unused parameter warning caused by some of the
|
# Disable unused parameter warning caused by some of the
|
||||||
@@ -88,8 +97,8 @@ endif
|
|||||||
EXTRA_DIST = dhcpsrv_messages.mes
|
EXTRA_DIST = dhcpsrv_messages.mes
|
||||||
|
|
||||||
# Distribute MySQL schema creation script and backend documentation
|
# Distribute MySQL schema creation script and backend documentation
|
||||||
EXTRA_DIST += dhcpdb_create.mysql database_backends.dox libdhcpsrv.dox
|
EXTRA_DIST += dhcpdb_create.mysql dhcpdb_create.pgsql database_backends.dox libdhcpsrv.dox
|
||||||
dist_pkgdata_DATA = dhcpdb_create.mysql
|
dist_pkgdata_DATA = dhcpdb_create.mysql dhcpdb_create.pgsql
|
||||||
|
|
||||||
install-data-local:
|
install-data-local:
|
||||||
$(mkinstalldirs) $(DESTDIR)$(dhcp_data_dir)
|
$(mkinstalldirs) $(DESTDIR)$(dhcp_data_dir)
|
||||||
|
@@ -121,4 +121,78 @@
|
|||||||
The unit tests are run automatically when "make check" is executed (providing
|
The unit tests are run automatically when "make check" is executed (providing
|
||||||
that BIND 10 has been build with the \--with-dhcp-mysql switch (see the installation
|
that BIND 10 has been build with the \--with-dhcp-mysql switch (see the installation
|
||||||
section in the <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND 10 Guide</a>).
|
section in the <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND 10 Guide</a>).
|
||||||
|
|
||||||
|
@subsection dhcp-pgsql-unittest PostgreSQL unit-tests
|
||||||
|
|
||||||
|
Conceptually, the steps required to run PostgreSQL unit-tests are the same as
|
||||||
|
in MySQL. First, a database called <i>keatest</i> must be created. A database
|
||||||
|
user, also called <i>keatest</i> (that will be allowed to log in using password
|
||||||
|
<i>keatest</i>) must be created and given full privileges in that database. The
|
||||||
|
unit tests create the schema in the database before each test and delete it
|
||||||
|
afterwards.
|
||||||
|
|
||||||
|
PostgreSQL set up differs from system to system. Please consult your OS-specific
|
||||||
|
PostgreSQL documentation. The remainder of that section uses Ubuntu 13.10 x64 as
|
||||||
|
example. On Ubuntu, after installing PostgreSQL (with <tt>sudo apt-get install
|
||||||
|
postgresql</tt>), it is installed as user <i>postgres</i>. To create new databases
|
||||||
|
or add new users, initial commands must be issued as user postgres:
|
||||||
|
|
||||||
|
@verbatim
|
||||||
|
$ sudo -u postgres psql postgres
|
||||||
|
[sudo] password for thomson:
|
||||||
|
psql (9.1.12)
|
||||||
|
Type "help" for help.
|
||||||
|
postgres=# CREATE USER keatest WITH PASSWORD 'keatest';
|
||||||
|
CREATE ROLE
|
||||||
|
postgres=# CREATE DATABASE keatest;
|
||||||
|
CREATE DATABASE
|
||||||
|
postgres=# GRANT ALL PRIVILEGES ON DATABASE keatest TO keatest;
|
||||||
|
GRANT
|
||||||
|
postgres=# \q
|
||||||
|
@endverbatim
|
||||||
|
|
||||||
|
Now we are back to our regular, unprivileged user. Try to log into the newly
|
||||||
|
created database using keatest credentials:
|
||||||
|
@verbatim
|
||||||
|
$ psql -d keatest -U keatest
|
||||||
|
$ psql keatest -U keatest -W
|
||||||
|
Password for user keatest:
|
||||||
|
psql (9.1.12)
|
||||||
|
Type "help" for help.
|
||||||
|
|
||||||
|
keatest=>
|
||||||
|
@endverbatim
|
||||||
|
|
||||||
|
If instead of seeing keatest=> prompt, your login will be refused with error
|
||||||
|
code about failed peer or indent authentication, it means that PostgreSQL is
|
||||||
|
configured to check unix username and reject login attepts if PostgreSQL names
|
||||||
|
are different. To alter that, PostgreSQL configuration must be changed.
|
||||||
|
Alternatively, you may set up your environment, so the tests would be run from
|
||||||
|
unix account keatest. <tt>/etc/postgresql/9.1/main/pg_hba.conf</tt> config file
|
||||||
|
had to betweaked. It may be in a different location in your system. The following
|
||||||
|
lines:
|
||||||
|
|
||||||
|
@verbatim
|
||||||
|
local all all peer
|
||||||
|
host all all 127.0.0.1/32 md5
|
||||||
|
host all all ::1/128 md5
|
||||||
|
@endverbatim
|
||||||
|
|
||||||
|
were replaced with:
|
||||||
|
|
||||||
|
@verbatim
|
||||||
|
local all all password
|
||||||
|
host all all 127.0.0.1/32 password
|
||||||
|
host all all ::1/128 password
|
||||||
|
@endverbatim
|
||||||
|
|
||||||
|
Please consult your PostgreSQL user manual before applying those changes as
|
||||||
|
those changes may expose your other databases that you run on the same system.
|
||||||
|
In general case, it is a poor idea to run anything of value on a system
|
||||||
|
that runs tests. Use caution!
|
||||||
|
|
||||||
|
The unit tests are run automatically when "make check" is executed (providing
|
||||||
|
that BIND 10 has been build with the \--with-dhcp-pgsql switch (see the installation
|
||||||
|
section in the <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND10 Guide</a>).
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@@ -67,7 +67,7 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
|
|||||||
|
|
||||||
// b. Check if the 'type; keyword known and throw an exception if not.
|
// b. Check if the 'type; keyword known and throw an exception if not.
|
||||||
string dbtype = type_ptr->second;
|
string dbtype = type_ptr->second;
|
||||||
if ((dbtype != "memfile") && (dbtype != "mysql")) {
|
if ((dbtype != "memfile") && (dbtype != "mysql") && (dbtype != "postgresql")) {
|
||||||
isc_throw(BadValue, "unknown backend database type: " << dbtype);
|
isc_throw(BadValue, "unknown backend database type: " << dbtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
129
src/lib/dhcpsrv/dhcpdb_create.pgsql
Normal file
129
src/lib/dhcpsrv/dhcpdb_create.pgsql
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
-- Copyright (C) 2012-2013 Internet Systems Consortium.
|
||||||
|
|
||||||
|
-- Permission to use, copy, modify, and distribute this software for any
|
||||||
|
-- purpose with or without fee is hereby granted, provided that the above
|
||||||
|
-- copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
|
||||||
|
-- DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||||
|
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||||
|
-- INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
-- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
|
||||||
|
-- FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
-- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||||
|
-- WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
-- This is the BIND 10 DHCP schema specification for PostgreSQL.
|
||||||
|
|
||||||
|
-- The schema is reasonably portable (with the exception of the engine
|
||||||
|
-- specification, which is MySQL-specific). Minor changes might be needed for
|
||||||
|
-- other databases.
|
||||||
|
|
||||||
|
-- To create the schema, either type the command:
|
||||||
|
|
||||||
|
-- psql -U <user> -W <password> <database> < dhcpdb_create.pgsql
|
||||||
|
|
||||||
|
-- ... at the command prompt, or log in to the PostgreSQL database and at the "postgres=#"
|
||||||
|
-- prompt, issue the command:
|
||||||
|
|
||||||
|
-- @dhcpdb_create.pgsql
|
||||||
|
|
||||||
|
|
||||||
|
-- Holds the IPv4 leases.
|
||||||
|
CREATE TABLE lease4 (
|
||||||
|
address BIGINT PRIMARY KEY NOT NULL, -- IPv4 address
|
||||||
|
hwaddr BYTEA, -- Hardware address
|
||||||
|
client_id BYTEA, -- Client ID
|
||||||
|
valid_lifetime BIGINT, -- Length of the lease (seconds)
|
||||||
|
expire TIMESTAMP, -- Expiration time of the lease
|
||||||
|
subnet_id BIGINT -- Subnet identification
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
-- Create search indexes for lease4 table
|
||||||
|
-- index by hwaddr and subnet_id
|
||||||
|
CREATE INDEX lease4_by_hwaddr_subnet_id ON lease4 (hwaddr, subnet_id);
|
||||||
|
|
||||||
|
-- index by client_id and subnet_id
|
||||||
|
CREATE INDEX lease4_by_client_id_subnet_id ON lease4 (client_id, subnet_id);
|
||||||
|
|
||||||
|
-- Holds the IPv6 leases.
|
||||||
|
-- N.B. The use of a VARCHAR for the address is temporary for development:
|
||||||
|
-- it will eventually be replaced by BINARY(16).
|
||||||
|
CREATE TABLE lease6 (
|
||||||
|
address VARCHAR(39) PRIMARY KEY NOT NULL, -- IPv6 address
|
||||||
|
duid BYTEA, -- DUID
|
||||||
|
valid_lifetime BIGINT, -- Length of the lease (seconds)
|
||||||
|
expire TIMESTAMP, -- Expiration time of the lease
|
||||||
|
subnet_id BIGINT, -- Subnet identification
|
||||||
|
pref_lifetime BIGINT, -- Preferred lifetime
|
||||||
|
lease_type SMALLINT, -- Lease type (see lease6_types
|
||||||
|
-- table for possible values)
|
||||||
|
iaid INT, -- See Section 10 of RFC 3315
|
||||||
|
prefix_len SMALLINT -- For IA_PD only
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Create search indexes for lease4 table
|
||||||
|
-- index by iaid, subnet_id, and duid
|
||||||
|
CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid);
|
||||||
|
|
||||||
|
-- ... and a definition of lease6 types. This table is a convenience for
|
||||||
|
-- users of the database - if they want to view the lease table and use the
|
||||||
|
-- type names, they can join this table with the lease6 table
|
||||||
|
CREATE TABLE lease6_types (
|
||||||
|
lease_type SMALLINT PRIMARY KEY NOT NULL, -- Lease type code.
|
||||||
|
name VARCHAR(5) -- Name of the lease type
|
||||||
|
);
|
||||||
|
START TRANSACTION;
|
||||||
|
INSERT INTO lease6_types VALUES (0, 'IA_NA'); -- Non-temporary v6 addresses
|
||||||
|
INSERT INTO lease6_types VALUES (1, 'IA_TA'); -- Temporary v6 addresses
|
||||||
|
INSERT INTO lease6_types VALUES (2, 'IA_PD'); -- Prefix delegations
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- Finally, the version of the schema. We start at 0.1 during development.
|
||||||
|
-- This table is only modified during schema upgrades. For historical reasons
|
||||||
|
-- (related to the names of the columns in the BIND 10 DNS database file), the
|
||||||
|
-- first column is called "version" and not "major".
|
||||||
|
|
||||||
|
-- NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h,
|
||||||
|
-- which defines the schema for the unit tests. If you are updating
|
||||||
|
-- the version number, the schema has changed: please ensure that
|
||||||
|
-- schema_copy.h has been updated as well.
|
||||||
|
CREATE TABLE schema_version (
|
||||||
|
version INT PRIMARY KEY NOT NULL, -- Major version number
|
||||||
|
minor INT -- Minor version number
|
||||||
|
);
|
||||||
|
START TRANSACTION;
|
||||||
|
INSERT INTO schema_version VALUES (1, 0);
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- Notes:
|
||||||
|
|
||||||
|
-- Indexes
|
||||||
|
-- =======
|
||||||
|
-- It is likely that additional indexes will be needed. However, the
|
||||||
|
-- increase in lookup performance from these will come at the expense
|
||||||
|
-- of a decrease in performance during insert operations due to the need
|
||||||
|
-- to update the indexes. For this reason, the need for additional indexes
|
||||||
|
-- will be determined by experiment during performance tests.
|
||||||
|
|
||||||
|
-- The most likely additional indexes will cover the following columns:
|
||||||
|
|
||||||
|
-- expire
|
||||||
|
-- To speed up the deletion of expired leases from the database.
|
||||||
|
|
||||||
|
-- hwaddr and client_id
|
||||||
|
-- For lease stability: if a client requests a new lease, try to find an
|
||||||
|
-- existing or recently expired lease for it so that it can keep using the
|
||||||
|
-- same IP address.
|
||||||
|
|
||||||
|
-- Field Sizes
|
||||||
|
-- ===========
|
||||||
|
-- If any of the VARxxx field sizes are altered, the lengths in the MySQL
|
||||||
|
-- backend source file (mysql_lease_mgr.cc) must be correspondingly changed.
|
||||||
|
|
||||||
|
-- Portability
|
||||||
|
-- ===========
|
||||||
|
-- Some columns contain binary data so are stored as BYTEA instead of
|
||||||
|
-- VARCHAR. This may be non-portable between databases: in this case, the
|
||||||
|
-- definition should be changed to VARCHAR.
|
@@ -375,6 +375,83 @@ lease from the MySQL database for the specified address.
|
|||||||
A debug message issued when the server is attempting to update IPv6
|
A debug message issued when the server is attempting to update IPv6
|
||||||
lease from the MySQL database for the specified address.
|
lease from the MySQL database for the specified address.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_ADD_ADDR4 adding IPv4 lease with address %1
|
||||||
|
A debug message issued when the server is about to add an IPv4 lease
|
||||||
|
with the specified address to the PostgreSQL backend database.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_ADD_ADDR6 adding IPv6 lease with address %1
|
||||||
|
A debug message issued when the server is about to add an IPv6 lease
|
||||||
|
with the specified address to the PostgreSQL backend database.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_COMMIT committing to MySQL database
|
||||||
|
The code has issued a commit call. All outstanding transactions will be
|
||||||
|
committed to the database. Note that depending on the PostgreSQL settings,
|
||||||
|
the committal may not include a write to disk.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_DB opening PostgreSQL lease database: %1
|
||||||
|
This informational message is logged when a DHCP server (either V4 or
|
||||||
|
V6) is about to open a PostgreSQL lease database. The parameters of the
|
||||||
|
connection including database name and username needed to access it
|
||||||
|
(but not the password if any) are logged.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_DELETE_ADDR deleting lease for address %1
|
||||||
|
A debug message issued when the server is attempting to delete a lease for
|
||||||
|
the specified address from the PostgreSQL database for the specified address.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_GET_ADDR4 obtaining IPv4 lease for address %1
|
||||||
|
A debug message issued when the server is attempting to obtain an IPv4
|
||||||
|
lease from the PostgreSQL database for the specified address.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_GET_ADDR6 obtaining IPv6 lease for address %1 (lease type %2)
|
||||||
|
A debug message issued when the server is attempting to obtain an IPv6
|
||||||
|
lease from the PostgreSQL database for the specified address.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_GET_CLIENTID obtaining IPv4 leases for client ID %1
|
||||||
|
A debug message issued when the server is attempting to obtain a set
|
||||||
|
of IPv4 leases from the PostgreSQL database for a client with the specified
|
||||||
|
client identification.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_GET_HWADDR obtaining IPv4 leases for hardware address %1
|
||||||
|
A debug message issued when the server is attempting to obtain a set
|
||||||
|
of IPv4 leases from the PostgreSQL database for a client with the specified
|
||||||
|
hardware address.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_GET_IAID_DUID obtaining IPv4 leases for IAID %1 and DUID %2, lease type %3
|
||||||
|
A debug message issued when the server is attempting to obtain a set of
|
||||||
|
IPv6 lease from the PostgreSQL database for a client with the specified IAID
|
||||||
|
(Identity Association ID) and DUID (DHCP Unique Identifier).
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_GET_IAID_SUBID_DUID obtaining IPv4 leases for IAID %1, Subnet ID %2 and DUID %3
|
||||||
|
A debug message issued when the server is attempting to obtain an IPv6
|
||||||
|
lease from the PostgreSQL database for a client with the specified IAID
|
||||||
|
(Identity Association ID), Subnet ID and DUID (DHCP Unique Identifier).
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_GET_SUBID_CLIENTID obtaining IPv4 lease for subnet ID %1 and client ID %2
|
||||||
|
A debug message issued when the server is attempting to obtain an IPv4
|
||||||
|
lease from the PostgreSQL database for a client with the specified subnet ID
|
||||||
|
and client ID.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_GET_SUBID_HWADDR obtaining IPv4 lease for subnet ID %1 and hardware address %2
|
||||||
|
A debug message issued when the server is attempting to obtain an IPv4
|
||||||
|
lease from the PostgreSQL database for a client with the specified subnet ID
|
||||||
|
and hardware address.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_GET_VERSION obtaining schema version information
|
||||||
|
A debug message issued when the server is about to obtain schema version
|
||||||
|
information from the PostgreSQL database.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_ROLLBACK rolling back PostgreSQL database
|
||||||
|
The code has issued a rollback call. All outstanding transaction will
|
||||||
|
be rolled back and not committed to the database.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_UPDATE_ADDR4 updating IPv4 lease for address %1
|
||||||
|
A debug message issued when the server is attempting to update IPv4
|
||||||
|
lease from the PostgreSQL database for the specified address.
|
||||||
|
|
||||||
|
% DHCPSRV_PGSQL_UPDATE_ADDR6 updating IPv6 lease for address %1
|
||||||
|
A debug message issued when the server is attempting to update IPv6
|
||||||
|
lease from the PostgreSQL database for the specified address.
|
||||||
|
|
||||||
% DHCPSRV_NOTYPE_DB no 'type' keyword to determine database backend: %1
|
% DHCPSRV_NOTYPE_DB no 'type' keyword to determine database backend: %1
|
||||||
This is an error message, logged when an attempt has been made to access
|
This is an error message, logged when an attempt has been made to access
|
||||||
a database backend, but where no 'type' keyword has been included in
|
a database backend, but where no 'type' keyword has been included in
|
||||||
|
@@ -20,6 +20,9 @@
|
|||||||
#ifdef HAVE_MYSQL
|
#ifdef HAVE_MYSQL
|
||||||
#include <dhcpsrv/mysql_lease_mgr.h>
|
#include <dhcpsrv/mysql_lease_mgr.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_PGSQL
|
||||||
|
#include <dhcpsrv/pgsql_lease_mgr.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
@@ -124,6 +127,13 @@ LeaseMgrFactory::create(const std::string& dbaccess) {
|
|||||||
getLeaseMgrPtr().reset(new MySqlLeaseMgr(parameters));
|
getLeaseMgrPtr().reset(new MySqlLeaseMgr(parameters));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_PGSQL
|
||||||
|
if (parameters[type] == string("postgresql")) {
|
||||||
|
LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_DB).arg(redacted);
|
||||||
|
getLeaseMgrPtr().reset(new PgSqlLeaseMgr(parameters));
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (parameters[type] == string("memfile")) {
|
if (parameters[type] == string("memfile")) {
|
||||||
LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_DB).arg(redacted);
|
LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_DB).arg(redacted);
|
||||||
|
1185
src/lib/dhcpsrv/pgsql_lease_mgr.cc
Normal file
1185
src/lib/dhcpsrv/pgsql_lease_mgr.cc
Normal file
File diff suppressed because it is too large
Load Diff
160
src/lib/dhcpsrv/pgsql_lease_mgr.h
Normal file
160
src/lib/dhcpsrv/pgsql_lease_mgr.h
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef PGSQL_LEASE_MGR_H
|
||||||
|
#define PGSQL_LEASE_MGR_H
|
||||||
|
|
||||||
|
#include <dhcp/hwaddr.h>
|
||||||
|
#include <dhcpsrv/lease_mgr.h>
|
||||||
|
|
||||||
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
#include <boost/utility.hpp>
|
||||||
|
#include <libpq-fe.h>
|
||||||
|
|
||||||
|
namespace isc {
|
||||||
|
namespace dhcp {
|
||||||
|
|
||||||
|
struct PgSqlParam {
|
||||||
|
std::string value;
|
||||||
|
int isbinary;
|
||||||
|
int binarylen;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<PgSqlParam> bindparams;
|
||||||
|
|
||||||
|
struct PgSqlStatementBind {
|
||||||
|
const char * stmt_name;
|
||||||
|
int stmt_nbparams;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PgSqlLease4Exchange;
|
||||||
|
class PgSqlLease6Exchange;
|
||||||
|
|
||||||
|
const uint32_t PG_CURRENT_VERSION = 1;
|
||||||
|
const uint32_t PG_CURRENT_MINOR = 0;
|
||||||
|
|
||||||
|
class PgSqlLeaseMgr : public LeaseMgr {
|
||||||
|
public:
|
||||||
|
PgSqlLeaseMgr(const ParameterMap& parameters);
|
||||||
|
virtual ~PgSqlLeaseMgr();
|
||||||
|
virtual bool addLease(const Lease4Ptr& lease);
|
||||||
|
virtual bool addLease(const Lease6Ptr& lease);
|
||||||
|
|
||||||
|
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
|
||||||
|
virtual Lease4Collection getLease4(const isc::dhcp::HWAddr& hwaddr) const;
|
||||||
|
virtual Lease4Ptr getLease4(const isc::dhcp::HWAddr& hwaddr,
|
||||||
|
SubnetID subnet_id) const;
|
||||||
|
virtual Lease4Collection getLease4(const ClientId& clientid) const;
|
||||||
|
virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr,
|
||||||
|
SubnetID subnet_id) const;
|
||||||
|
virtual Lease4Ptr getLease4(const ClientId& clientid,
|
||||||
|
SubnetID subnet_id) const;
|
||||||
|
|
||||||
|
virtual Lease6Ptr getLease6(Lease::Type type,
|
||||||
|
const isc::asiolink::IOAddress& addr) const;
|
||||||
|
virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
|
||||||
|
uint32_t iaid) const;
|
||||||
|
virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
|
||||||
|
uint32_t iaid, SubnetID subnet_id) const;
|
||||||
|
|
||||||
|
virtual void updateLease4(const Lease4Ptr& lease4);
|
||||||
|
virtual void updateLease6(const Lease6Ptr& lease6);
|
||||||
|
virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
|
||||||
|
virtual std::string getType() const {
|
||||||
|
return (std::string("postgresql"));
|
||||||
|
}
|
||||||
|
virtual std::string getName() const;
|
||||||
|
virtual std::string getDescription() const;
|
||||||
|
virtual std::pair<uint32_t, uint32_t> getVersion() const;
|
||||||
|
virtual void commit();
|
||||||
|
virtual void rollback();
|
||||||
|
enum StatementIndex {
|
||||||
|
DELETE_LEASE4, // Delete from lease4 by address
|
||||||
|
DELETE_LEASE6, // Delete from lease6 by address
|
||||||
|
GET_LEASE4_ADDR, // Get lease4 by address
|
||||||
|
GET_LEASE4_CLIENTID, // Get lease4 by client ID
|
||||||
|
GET_LEASE4_CLIENTID_SUBID, // Get lease4 by client ID & subnet ID
|
||||||
|
GET_LEASE4_HWADDR, // Get lease4 by HW address
|
||||||
|
GET_LEASE4_HWADDR_SUBID, // Get lease4 by HW address & subnet ID
|
||||||
|
GET_LEASE6_ADDR, // Get lease6 by address
|
||||||
|
GET_LEASE6_DUID_IAID, // Get lease6 by DUID and IAID
|
||||||
|
GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
|
||||||
|
GET_VERSION, // Obtain version number
|
||||||
|
INSERT_LEASE4, // Add entry to lease4 table
|
||||||
|
INSERT_LEASE6, // Add entry to lease6 table
|
||||||
|
UPDATE_LEASE4, // Update a Lease4 entry
|
||||||
|
UPDATE_LEASE6, // Update a Lease6 entry
|
||||||
|
NUM_STATEMENTS // Number of statements
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
void prepareStatements();
|
||||||
|
void openDatabase();
|
||||||
|
bool addLeaseCommon(StatementIndex stindex, bindparams & params);
|
||||||
|
template <typename Exchange, typename LeaseCollection>
|
||||||
|
void getLeaseCollection(StatementIndex stindex, bindparams & params,
|
||||||
|
Exchange& exchange, LeaseCollection& result,
|
||||||
|
bool single = false) const;
|
||||||
|
void getLeaseCollection(StatementIndex stindex, bindparams & params,
|
||||||
|
Lease4Collection& result) const {
|
||||||
|
getLeaseCollection(stindex, params, exchange4_, result);
|
||||||
|
}
|
||||||
|
void getLeaseCollection(StatementIndex stindex, bindparams & params,
|
||||||
|
Lease6Collection& result) const {
|
||||||
|
getLeaseCollection(stindex, params, exchange6_, result);
|
||||||
|
}
|
||||||
|
inline void checkError(PGresult * r, StatementIndex index,
|
||||||
|
const char* what) const {
|
||||||
|
int s = PQresultStatus(r);
|
||||||
|
if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
|
||||||
|
PQclear(r);
|
||||||
|
|
||||||
|
isc_throw(DbOperationError, what << " for <" <<
|
||||||
|
statements_[index].stmt_name << ">, " <<
|
||||||
|
PQerrorMessage(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline void convertToQuery(bindparams & params,
|
||||||
|
std::vector<const char *>& params_,
|
||||||
|
std::vector<int>& lengths_,
|
||||||
|
std::vector<int>& formats_) const {
|
||||||
|
params_.reserve(params.size());
|
||||||
|
lengths_.reserve(params.size());
|
||||||
|
formats_.reserve(params.size());
|
||||||
|
|
||||||
|
for(bindparams::const_iterator it = params.begin(); it != params.end();
|
||||||
|
++ it) {
|
||||||
|
params_.push_back((* it).value.c_str());
|
||||||
|
lengths_.push_back((* it).binarylen);
|
||||||
|
formats_.push_back((* it).isbinary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void getLease(StatementIndex stindex, bindparams & params,
|
||||||
|
Lease4Ptr& result) const;
|
||||||
|
void getLease(StatementIndex stindex, bindparams & params,
|
||||||
|
Lease6Ptr& result) const;
|
||||||
|
template <typename LeasePtr>
|
||||||
|
void updateLeaseCommon(StatementIndex stindex, bindparams & params,
|
||||||
|
const LeasePtr& lease);
|
||||||
|
bool deleteLeaseCommon(StatementIndex stindex, bindparams & params);
|
||||||
|
|
||||||
|
boost::scoped_ptr<PgSqlLease4Exchange> exchange4_;
|
||||||
|
boost::scoped_ptr<PgSqlLease6Exchange> exchange6_;
|
||||||
|
std::vector<PgSqlStatementBind> statements_;
|
||||||
|
PGconn * status;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // end of isc::dhcp namespace
|
||||||
|
}; // end of isc namespace
|
||||||
|
|
||||||
|
#endif // PGSQL_LEASE_MGR_H
|
@@ -67,8 +67,12 @@ libdhcpsrv_unittests_SOURCES += dhcp_parsers_unittest.cc
|
|||||||
if HAVE_MYSQL
|
if HAVE_MYSQL
|
||||||
libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
|
libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
|
||||||
endif
|
endif
|
||||||
|
if HAVE_PGSQL
|
||||||
|
libdhcpsrv_unittests_SOURCES += pgsql_lease_mgr_unittest.cc
|
||||||
|
endif
|
||||||
libdhcpsrv_unittests_SOURCES += pool_unittest.cc
|
libdhcpsrv_unittests_SOURCES += pool_unittest.cc
|
||||||
libdhcpsrv_unittests_SOURCES += schema_copy.h
|
libdhcpsrv_unittests_SOURCES += schema_mysql_copy.h
|
||||||
|
libdhcpsrv_unittests_SOURCES += schema_pgsql_copy.h
|
||||||
libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
|
libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
|
||||||
libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
|
libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
|
||||||
libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
|
libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
|
||||||
@@ -78,11 +82,17 @@ libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INC
|
|||||||
if HAVE_MYSQL
|
if HAVE_MYSQL
|
||||||
libdhcpsrv_unittests_CPPFLAGS += $(MYSQL_CPPFLAGS)
|
libdhcpsrv_unittests_CPPFLAGS += $(MYSQL_CPPFLAGS)
|
||||||
endif
|
endif
|
||||||
|
if HAVE_PGSQL
|
||||||
|
libdhcpsrv_unittests_CPPFLAGS += $(PGSQL_CPPFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
libdhcpsrv_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
libdhcpsrv_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
||||||
if HAVE_MYSQL
|
if HAVE_MYSQL
|
||||||
libdhcpsrv_unittests_LDFLAGS += $(MYSQL_LIBS)
|
libdhcpsrv_unittests_LDFLAGS += $(MYSQL_LIBS)
|
||||||
endif
|
endif
|
||||||
|
if HAVE_PGSQL
|
||||||
|
libdhcpsrv_unittests_LDFLAGS += $(PGSQL_LIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
libdhcpsrv_unittests_CXXFLAGS = $(AM_CXXFLAGS)
|
libdhcpsrv_unittests_CXXFLAGS = $(AM_CXXFLAGS)
|
||||||
if USE_CLANGPP
|
if USE_CLANGPP
|
||||||
|
453
src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc
Normal file
453
src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc
Normal file
@@ -0,0 +1,453 @@
|
|||||||
|
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <asiolink/io_address.h>
|
||||||
|
#include <dhcpsrv/lease_mgr_factory.h>
|
||||||
|
#include <dhcpsrv/pgsql_lease_mgr.h>
|
||||||
|
#include <dhcpsrv/tests/test_utils.h>
|
||||||
|
#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
using namespace isc;
|
||||||
|
using namespace isc::asiolink;
|
||||||
|
using namespace isc::dhcp;
|
||||||
|
using namespace isc::dhcp::test;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// This holds statements to create and destroy the schema.
|
||||||
|
#include "schema_pgsql_copy.h"
|
||||||
|
|
||||||
|
// Connection strings.
|
||||||
|
// Database: keatest
|
||||||
|
// Host: localhost
|
||||||
|
// Username: keatest
|
||||||
|
// Password: keatest
|
||||||
|
const char* VALID_TYPE = "type=postgresql";
|
||||||
|
const char* INVALID_TYPE = "type=unknown";
|
||||||
|
const char* VALID_NAME = "name=keatest";
|
||||||
|
const char* INVALID_NAME = "name=invalidname";
|
||||||
|
const char* VALID_HOST = "host=localhost";
|
||||||
|
const char* INVALID_HOST = "host=invalidhost";
|
||||||
|
const char* VALID_USER = "user=keatest";
|
||||||
|
const char* INVALID_USER = "user=invaliduser";
|
||||||
|
const char* VALID_PASSWORD = "password=keatest";
|
||||||
|
const char* INVALID_PASSWORD = "password=invalid";
|
||||||
|
|
||||||
|
// Given a combination of strings above, produce a connection string.
|
||||||
|
string connectionString(const char* type, const char* name, const char* host,
|
||||||
|
const char* user, const char* password) {
|
||||||
|
const string space = " ";
|
||||||
|
string result = "";
|
||||||
|
|
||||||
|
if (type != NULL) {
|
||||||
|
result += string(type);
|
||||||
|
}
|
||||||
|
if (name != NULL) {
|
||||||
|
if (! result.empty()) {
|
||||||
|
result += space;
|
||||||
|
}
|
||||||
|
result += string(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host != NULL) {
|
||||||
|
if (! result.empty()) {
|
||||||
|
result += space;
|
||||||
|
}
|
||||||
|
result += string(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user != NULL) {
|
||||||
|
if (! result.empty()) {
|
||||||
|
result += space;
|
||||||
|
}
|
||||||
|
result += string(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password != NULL) {
|
||||||
|
if (! result.empty()) {
|
||||||
|
result += space;
|
||||||
|
}
|
||||||
|
result += string(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return valid connection string
|
||||||
|
string
|
||||||
|
validConnectionString() {
|
||||||
|
return (connectionString(VALID_TYPE, VALID_NAME, VALID_HOST,
|
||||||
|
VALID_USER, VALID_PASSWORD));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @brief Clear everything from the database
|
||||||
|
//
|
||||||
|
// There is no error checking in this code: if something fails, one of the
|
||||||
|
// tests will (should) fall over.
|
||||||
|
void destroySchema() {
|
||||||
|
// Open database
|
||||||
|
PGconn * conn = 0;
|
||||||
|
conn = PQconnectdb("host = 'localhost' user = 'keatest'"
|
||||||
|
" password = 'keatest' dbname = 'keatest'");
|
||||||
|
|
||||||
|
PGresult * r;
|
||||||
|
// Get rid of everything in it.
|
||||||
|
for (int i = 0; destroy_statement[i] != NULL; ++i) {
|
||||||
|
r = PQexec(conn, destroy_statement[i]);
|
||||||
|
PQclear(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
PQfinish(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @brief Create the Schema
|
||||||
|
//
|
||||||
|
// Creates all the tables in what is assumed to be an empty database.
|
||||||
|
//
|
||||||
|
// There is no error checking in this code: if it fails, one of the tests
|
||||||
|
// will fall over.
|
||||||
|
void createSchema() {
|
||||||
|
// Open database
|
||||||
|
PGconn * conn = 0;
|
||||||
|
conn = PQconnectdb("host = 'localhost' user = 'keatest'"
|
||||||
|
" password = 'keatest' dbname = 'keatest'");
|
||||||
|
|
||||||
|
PGresult * r;
|
||||||
|
// Get rid of everything in it.
|
||||||
|
for (int i = 0; create_statement[i] != NULL; ++i) {
|
||||||
|
r = PQexec(conn, create_statement[i]);
|
||||||
|
PQclear(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Test fixture class for testing PostgreSQL Lease Manager
|
||||||
|
///
|
||||||
|
/// Opens the database prior to each test and closes it afterwards.
|
||||||
|
/// All pending transactions are deleted prior to closure.
|
||||||
|
|
||||||
|
class PgSqlLeaseMgrTest : public GenericLeaseMgrTest {
|
||||||
|
public:
|
||||||
|
/// @brief Constructor
|
||||||
|
///
|
||||||
|
/// Deletes everything from the database and opens it.
|
||||||
|
PgSqlLeaseMgrTest() {
|
||||||
|
|
||||||
|
// Ensure schema is the correct one.
|
||||||
|
destroySchema();
|
||||||
|
createSchema();
|
||||||
|
|
||||||
|
// Connect to the database
|
||||||
|
try {
|
||||||
|
LeaseMgrFactory::create(validConnectionString());
|
||||||
|
} catch (...) {
|
||||||
|
std::cerr << "*** ERROR: unable to open database. The test\n"
|
||||||
|
"*** environment is broken and must be fixed before\n"
|
||||||
|
"*** the PostgreSQL tests will run correctly.\n"
|
||||||
|
"*** The reason for the problem is described in the\n"
|
||||||
|
"*** accompanying exception output.\n";
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
lmptr_ = &(LeaseMgrFactory::instance());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Destructor
|
||||||
|
///
|
||||||
|
/// Rolls back all pending transactions. The deletion of lmptr_ will close
|
||||||
|
/// the database. Then reopen it and delete everything created by the test.
|
||||||
|
virtual ~PgSqlLeaseMgrTest() {
|
||||||
|
lmptr_->rollback();
|
||||||
|
LeaseMgrFactory::destroy();
|
||||||
|
destroySchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Reopen the database
|
||||||
|
///
|
||||||
|
/// Closes the database and re-open it. Anything committed should be
|
||||||
|
/// visible.
|
||||||
|
void reopen() {
|
||||||
|
LeaseMgrFactory::destroy();
|
||||||
|
LeaseMgrFactory::create(validConnectionString());
|
||||||
|
lmptr_ = &(LeaseMgrFactory::instance());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Check that database can be opened
|
||||||
|
///
|
||||||
|
/// This test checks if the PgSqlLeaseMgr can be instantiated. This happens
|
||||||
|
/// only if the database can be opened. Note that this is not part of the
|
||||||
|
/// PgSqlLeaseMgr test fixure set. This test checks that the database can be
|
||||||
|
/// opened: the fixtures assume that and check basic operations.
|
||||||
|
|
||||||
|
TEST(PgSqlOpenTest, OpenDatabase) {
|
||||||
|
|
||||||
|
// Schema needs to be created for the test to work.
|
||||||
|
destroySchema();
|
||||||
|
createSchema();
|
||||||
|
|
||||||
|
// Check that lease manager open the database opens correctly and tidy up.
|
||||||
|
// If it fails, print the error message.
|
||||||
|
try {
|
||||||
|
LeaseMgrFactory::create(validConnectionString());
|
||||||
|
EXPECT_NO_THROW((void) LeaseMgrFactory::instance());
|
||||||
|
LeaseMgrFactory::destroy();
|
||||||
|
} catch (const isc::Exception& ex) {
|
||||||
|
FAIL() << "*** ERROR: unable to open database, reason:\n"
|
||||||
|
<< " " << ex.what() << "\n"
|
||||||
|
<< "*** The test environment is broken and must be fixed\n"
|
||||||
|
<< "*** before the PostgreSQL tests will run correctly.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that attempting to get an instance of the lease manager when
|
||||||
|
// none is set throws an exception.
|
||||||
|
EXPECT_THROW(LeaseMgrFactory::instance(), NoLeaseManager);
|
||||||
|
|
||||||
|
// Check that wrong specification of backend throws an exception.
|
||||||
|
// (This is really a check on LeaseMgrFactory, but is convenient to
|
||||||
|
// perform here.)
|
||||||
|
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
|
||||||
|
NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
|
||||||
|
InvalidParameter);
|
||||||
|
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
|
||||||
|
INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
|
||||||
|
InvalidType);
|
||||||
|
|
||||||
|
// Check that invalid login data causes an exception.
|
||||||
|
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
|
||||||
|
VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
|
||||||
|
DbOpenError);
|
||||||
|
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
|
||||||
|
VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
|
||||||
|
DbOpenError);
|
||||||
|
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
|
||||||
|
VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
|
||||||
|
DbOpenError);
|
||||||
|
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
|
||||||
|
VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
|
||||||
|
DbOpenError);
|
||||||
|
|
||||||
|
// Check for missing parameters
|
||||||
|
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
|
||||||
|
VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
|
||||||
|
NoDatabaseName);
|
||||||
|
|
||||||
|
// Tidy up after the test
|
||||||
|
destroySchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check the getType() method
|
||||||
|
///
|
||||||
|
/// getType() returns a string giving the type of the backend, which should
|
||||||
|
/// always be "postgresql".
|
||||||
|
TEST_F(PgSqlLeaseMgrTest, getType) {
|
||||||
|
EXPECT_EQ(std::string("postgresql"), lmptr_->getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check getName() returns correct database name
|
||||||
|
TEST_F(PgSqlLeaseMgrTest, getName) {
|
||||||
|
EXPECT_EQ(std::string("keatest"), lmptr_->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check that getVersion() returns the expected version
|
||||||
|
TEST_F(PgSqlLeaseMgrTest, checkVersion) {
|
||||||
|
// Check version
|
||||||
|
pair<uint32_t, uint32_t> version;
|
||||||
|
ASSERT_NO_THROW(version = lmptr_->getVersion());
|
||||||
|
EXPECT_EQ(PG_CURRENT_VERSION, version.first);
|
||||||
|
EXPECT_EQ(PG_CURRENT_MINOR, version.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Basic Lease4 Checks
|
||||||
|
///
|
||||||
|
/// Checks that the addLease, getLease4 (by address) and deleteLease (with an
|
||||||
|
/// IPv4 address) works.
|
||||||
|
TEST_F(PgSqlLeaseMgrTest, basicLease4) {
|
||||||
|
// Get the leases to be used for the test.
|
||||||
|
vector<Lease4Ptr> leases = createLeases4();
|
||||||
|
|
||||||
|
// Start the tests. Add three leases to the database, read them back and
|
||||||
|
// check they are what we think they are.
|
||||||
|
EXPECT_TRUE(lmptr_->addLease(leases[1]));
|
||||||
|
EXPECT_TRUE(lmptr_->addLease(leases[2]));
|
||||||
|
EXPECT_TRUE(lmptr_->addLease(leases[3]));
|
||||||
|
lmptr_->commit();
|
||||||
|
|
||||||
|
// Reopen the database to ensure that they actually got stored.
|
||||||
|
reopen();
|
||||||
|
|
||||||
|
Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[1], l_returned);
|
||||||
|
|
||||||
|
l_returned = lmptr_->getLease4(ioaddress4_[2]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[2], l_returned);
|
||||||
|
|
||||||
|
l_returned = lmptr_->getLease4(ioaddress4_[3]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[3], l_returned);
|
||||||
|
|
||||||
|
// Check that we can't add a second lease with the same address
|
||||||
|
EXPECT_FALSE(lmptr_->addLease(leases[1]));
|
||||||
|
|
||||||
|
// Delete a lease, check that it's gone, and that we can't delete it
|
||||||
|
// a second time.
|
||||||
|
EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[1]));
|
||||||
|
l_returned = lmptr_->getLease4(ioaddress4_[1]);
|
||||||
|
EXPECT_FALSE(l_returned);
|
||||||
|
EXPECT_FALSE(lmptr_->deleteLease(ioaddress4_[1]));
|
||||||
|
|
||||||
|
// Check that the second address is still there.
|
||||||
|
l_returned = lmptr_->getLease4(ioaddress4_[2]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[2], l_returned);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Basic Lease4 Checks
|
||||||
|
///
|
||||||
|
/// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id),
|
||||||
|
/// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id.
|
||||||
|
/// (client-id is optional and may not be present)
|
||||||
|
TEST_F(PgSqlLeaseMgrTest, lease4NullClientId) {
|
||||||
|
// Get the leases to be used for the test.
|
||||||
|
vector<Lease4Ptr> leases = createLeases4();
|
||||||
|
|
||||||
|
// Let's clear client-id pointers
|
||||||
|
leases[1]->client_id_ = ClientIdPtr();
|
||||||
|
leases[2]->client_id_ = ClientIdPtr();
|
||||||
|
leases[3]->client_id_ = ClientIdPtr();
|
||||||
|
|
||||||
|
// Start the tests. Add three leases to the database, read them back and
|
||||||
|
// check they are what we think they are.
|
||||||
|
EXPECT_TRUE(lmptr_->addLease(leases[1]));
|
||||||
|
EXPECT_TRUE(lmptr_->addLease(leases[2]));
|
||||||
|
EXPECT_TRUE(lmptr_->addLease(leases[3]));
|
||||||
|
lmptr_->commit();
|
||||||
|
|
||||||
|
// Reopen the database to ensure that they actually got stored.
|
||||||
|
reopen();
|
||||||
|
|
||||||
|
Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[1], l_returned);
|
||||||
|
|
||||||
|
l_returned = lmptr_->getLease4(ioaddress4_[2]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[2], l_returned);
|
||||||
|
|
||||||
|
l_returned = lmptr_->getLease4(ioaddress4_[3]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[3], l_returned);
|
||||||
|
|
||||||
|
// Check that we can't add a second lease with the same address
|
||||||
|
EXPECT_FALSE(lmptr_->addLease(leases[1]));
|
||||||
|
|
||||||
|
// Check that we can get the lease by HWAddr
|
||||||
|
HWAddr tmp(leases[2]->hwaddr_, HTYPE_ETHER);
|
||||||
|
Lease4Collection returned = lmptr_->getLease4(tmp);
|
||||||
|
ASSERT_EQ(1, returned.size());
|
||||||
|
detailCompareLease(leases[2], *returned.begin());
|
||||||
|
|
||||||
|
l_returned = lmptr_->getLease4(tmp, leases[2]->subnet_id_);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[2], l_returned);
|
||||||
|
|
||||||
|
|
||||||
|
// Check that we can update the lease
|
||||||
|
// Modify some fields in lease 1 (not the address) and update it.
|
||||||
|
++leases[1]->subnet_id_;
|
||||||
|
leases[1]->valid_lft_ *= 2;
|
||||||
|
lmptr_->updateLease4(leases[1]);
|
||||||
|
|
||||||
|
// ... and check that the lease is indeed updated
|
||||||
|
l_returned = lmptr_->getLease4(ioaddress4_[1]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[1], l_returned);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Delete a lease, check that it's gone, and that we can't delete it
|
||||||
|
// a second time.
|
||||||
|
EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[1]));
|
||||||
|
l_returned = lmptr_->getLease4(ioaddress4_[1]);
|
||||||
|
EXPECT_FALSE(l_returned);
|
||||||
|
EXPECT_FALSE(lmptr_->deleteLease(ioaddress4_[1]));
|
||||||
|
|
||||||
|
// Check that the second address is still there.
|
||||||
|
l_returned = lmptr_->getLease4(ioaddress4_[2]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[2], l_returned);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Basic Lease6 Checks
|
||||||
|
///
|
||||||
|
/// Checks that the addLease, getLease6 (by address) and deleteLease (with an
|
||||||
|
/// IPv6 address) works.
|
||||||
|
TEST_F(PgSqlLeaseMgrTest, basicLease6) {
|
||||||
|
// Get the leases to be used for the test.
|
||||||
|
vector<Lease6Ptr> leases = createLeases6();
|
||||||
|
|
||||||
|
// Start the tests. Add three leases to the database, read them back and
|
||||||
|
// check they are what we think they are.
|
||||||
|
EXPECT_TRUE(lmptr_->addLease(leases[1]));
|
||||||
|
EXPECT_TRUE(lmptr_->addLease(leases[2]));
|
||||||
|
EXPECT_TRUE(lmptr_->addLease(leases[3]));
|
||||||
|
lmptr_->commit();
|
||||||
|
|
||||||
|
// Reopen the database to ensure that they actually got stored.
|
||||||
|
reopen();
|
||||||
|
|
||||||
|
Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[1], l_returned);
|
||||||
|
|
||||||
|
l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[2], l_returned);
|
||||||
|
|
||||||
|
l_returned = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[3], l_returned);
|
||||||
|
|
||||||
|
// Check that we can't add a second lease with the same address
|
||||||
|
EXPECT_FALSE(lmptr_->addLease(leases[1]));
|
||||||
|
|
||||||
|
// Delete a lease, check that it's gone, and that we can't delete it
|
||||||
|
// a second time.
|
||||||
|
EXPECT_TRUE(lmptr_->deleteLease(ioaddress6_[1]));
|
||||||
|
l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
|
||||||
|
EXPECT_FALSE(l_returned);
|
||||||
|
EXPECT_FALSE(lmptr_->deleteLease(ioaddress6_[1]));
|
||||||
|
|
||||||
|
// Check that the second address is still there.
|
||||||
|
l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
|
||||||
|
ASSERT_TRUE(l_returned);
|
||||||
|
detailCompareLease(leases[2], l_returned);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
89
src/lib/dhcpsrv/tests/schema_pgsql_copy.h
Normal file
89
src/lib/dhcpsrv/tests/schema_pgsql_copy.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef SCHEMA_COPY_H
|
||||||
|
#define SCHEMA_COPY_H
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// What follows is a set of statements that creates a copy of the schema
|
||||||
|
// in the test database. It is used by the PostgreSQL unit test prior to each
|
||||||
|
// test.
|
||||||
|
//
|
||||||
|
// Each SQL statement is a single string. The statements are not terminated
|
||||||
|
// by semicolons, and the strings must end with a comma. The final line
|
||||||
|
// statement must be NULL (not in quotes)
|
||||||
|
|
||||||
|
// NOTE: This file mirrors the schema in src/lib/dhcpsrv/dhcpdb_create.pgsql.
|
||||||
|
// If this file is altered, please ensure that any change is compatible
|
||||||
|
// with the schema in dhcpdb_create.pgsql.
|
||||||
|
|
||||||
|
// Deletion of existing tables.
|
||||||
|
|
||||||
|
const char* destroy_statement[] = {
|
||||||
|
"DROP TABLE lease4",
|
||||||
|
"DROP TABLE lease6",
|
||||||
|
"DROP TABLE lease6_types",
|
||||||
|
"DROP TABLE schema_version",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
// Creation of the new tables.
|
||||||
|
|
||||||
|
const char* create_statement[] = {
|
||||||
|
"START TRANSACTION",
|
||||||
|
"CREATE TABLE lease4 ("
|
||||||
|
"address BIGINT PRIMARY KEY NOT NULL,"
|
||||||
|
"hwaddr BYTEA,"
|
||||||
|
"client_id BYTEA,"
|
||||||
|
"valid_lifetime BIGINT,"
|
||||||
|
"expire TIMESTAMP,"
|
||||||
|
"subnet_id BIGINT"
|
||||||
|
")",
|
||||||
|
|
||||||
|
"CREATE TABLE lease6 ("
|
||||||
|
"address VARCHAR(39) PRIMARY KEY NOT NULL,"
|
||||||
|
"duid BYTEA,"
|
||||||
|
"valid_lifetime BIGINT,"
|
||||||
|
"expire TIMESTAMP,"
|
||||||
|
"subnet_id BIGINT,"
|
||||||
|
"pref_lifetime BIGINT,"
|
||||||
|
"lease_type SMALLINT,"
|
||||||
|
"iaid BIGINT,"
|
||||||
|
"prefix_len SMALLINT"
|
||||||
|
")",
|
||||||
|
|
||||||
|
"CREATE TABLE lease6_types ("
|
||||||
|
"lease_type SMALLINT PRIMARY KEY NOT NULL,"
|
||||||
|
"name VARCHAR(5)"
|
||||||
|
")",
|
||||||
|
|
||||||
|
"INSERT INTO lease6_types VALUES (0, 'IA_NA')",
|
||||||
|
"INSERT INTO lease6_types VALUES (1, 'IA_TA')",
|
||||||
|
"INSERT INTO lease6_types VALUES (2, 'IA_PD')",
|
||||||
|
|
||||||
|
"CREATE TABLE schema_version ("
|
||||||
|
"version INT PRIMARY KEY NOT NULL,"
|
||||||
|
"minor INT"
|
||||||
|
")",
|
||||||
|
|
||||||
|
"INSERT INTO schema_version VALUES (1, 0)",
|
||||||
|
"COMMIT",
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // Anonymous namespace
|
||||||
|
|
||||||
|
#endif // SCHEMA_COPY_H
|
Reference in New Issue
Block a user