~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/schema.cc

fixes to Transforms to make them subselect-compatible

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (C) 2000-2003 MySQL AB
2
 
 
3
 
   This program is free software; you can redistribute it and/or modify
4
 
   it under the terms of the GNU General Public License as published by
5
 
   the Free Software Foundation; version 2 of the License.
6
 
 
7
 
   This program is distributed in the hope that it will be useful,
8
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 
   GNU General Public License for more details.
11
 
 
12
 
   You should have received a copy of the GNU General Public License
13
 
   along with this program; if not, write to the Free Software
14
 
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
15
 
 
16
 
 
17
 
/* create and drop of databases */
18
 
#include <config.h>
19
 
 
20
 
#include <fcntl.h>
21
 
#include <sys/stat.h>
22
 
#include <sys/types.h>
23
 
 
24
 
#include <set>
25
 
#include <string>
26
 
#include <fstream>
27
 
 
28
 
#include <drizzled/error.h>
29
 
#include <drizzled/gettext.h>
30
 
#include <drizzled/internal/m_string.h>
31
 
#include <drizzled/session.h>
32
 
#include <drizzled/schema.h>
33
 
#include <drizzled/sql_base.h>
34
 
#include <drizzled/lock.h>
35
 
#include <drizzled/errmsg_print.h>
36
 
#include <drizzled/transaction_services.h>
37
 
#include <drizzled/message/schema.pb.h>
38
 
#include <drizzled/sql_table.h>
39
 
#include <drizzled/plugin/storage_engine.h>
40
 
#include <drizzled/plugin/authorization.h>
41
 
#include <drizzled/pthread_globals.h>
42
 
#include <drizzled/charset.h>
43
 
#include <drizzled/internal/my_sys.h>
44
 
#include <drizzled/catalog/instance.h>
45
 
#include <boost/thread/mutex.hpp>
46
 
 
47
 
using namespace std;
48
 
 
49
 
namespace drizzled {
50
 
namespace schema {
51
 
 
52
 
/*
53
 
  Create a database
54
 
 
55
 
  SYNOPSIS
56
 
  create_db()
57
 
  session               Thread handler
58
 
  db            Name of database to create
59
 
                Function assumes that this is already validated.
60
 
  create_info   Database create options (like character set)
61
 
 
62
 
  SIDE-EFFECTS
63
 
   1. Report back to client that command succeeded (my_ok)
64
 
   2. Report errors to client
65
 
   3. Log event to binary log
66
 
 
67
 
  RETURN VALUES
68
 
  false ok
69
 
  true  Error
70
 
 
71
 
*/
72
 
 
73
 
bool create(Session &session, const message::Schema &schema_message, const bool is_if_not_exists)
74
 
{
75
 
  bool error= false;
76
 
 
77
 
  /*
78
 
    Do not create database if another thread is holding read lock.
79
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
80
 
    After wait_if_global_read_lock() we have protection against another
81
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
82
 
    another thread could step in and get the global read lock before we
83
 
    reach wait_if_global_read_lock(). If this thread tries the same as we
84
 
    (admin a db), it would then go and wait on session->catalog()->schemaLock()...
85
 
    Furthermore wait_if_global_read_lock() checks if the current thread
86
 
    has the global read lock and refuses the operation with
87
 
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
88
 
  */
89
 
  if (session.wait_if_global_read_lock(false, true))
90
 
  {
91
 
    return false;
92
 
  }
93
 
 
94
 
  assert(schema_message.has_name());
95
 
  assert(schema_message.has_collation());
96
 
 
97
 
  // @todo push this lock down into the engine
98
 
  {
99
 
    boost::mutex::scoped_lock scopedLock(session.catalog().schemaLock());
100
 
 
101
 
    // Check to see if it exists already.  
102
 
    identifier::Schema schema_identifier(schema_message.name());
103
 
    if (plugin::StorageEngine::doesSchemaExist(schema_identifier))
104
 
    {
105
 
      if (not is_if_not_exists)
106
 
      {
107
 
        my_error(ER_DB_CREATE_EXISTS, schema_identifier);
108
 
        error= true;
109
 
      }
110
 
      else
111
 
      {
112
 
        push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
113
 
                            ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS),
114
 
                            schema_message.name().c_str());
115
 
        session.my_ok();
116
 
      }
117
 
    }
118
 
    else if (not plugin::StorageEngine::createSchema(schema_message)) // Try to create it 
119
 
    {
120
 
      my_error(ER_CANT_CREATE_DB, MYF(0), schema_message.name().c_str(), errno);
121
 
      error= true;
122
 
    }
123
 
    else // Created !
124
 
    {
125
 
      TransactionServices::createSchema(session, schema_message);
126
 
      session.my_ok(1);
127
 
    }
128
 
  }
129
 
  session.startWaitingGlobalReadLock();
130
 
 
131
 
  return error;
132
 
}
133
 
 
134
 
 
135
 
/* db-name is already validated when we come here */
136
 
 
137
 
bool alter(Session &session,
138
 
           const message::Schema &schema_message,
139
 
           const message::Schema &original_schema)
140
 
{
141
 
  /*
142
 
    Do not alter database if another thread is holding read lock.
143
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
144
 
    After wait_if_global_read_lock() we have protection against another
145
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
146
 
    another thread could step in and get the global read lock before we
147
 
    reach wait_if_global_read_lock(). If this thread tries the same as we
148
 
    (admin a db), it would then go and wait on session->catalog()->schemaLock()...
149
 
    Furthermore wait_if_global_read_lock() checks if the current thread
150
 
    has the global read lock and refuses the operation with
151
 
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
152
 
  */
153
 
  if ((session.wait_if_global_read_lock(false, true)))
154
 
    return false;
155
 
 
156
 
  bool success;
157
 
  {
158
 
    boost::mutex::scoped_lock scopedLock(session.catalog().schemaLock());
159
 
 
160
 
    identifier::Schema schema_idenifier(schema_message.name());
161
 
    if (not plugin::StorageEngine::doesSchemaExist(schema_idenifier))
162
 
    {
163
 
      my_error(ER_SCHEMA_DOES_NOT_EXIST, schema_idenifier);
164
 
      return false;
165
 
    }
166
 
 
167
 
    /* Change options if current database is being altered. */
168
 
    success= plugin::StorageEngine::alterSchema(schema_message);
169
 
 
170
 
    if (success)
171
 
    {
172
 
      TransactionServices::alterSchema(session, original_schema, schema_message);
173
 
      session.my_ok(1);
174
 
    }
175
 
    else
176
 
    {
177
 
      my_error(ER_ALTER_SCHEMA, schema_idenifier);
178
 
    }
179
 
  }
180
 
  session.startWaitingGlobalReadLock();
181
 
 
182
 
  return success;
183
 
}
184
 
 
185
 
 
186
 
/*
187
 
  Drop all tables in a database and the database itself
188
 
 
189
 
  SYNOPSIS
190
 
    rm_db()
191
 
    session                     Thread handle
192
 
    db                  Database name in the case given by user
193
 
                        It's already validated and set to lower case
194
 
                        (if needed) when we come here
195
 
    if_exists           Don't give error if database doesn't exists
196
 
    silent              Don't generate errors
197
 
 
198
 
  RETURN
199
 
    false ok (Database dropped)
200
 
    ERROR Error
201
 
*/
202
 
 
203
 
bool drop(Session &session, const identifier::Schema &schema_identifier, bool if_exists)
204
 
{
205
 
  /*
206
 
    Do not drop database if another thread is holding read lock.
207
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
208
 
    After wait_if_global_read_lock() we have protection against another
209
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
210
 
    another thread could step in and get the global read lock before we
211
 
    reach wait_if_global_read_lock(). If this thread tries the same as we
212
 
    (admin a db), it would then go and wait on session->catalog()->schemaLock()...
213
 
    Furthermore wait_if_global_read_lock() checks if the current thread
214
 
    has the global read lock and refuses the operation with
215
 
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
216
 
  */
217
 
  if (session.wait_if_global_read_lock(false, true))
218
 
  {
219
 
    return true;
220
 
  }
221
 
 
222
 
  bool error= false;
223
 
  {
224
 
    boost::mutex::scoped_lock scopedLock(session.catalog().schemaLock());
225
 
    if (message::schema::shared_ptr message= plugin::StorageEngine::getSchemaDefinition(schema_identifier))
226
 
    {
227
 
      error= plugin::StorageEngine::dropSchema(session, schema_identifier, *message);
228
 
    }
229
 
    else if (if_exists)
230
 
    {
231
 
      push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_NOTE, ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS),
232
 
                          schema_identifier.getSQLPath().c_str());
233
 
    }
234
 
    else
235
 
    {
236
 
      error= true;
237
 
      my_error(ER_DB_DROP_EXISTS, schema_identifier);
238
 
    }
239
 
  };
240
 
 
241
 
  /*
242
 
    If this database was the client's selected database, we silently
243
 
    change the client's selected database to nothing (to have an empty
244
 
    SELECT DATABASE() in the future). For this we free() session->db and set
245
 
    it to 0.
246
 
  */
247
 
  if (not error and schema_identifier.compare(*session.schema()))
248
 
    session.set_schema("");
249
 
 
250
 
  session.startWaitingGlobalReadLock();
251
 
 
252
 
  return error;
253
 
}
254
 
 
255
 
/**
256
 
  @brief Change the current database and its attributes unconditionally.
257
 
 
258
 
  @param session          thread handle
259
 
  @param new_db_name  database name
260
 
  @param force_switch if force_switch is false, then the operation will fail if
261
 
 
262
 
                        - new_db_name is NULL or empty;
263
 
 
264
 
                        - OR new database name is invalid
265
 
                          (check_db_name() failed);
266
 
 
267
 
                        - OR user has no privilege on the new database;
268
 
 
269
 
                        - OR new database does not exist;
270
 
 
271
 
                      if force_switch is true, then
272
 
 
273
 
                        - if new_db_name is NULL or empty, the current
274
 
                          database will be NULL, @@collation_database will
275
 
                          be set to @@collation_server, the operation will
276
 
                          succeed.
277
 
 
278
 
                        - if new database name is invalid
279
 
                          (check_db_name() failed), the current database
280
 
                          will be NULL, @@collation_database will be set to
281
 
                          @@collation_server, but the operation will fail;
282
 
 
283
 
                        - user privileges will not be checked
284
 
                          (Session::db_access however is updated);
285
 
 
286
 
                          TODO: is this really the intention?
287
 
                                (see sp-security.test).
288
 
 
289
 
                        - if new database does not exist,the current database
290
 
                          will be NULL, @@collation_database will be set to
291
 
                          @@collation_server, a warning will be thrown, the
292
 
                          operation will succeed.
293
 
 
294
 
  @details The function checks that the database name corresponds to a
295
 
  valid and existent database, checks access rights and changes the current
296
 
  database with database attributes (@@collation_database session variable,
297
 
  Session::db_access).
298
 
 
299
 
  This function is not the only way to switch the database that is
300
 
  currently employed. When the replication slave thread switches the
301
 
  database before executing a query, it calls session->set_db directly.
302
 
  However, if the query, in turn, uses a stored routine, the stored routine
303
 
  will use this function, even if it's run on the slave.
304
 
 
305
 
  This function allocates the name of the database on the system heap: this
306
 
  is necessary to be able to uniformly change the database from any module
307
 
  of the server. Up to 5.0 different modules were using different memory to
308
 
  store the name of the database, and this led to memory corruption:
309
 
  a stack pointer set by Stored Procedures was used by replication after
310
 
  the stack address was long gone.
311
 
 
312
 
  @return Operation status
313
 
    @retval false Success
314
 
    @retval true  Error
315
 
*/
316
 
 
317
 
bool change(Session &session, const identifier::Schema &schema_identifier)
318
 
{
319
 
 
320
 
  if (not plugin::Authorization::isAuthorized(*session.user(), schema_identifier))
321
 
  {
322
 
    /* Error message is set in isAuthorized */
323
 
    return true;
324
 
  }
325
 
 
326
 
  if (not check(session, schema_identifier))
327
 
  {
328
 
    my_error(ER_WRONG_DB_NAME, schema_identifier);
329
 
 
330
 
    return true;
331
 
  }
332
 
 
333
 
  if (not plugin::StorageEngine::doesSchemaExist(schema_identifier))
334
 
  {
335
 
    my_error(ER_BAD_DB_ERROR, schema_identifier);
336
 
 
337
 
    /* The operation failed. */
338
 
 
339
 
    return true;
340
 
  }
341
 
 
342
 
  session.set_schema(schema_identifier.getSchemaName());
343
 
 
344
 
  return false;
345
 
}
346
 
 
347
 
/**
348
 
  @brief Internal implementation: switch current database to a valid one.
349
 
 
350
 
  @param session            Thread context.
351
 
  @param new_db_name    Name of the database to switch to. The function will
352
 
                        take ownership of the name (the caller must not free
353
 
                        the allocated memory). If the name is NULL, we're
354
 
                        going to switch to NULL db.
355
 
  @param new_db_charset Character set of the new database.
356
 
*/
357
 
 
358
 
 
359
 
/*
360
 
  Check if database name is valid
361
 
 
362
 
  SYNPOSIS
363
 
    check()
364
 
    org_name            Name of database and length
365
 
 
366
 
  RETURN
367
 
    false error
368
 
    true ok
369
 
*/
370
 
 
371
 
bool check(Session &session, const identifier::Schema &schema_identifier)
372
 
{
373
 
  if (not plugin::Authorization::isAuthorized(*session.user(), schema_identifier))
374
 
    return false;
375
 
  return schema_identifier.isValid();
376
 
}
377
 
 
378
 
} /* namespace schema */
379
 
 
380
 
} /* namespace drizzled */